@saws/cli 2.0.0-beta.2 → 2.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/bin/saws.d.ts +0 -1
- package/dist/bin/saws.js +28 -40
- package/dist/commands/deploy/command.d.ts +2 -7
- package/dist/commands/deploy/command.js +13 -19
- package/dist/commands/deploy/index.d.ts +0 -1
- package/dist/commands/deploy/index.js +1 -4
- package/dist/commands/dev/command.d.ts +1 -7
- package/dist/commands/dev/command.js +46 -48
- package/dist/commands/dev/index.d.ts +0 -1
- package/dist/commands/dev/index.js +1 -6
- package/dist/commands/dev/tui/dev-tui.d.ts +18 -3
- package/dist/commands/dev/tui/dev-tui.js +263 -29
- package/dist/commands/host/command.d.ts +2 -2
- package/dist/commands/host/command.js +130 -7
- package/dist/commands/host/index.d.ts +0 -1
- package/dist/commands/host/index.js +4 -3
- package/dist/commands/init/command.d.ts +1 -7
- package/dist/commands/init/command.js +24 -15
- package/dist/commands/init/index.d.ts +0 -1
- package/dist/commands/init/index.js +1 -5
- package/dist/commands/init/templates/gitignore.template.d.ts +1 -0
- package/dist/commands/init/templates/gitignore.template.js +4 -0
- package/dist/commands/init/templates/saws-ts.template.d.ts +3 -0
- package/dist/commands/init/templates/saws-ts.template.js +7 -0
- package/dist/commands/init/templates/tsconfig-json.template.d.ts +1 -0
- package/dist/commands/init/templates/tsconfig-json.template.js +4 -0
- package/dist/commands/secrets/command.d.ts +2 -2
- package/dist/commands/secrets/command.js +21 -9
- package/dist/commands/secrets/index.d.ts +0 -1
- package/dist/commands/secrets/index.js +7 -5
- package/dist/hosts.d.ts +1 -3
- package/dist/hosts.js +11 -10
- package/package.json +12 -26
- package/dist/bin/saws.d.ts.map +0 -1
- package/dist/commands/deploy/command.d.ts.map +0 -1
- package/dist/commands/deploy/index.d.ts.map +0 -1
- package/dist/commands/dev/command.d.ts.map +0 -1
- package/dist/commands/dev/index.d.ts.map +0 -1
- package/dist/commands/dev/tui/dev-tui.d.ts.map +0 -1
- package/dist/commands/host/command.d.ts.map +0 -1
- package/dist/commands/host/command.test.d.ts +0 -2
- package/dist/commands/host/command.test.d.ts.map +0 -1
- package/dist/commands/host/command.test.js +0 -13
- package/dist/commands/host/index.d.ts.map +0 -1
- package/dist/commands/init/command.d.ts.map +0 -1
- package/dist/commands/init/command.test.d.ts +0 -2
- package/dist/commands/init/command.test.d.ts.map +0 -1
- package/dist/commands/init/command.test.js +0 -40
- package/dist/commands/init/index.d.ts.map +0 -1
- package/dist/commands/secrets/command.d.ts.map +0 -1
- package/dist/commands/secrets/index.d.ts.map +0 -1
- package/dist/hosts.d.ts.map +0 -1
|
@@ -1,14 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { chmod, mkdtemp, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { ParameterNotFoundError, SecretsManager, ServiceDefinition, getSawsConfigModule, hostSshPublicKeyEnvName, } from "@saws/core";
|
|
2
7
|
import { findConfiguredHosts } from "../../hosts.js";
|
|
3
8
|
export async function configureHostCommand(name, options) {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
const config = await getSawsConfigModule(options.config);
|
|
10
|
+
if (!(config.secrets instanceof SecretsManager)) {
|
|
11
|
+
throw new Error('saws.ts must export a SecretsManager instance named "secrets"');
|
|
12
|
+
}
|
|
13
|
+
if (!(config.default instanceof ServiceDefinition)) {
|
|
14
|
+
throw new Error("saws.ts must default-export a ServiceDefinition");
|
|
15
|
+
}
|
|
16
|
+
const host = selectHost(findConfiguredHosts(config.default), name);
|
|
17
|
+
validatePrivateKeyReference(host, config.secrets);
|
|
18
|
+
if (options.dryRun) {
|
|
19
|
+
await host.configure({
|
|
20
|
+
bootstrapUser: options.user,
|
|
21
|
+
deploymentPublicKey: "[redacted deployment public key]",
|
|
22
|
+
dryRun: true,
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const { privateKey, created } = await getOrCreateDeploymentKey(config.secrets, host);
|
|
27
|
+
const publicKey = await derivePublicKey(privateKey);
|
|
28
|
+
if (created) {
|
|
29
|
+
await config.secrets.global.set(host.sshPrivateKey.name, privateKey);
|
|
30
|
+
}
|
|
31
|
+
await updatePublicKeyEnvironment(config.secrets.rootDir, hostSshPublicKeyEnvName(host.name), publicKey);
|
|
32
|
+
await host.configure({
|
|
33
|
+
bootstrapUser: options.user,
|
|
34
|
+
deploymentPublicKey: publicKey,
|
|
35
|
+
});
|
|
8
36
|
}
|
|
9
37
|
export function selectHost(hosts, name) {
|
|
10
38
|
if (hosts.length === 0) {
|
|
11
|
-
throw new Error("No
|
|
39
|
+
throw new Error("No hosts are configured");
|
|
12
40
|
}
|
|
13
41
|
const host = name == null
|
|
14
42
|
? hosts.length === 1
|
|
@@ -17,8 +45,103 @@ export function selectHost(hosts, name) {
|
|
|
17
45
|
: hosts.find((candidate) => candidate.name === name);
|
|
18
46
|
if (host != null)
|
|
19
47
|
return host;
|
|
20
|
-
const available = hosts
|
|
48
|
+
const available = hosts
|
|
49
|
+
.map((candidate) => candidate.name)
|
|
50
|
+
.sort()
|
|
51
|
+
.join(", ");
|
|
21
52
|
throw new Error(name == null
|
|
22
53
|
? `Host name is required. Available hosts: ${available}`
|
|
23
54
|
: `Host "${name}" was not found. Available hosts: ${available}`);
|
|
24
55
|
}
|
|
56
|
+
function validatePrivateKeyReference(host, manager) {
|
|
57
|
+
if (host.sshPrivateKey == null || host.sshPrivateKey.scope !== "global") {
|
|
58
|
+
throw new Error(`Host "${host.name}" must configure sshPrivateKey with secrets.global.reference(...)`);
|
|
59
|
+
}
|
|
60
|
+
if (!host.sshPrivateKey.isManagedBy(manager)) {
|
|
61
|
+
throw new Error(`Host "${host.name}" sshPrivateKey must use the SecretsManager exported as "secrets"`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function getOrCreateDeploymentKey(manager, host) {
|
|
65
|
+
try {
|
|
66
|
+
return {
|
|
67
|
+
privateKey: await manager.global.get(host.sshPrivateKey.name),
|
|
68
|
+
created: false,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (!(error instanceof ParameterNotFoundError))
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
privateKey: await generatePrivateKey(),
|
|
77
|
+
created: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function generatePrivateKey() {
|
|
81
|
+
return withTemporaryDirectory(async (directory) => {
|
|
82
|
+
const keyPath = path.join(directory, "id_ed25519");
|
|
83
|
+
await execFileAsync("ssh-keygen", ["-q", "-t", "ed25519", "-N", "", "-f", keyPath]);
|
|
84
|
+
return readFile(keyPath, "utf8");
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
async function derivePublicKey(privateKey) {
|
|
88
|
+
return withTemporaryDirectory(async (directory) => {
|
|
89
|
+
const keyPath = path.join(directory, "id_ed25519");
|
|
90
|
+
await writeFile(keyPath, privateKey, { mode: 0o600 });
|
|
91
|
+
const { stdout } = await execFileAsync("ssh-keygen", ["-y", "-f", keyPath]);
|
|
92
|
+
const publicKey = stdout.trim();
|
|
93
|
+
if (publicKey.length === 0) {
|
|
94
|
+
throw new Error("Could not derive the deployment public key");
|
|
95
|
+
}
|
|
96
|
+
return publicKey;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async function withTemporaryDirectory(callback) {
|
|
100
|
+
const directory = await mkdtemp(path.join(tmpdir(), "saws-key-"));
|
|
101
|
+
try {
|
|
102
|
+
return await callback(directory);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
await rm(directory, { recursive: true, force: true });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function updatePublicKeyEnvironment(rootDir, variableName, publicKey) {
|
|
109
|
+
const envPath = path.resolve(rootDir, ".env");
|
|
110
|
+
let contents = "";
|
|
111
|
+
try {
|
|
112
|
+
contents = await readFile(envPath, "utf8");
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (error.code !== "ENOENT")
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
const matcher = new RegExp(`^(?:export\\s+)?${escapeRegExp(variableName)}\\s*=`);
|
|
119
|
+
const lines = contents.split(/\r?\n/);
|
|
120
|
+
const replacement = `${variableName}=${publicKey}`;
|
|
121
|
+
let replaced = false;
|
|
122
|
+
const updated = lines.flatMap((line) => {
|
|
123
|
+
if (!matcher.test(line))
|
|
124
|
+
return [line];
|
|
125
|
+
if (replaced)
|
|
126
|
+
return [];
|
|
127
|
+
replaced = true;
|
|
128
|
+
return [replacement];
|
|
129
|
+
});
|
|
130
|
+
while (updated.at(-1) === "")
|
|
131
|
+
updated.pop();
|
|
132
|
+
if (!replaced)
|
|
133
|
+
updated.push(replacement);
|
|
134
|
+
const temporaryPath = `${envPath}.${process.pid}.tmp`;
|
|
135
|
+
try {
|
|
136
|
+
await writeFile(temporaryPath, `${updated.join("\n")}\n`, { mode: 0o600 });
|
|
137
|
+
await rename(temporaryPath, envPath);
|
|
138
|
+
await chmod(envPath, 0o600);
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
await rm(temporaryPath, { force: true });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function escapeRegExp(value) {
|
|
145
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
146
|
+
}
|
|
147
|
+
const execFileAsync = promisify(execFile);
|
|
@@ -3,8 +3,9 @@ import { configureHostCommand } from "./command.js";
|
|
|
3
3
|
export const createCommand = () => new Command("host")
|
|
4
4
|
.description("configure deployment hosts")
|
|
5
5
|
.addCommand(new Command("configure")
|
|
6
|
-
.description("
|
|
6
|
+
.description("bootstrap a deployment user and apply host security policy")
|
|
7
7
|
.argument("[name]", "host name; optional when exactly one host exists")
|
|
8
|
-
.
|
|
9
|
-
.option("--
|
|
8
|
+
.requiredOption("--user <bootstrap-user>", "existing SSH account used for initial configuration")
|
|
9
|
+
.option("--config <path>", "path to saws.ts")
|
|
10
|
+
.option("--dry-run", "describe configuration without making changes")
|
|
10
11
|
.action(configureHostCommand));
|
|
@@ -1,7 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
rootDir?: string;
|
|
3
|
-
config?: string;
|
|
4
|
-
dryRun?: boolean;
|
|
5
|
-
}
|
|
6
|
-
export declare function initCommand(serviceName: string, path: string | undefined, options: InitCommandOptions): Promise<void>;
|
|
7
|
-
//# sourceMappingURL=command.d.ts.map
|
|
1
|
+
export declare const initCommand: (serviceName?: string, configPath?: string) => Promise<void>;
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { findServiceDefinition, getSawsConfig } from "@saws/core";
|
|
4
|
+
import { installDependencies } from "@saws/core/utils/dependency-management";
|
|
5
|
+
import { createFileIfNotExists } from "@saws/core/utils/create-file-if-not-exists";
|
|
6
|
+
import { sawsTsTemplate } from "./templates/saws-ts.template.js";
|
|
7
|
+
import { tsconfigJsonTemplate } from "./templates/tsconfig-json.template.js";
|
|
8
|
+
import { gitignoreTemplate } from "./templates/gitignore.template.js";
|
|
9
|
+
export const initCommand = async (serviceName, configPath) => {
|
|
10
|
+
if (serviceName != null) {
|
|
11
|
+
const serviceDefinition = await getSawsConfig(configPath);
|
|
12
|
+
await findServiceDefinition(serviceDefinition, serviceName).init();
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const name = path.parse(path.resolve(".")).name;
|
|
16
|
+
// not used for now
|
|
17
|
+
await installDependencies([]);
|
|
18
|
+
await installDependencies(["@saws/core", "typescript", "@tsconfig/node26"], {
|
|
19
|
+
development: true,
|
|
20
|
+
});
|
|
21
|
+
await fs.writeFile("./tsconfig.json", tsconfigJsonTemplate(), {});
|
|
22
|
+
await createFileIfNotExists("./saws.ts", sawsTsTemplate({ name }));
|
|
23
|
+
await createFileIfNotExists("./.gitignore", gitignoreTemplate());
|
|
24
|
+
};
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { initCommand } from "./command.js";
|
|
3
3
|
export const createCommand = () => new Command("init")
|
|
4
|
-
.
|
|
5
|
-
.argument("<service>", "name of the service to initialize")
|
|
4
|
+
.argument("[service]", "name of the service to initialize")
|
|
6
5
|
.argument("[config]", "path to service definition")
|
|
7
|
-
.option("--root-dir <string>", "project root used for generated files")
|
|
8
|
-
.option("--config <string>", "path to service definition")
|
|
9
|
-
.option("--dry-run", "allow service initializers to preview their changes")
|
|
10
6
|
.action(initCommand);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const gitignoreTemplate: () => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const tsconfigJsonTemplate: () => string;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export interface SecretsCommandOptions {
|
|
2
|
+
config?: string;
|
|
2
3
|
stage?: string;
|
|
4
|
+
global?: boolean;
|
|
3
5
|
set?: string;
|
|
4
6
|
get?: boolean;
|
|
5
|
-
rootDir?: string;
|
|
6
7
|
}
|
|
7
8
|
export declare function secretsCommand(name: string, options: SecretsCommandOptions): Promise<void>;
|
|
8
|
-
//# sourceMappingURL=command.d.ts.map
|
|
@@ -1,16 +1,28 @@
|
|
|
1
|
-
import { SecretsManager } from "@saws/
|
|
1
|
+
import { getSawsConfigModule, SecretsManager } from "@saws/core";
|
|
2
2
|
export async function secretsCommand(name, options) {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
3
|
+
const value = options.set;
|
|
4
|
+
if (options.get && value != null) {
|
|
5
|
+
throw new Error("secrets accepts only one of --get or --set <value>");
|
|
6
|
+
}
|
|
7
|
+
if (!options.get && value == null) {
|
|
8
|
+
throw new Error("secrets requires either --get or --set <value>");
|
|
9
|
+
}
|
|
10
|
+
if (!options.global) {
|
|
11
|
+
process.env.STAGE = options.stage ?? "local";
|
|
12
|
+
}
|
|
13
|
+
const config = await getSawsConfigModule(options.config);
|
|
14
|
+
const manager = config.secrets;
|
|
15
|
+
if (!(manager instanceof SecretsManager)) {
|
|
16
|
+
throw new Error('saws.ts must export a SecretsManager instance named "secrets"');
|
|
17
|
+
}
|
|
18
|
+
const secrets = options.global ? manager.global : manager;
|
|
7
19
|
if (options.get) {
|
|
8
|
-
console.log(await
|
|
20
|
+
console.log(await secrets.get(name));
|
|
9
21
|
return;
|
|
10
22
|
}
|
|
11
|
-
if (
|
|
12
|
-
throw new Error("secrets requires
|
|
23
|
+
if (value == null) {
|
|
24
|
+
throw new Error("secrets requires --set <value>");
|
|
13
25
|
}
|
|
14
|
-
await
|
|
26
|
+
await secrets.set(name, value);
|
|
15
27
|
console.log("Set secret");
|
|
16
28
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { secretsCommand } from "./command.js";
|
|
3
3
|
export const createCommand = () => new Command("secrets")
|
|
4
|
-
.
|
|
5
|
-
.
|
|
6
|
-
.option("--
|
|
7
|
-
.option("--
|
|
8
|
-
.option("--
|
|
4
|
+
.description("get or set encrypted project secrets")
|
|
5
|
+
.argument("<name>", "secret name")
|
|
6
|
+
.option("--stage <string>", "stage for a stage-scoped secret", "local")
|
|
7
|
+
.option("--global", "use the global secret scope")
|
|
8
|
+
.option("--set <string>", "set the secret value")
|
|
9
|
+
.option("--get", "get the secret value")
|
|
10
|
+
.option("--config <string>", "path to service definition")
|
|
9
11
|
.action(secretsCommand);
|
package/dist/hosts.d.ts
CHANGED
package/dist/hosts.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { Host } from "@saws/
|
|
1
|
+
import { Host } from "@saws/core";
|
|
2
2
|
export function findConfiguredHosts(root) {
|
|
3
3
|
const hosts = new Set();
|
|
4
|
-
const visited = new
|
|
5
|
-
const visit = (
|
|
6
|
-
if (visited.has(
|
|
4
|
+
const visited = new WeakSet();
|
|
5
|
+
const visit = (value) => {
|
|
6
|
+
if (value == null || typeof value !== "object" || visited.has(value))
|
|
7
7
|
return;
|
|
8
|
-
visited.add(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
visited.add(value);
|
|
9
|
+
if (value instanceof Host) {
|
|
10
|
+
hosts.add(value);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
for (const child of Object.values(value))
|
|
14
|
+
visit(child);
|
|
14
15
|
};
|
|
15
16
|
visit(root);
|
|
16
17
|
return [...hosts];
|
package/package.json
CHANGED
|
@@ -1,34 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saws/cli",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
4
|
-
"description": "
|
|
5
|
-
"repository": {
|
|
6
|
-
"type": "git",
|
|
7
|
-
"url": "git+https://github.com/shichongrui/saws.git",
|
|
8
|
-
"directory": "packages/cli"
|
|
9
|
-
},
|
|
10
|
-
"type": "module",
|
|
3
|
+
"version": "2.0.0-beta.4",
|
|
4
|
+
"description": "",
|
|
11
5
|
"bin": {
|
|
12
|
-
"saws": "dist/bin/saws.js"
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"dist"
|
|
16
|
-
],
|
|
17
|
-
"scripts": {
|
|
18
|
-
"prebuild": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\"",
|
|
19
|
-
"build": "tsc -p tsconfig.json",
|
|
20
|
-
"postbuild": "chmod +x dist/bin/saws.js",
|
|
21
|
-
"test": "npm run build && node --test dist/commands/**/*.test.js",
|
|
22
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
6
|
+
"saws": "./dist/bin/saws.js"
|
|
23
7
|
},
|
|
8
|
+
"type": "module",
|
|
24
9
|
"dependencies": {
|
|
25
|
-
"@saws/core": "2.0.0-beta.
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"commander": "^14.0.3"
|
|
10
|
+
"@saws/core": "2.0.0-beta.4",
|
|
11
|
+
"commander": "^15.0.0",
|
|
12
|
+
"find-package-json": "^1.2.0"
|
|
29
13
|
},
|
|
30
14
|
"devDependencies": {
|
|
31
|
-
"@types/
|
|
32
|
-
|
|
33
|
-
|
|
15
|
+
"@types/find-package-json": "^1.2.7"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"./dist"
|
|
19
|
+
]
|
|
34
20
|
}
|
package/dist/bin/saws.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"saws.d.ts","sourceRoot":"","sources":["../../src/bin/saws.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy/command.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,oBAAoB,iBA2B9B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,aAAa,eAOA,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/commands/dev/command.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,iBAAiB,iBAoD3B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/dev/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,aAAa,eAMH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dev-tui.d.ts","sourceRoot":"","sources":["../../../../src/commands/dev/tui/dev-tui.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,cAAc,EAAE,MAAM,YAAY,CAAC;AAkBlE,qBAAa,MAAM;IACjB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAiC;IAEjE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;IACtD,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;gBAErB,QAAQ,EAAE,MAAM,EAAE;IAQ9B,KAAK;IAkBL,IAAI;IAUJ,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,QAAQ,GAAG,QAAmB;IAStE,OAAO,CAAC,cAAc,CAqBpB;IAEF,OAAO,CAAC,MAAM;IAkBd,OAAO,CAAC,MAAM,CA6BZ;CACH"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/commands/host/command.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGvC,MAAM,WAAW,2BAA2B;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,2BAA2B,iBAOrC;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,QAmBtD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"command.test.d.ts","sourceRoot":"","sources":["../../../src/commands/host/command.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { Host } from "@saws/host";
|
|
4
|
-
import { selectHost } from "./command.js";
|
|
5
|
-
const first = new Host({ name: "first", address: "first.example.test" });
|
|
6
|
-
const second = new Host({ name: "second", address: "second.example.test" });
|
|
7
|
-
test("selects the only configured host when its name is omitted", () => {
|
|
8
|
-
assert.equal(selectHost([first]), first);
|
|
9
|
-
});
|
|
10
|
-
test("requires a valid name when several hosts are configured", () => {
|
|
11
|
-
assert.throws(() => selectHost([second, first]), /Host name is required\. Available hosts: first, second/);
|
|
12
|
-
assert.throws(() => selectHost([first, second], "missing"), /Host "missing" was not found\. Available hosts: first, second/);
|
|
13
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/host/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,aAAa,eAUrB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/commands/init/command.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,kBAAkB,iBAiB5B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"command.test.d.ts","sourceRoot":"","sources":["../../../src/commands/init/command.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import test from "node:test";
|
|
6
|
-
import { pathToFileURL } from "node:url";
|
|
7
|
-
import { initCommand } from "./command.js";
|
|
8
|
-
test("initializes only the selected service branch in dependency order", async () => {
|
|
9
|
-
const directory = await mkdtemp(path.join(os.tmpdir(), "saws-init-"));
|
|
10
|
-
const configPath = path.join(directory, "saws.mjs");
|
|
11
|
-
const packageUrl = pathToFileURL(path.resolve("../core/dist/index.js")).href;
|
|
12
|
-
await writeFile(configPath, `
|
|
13
|
-
import { appendFile } from "node:fs/promises";
|
|
14
|
-
import path from "node:path";
|
|
15
|
-
import { ServiceDefinition } from ${JSON.stringify(packageUrl)};
|
|
16
|
-
|
|
17
|
-
class InitializableService extends ServiceDefinition {
|
|
18
|
-
async onInit(context) {
|
|
19
|
-
await appendFile(path.join(context.rootDir, "initialized.txt"), this.name + "\\n");
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const shared = new InitializableService({ name: "shared" });
|
|
24
|
-
const api = new InitializableService({
|
|
25
|
-
name: "api",
|
|
26
|
-
dependencies: [shared],
|
|
27
|
-
});
|
|
28
|
-
const worker = new InitializableService({ name: "worker" });
|
|
29
|
-
|
|
30
|
-
export default new InitializableService({
|
|
31
|
-
name: "app",
|
|
32
|
-
dependencies: [api, worker],
|
|
33
|
-
});
|
|
34
|
-
`);
|
|
35
|
-
await initCommand("api", undefined, {
|
|
36
|
-
config: configPath,
|
|
37
|
-
rootDir: directory,
|
|
38
|
-
});
|
|
39
|
-
assert.equal(await readFile(path.join(directory, "initialized.txt"), "utf8"), "shared\napi\n");
|
|
40
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/init/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,aAAa,eAQF,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/commands/secrets/command.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,qBAAqB,iBAkB/B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/secrets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,aAAa,eAOC,CAAC"}
|
package/dist/hosts.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hosts.d.ts","sourceRoot":"","sources":["../src/hosts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,UAkB1D"}
|