@simplysm/sd-cli 13.0.68 → 13.0.70
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 +10 -957
- package/dist/builders/BaseBuilder.d.ts +23 -23
- package/dist/builders/BaseBuilder.d.ts.map +1 -1
- package/dist/builders/BaseBuilder.js +15 -15
- package/dist/builders/DtsBuilder.d.ts +4 -4
- package/dist/builders/DtsBuilder.js +1 -1
- package/dist/builders/LibraryBuilder.d.ts +3 -3
- package/dist/builders/types.d.ts +10 -10
- package/dist/capacitor/capacitor.d.ts +36 -36
- package/dist/capacitor/capacitor.js +63 -63
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/add-client.d.ts +8 -8
- package/dist/commands/add-client.js +15 -15
- package/dist/commands/add-client.js.map +1 -1
- package/dist/commands/add-server.d.ts +9 -9
- package/dist/commands/add-server.js +13 -13
- package/dist/commands/add-server.js.map +1 -1
- package/dist/commands/build.d.ts +9 -9
- package/dist/commands/check.js +3 -3
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/dev.d.ts +9 -9
- package/dist/commands/device.d.ts +9 -9
- package/dist/commands/device.d.ts.map +1 -1
- package/dist/commands/device.js +17 -17
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/init.d.ts +6 -6
- package/dist/commands/init.js +12 -12
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts +23 -23
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +25 -25
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/publish.d.ts +13 -13
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +61 -61
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/replace-deps.d.ts +3 -3
- package/dist/commands/replace-deps.d.ts.map +1 -1
- package/dist/commands/replace-deps.js +1 -1
- package/dist/commands/replace-deps.js.map +1 -1
- package/dist/commands/typecheck.d.ts +20 -20
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +20 -20
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/commands/watch.d.ts +7 -7
- package/dist/electron/electron.d.ts +27 -27
- package/dist/electron/electron.js +32 -32
- package/dist/electron/electron.js.map +1 -1
- package/dist/infra/ResultCollector.d.ts +9 -9
- package/dist/infra/ResultCollector.js +5 -5
- package/dist/infra/SignalHandler.d.ts +7 -7
- package/dist/infra/SignalHandler.js +4 -4
- package/dist/infra/WorkerManager.d.ts +14 -14
- package/dist/infra/WorkerManager.js +11 -11
- package/dist/orchestrators/BuildOrchestrator.d.ts +19 -19
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js +26 -26
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
- package/dist/orchestrators/DevOrchestrator.d.ts +25 -25
- package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevOrchestrator.js +30 -30
- package/dist/orchestrators/DevOrchestrator.js.map +1 -1
- package/dist/orchestrators/WatchOrchestrator.d.ts +13 -13
- package/dist/orchestrators/WatchOrchestrator.js +17 -17
- package/dist/orchestrators/WatchOrchestrator.js.map +1 -1
- package/dist/sd-cli-entry.d.ts +2 -2
- package/dist/sd-cli-entry.js +38 -38
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-cli.d.ts +2 -2
- package/dist/sd-cli.js +1 -1
- package/dist/sd-cli.js.map +1 -1
- package/dist/sd-config.types.d.ts +84 -84
- package/dist/sd-config.types.d.ts.map +1 -1
- package/dist/utils/build-env.d.ts +1 -1
- package/dist/utils/config-editor.d.ts +5 -5
- package/dist/utils/config-editor.js +2 -2
- package/dist/utils/config-editor.js.map +1 -1
- package/dist/utils/copy-public.d.ts +9 -9
- package/dist/utils/copy-src.d.ts +9 -9
- package/dist/utils/esbuild-config.d.ts +30 -30
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/output-utils.d.ts +6 -6
- package/dist/utils/package-utils.d.ts +6 -6
- package/dist/utils/package-utils.js +1 -1
- package/dist/utils/package-utils.js.map +1 -1
- package/dist/utils/rebuild-manager.js +3 -3
- package/dist/utils/rebuild-manager.js.map +1 -1
- package/dist/utils/replace-deps.d.ts +25 -25
- package/dist/utils/replace-deps.js +3 -3
- package/dist/utils/replace-deps.js.map +1 -1
- package/dist/utils/sd-config.d.ts +3 -3
- package/dist/utils/sd-config.js +3 -3
- package/dist/utils/sd-config.js.map +1 -1
- package/dist/utils/tailwind-config-deps.d.ts +3 -3
- package/dist/utils/template.d.ts +8 -8
- package/dist/utils/tsconfig.d.ts +16 -16
- package/dist/utils/tsconfig.js +2 -2
- package/dist/utils/tsconfig.js.map +1 -1
- package/dist/utils/typecheck-serialization.d.ts +8 -8
- package/dist/utils/vite-config.d.ts +8 -8
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +3 -3
- package/dist/utils/worker-events.d.ts +12 -12
- package/dist/utils/worker-events.d.ts.map +1 -1
- package/dist/utils/worker-utils.d.ts +3 -3
- package/dist/utils/worker-utils.js +2 -2
- package/dist/utils/worker-utils.js.map +1 -1
- package/dist/workers/client.worker.d.ts +14 -14
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +1 -1
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/dts.worker.d.ts +13 -13
- package/dist/workers/dts.worker.d.ts.map +1 -1
- package/dist/workers/dts.worker.js +3 -3
- package/dist/workers/dts.worker.js.map +1 -1
- package/dist/workers/library.worker.d.ts +12 -12
- package/dist/workers/library.worker.js +1 -1
- package/dist/workers/library.worker.js.map +1 -1
- package/dist/workers/lint.worker.d.ts +1 -1
- package/dist/workers/server-runtime.worker.d.ts +6 -6
- package/dist/workers/server-runtime.worker.js +6 -6
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/dist/workers/server.worker.d.ts +20 -20
- package/dist/workers/server.worker.d.ts.map +1 -1
- package/dist/workers/server.worker.js +6 -6
- package/dist/workers/server.worker.js.map +1 -1
- package/package.json +8 -7
- package/src/builders/BaseBuilder.ts +33 -33
- package/src/builders/DtsBuilder.ts +5 -5
- package/src/builders/LibraryBuilder.ts +9 -9
- package/src/builders/types.ts +10 -10
- package/src/capacitor/capacitor.ts +119 -119
- package/src/commands/add-client.ts +31 -31
- package/src/commands/add-server.ts +34 -34
- package/src/commands/build.ts +9 -9
- package/src/commands/check.ts +5 -5
- package/src/commands/dev.ts +9 -9
- package/src/commands/device.ts +30 -30
- package/src/commands/init.ts +25 -25
- package/src/commands/lint.ts +64 -64
- package/src/commands/publish.ts +139 -139
- package/src/commands/replace-deps.ts +4 -4
- package/src/commands/typecheck.ts +74 -74
- package/src/commands/watch.ts +7 -7
- package/src/electron/electron.ts +51 -51
- package/src/infra/ResultCollector.ts +9 -9
- package/src/infra/SignalHandler.ts +7 -7
- package/src/infra/WorkerManager.ts +14 -14
- package/src/orchestrators/BuildOrchestrator.ts +76 -76
- package/src/orchestrators/DevOrchestrator.ts +88 -88
- package/src/orchestrators/WatchOrchestrator.ts +39 -39
- package/src/sd-cli-entry.ts +43 -43
- package/src/sd-cli.ts +15 -15
- package/src/sd-config.types.ts +85 -85
- package/src/utils/build-env.ts +1 -1
- package/src/utils/config-editor.ts +19 -19
- package/src/utils/copy-public.ts +17 -17
- package/src/utils/copy-src.ts +11 -11
- package/src/utils/esbuild-config.ts +33 -33
- package/src/utils/output-utils.ts +11 -11
- package/src/utils/package-utils.ts +12 -12
- package/src/utils/rebuild-manager.ts +3 -3
- package/src/utils/replace-deps.ts +361 -361
- package/src/utils/sd-config.ts +44 -44
- package/src/utils/tailwind-config-deps.ts +98 -98
- package/src/utils/template.ts +56 -56
- package/src/utils/tsconfig.ts +127 -127
- package/src/utils/typecheck-serialization.ts +86 -86
- package/src/utils/vite-config.ts +341 -341
- package/src/utils/worker-events.ts +16 -16
- package/src/utils/worker-utils.ts +45 -45
- package/src/workers/client.worker.ts +34 -34
- package/src/workers/dts.worker.ts +467 -467
- package/src/workers/library.worker.ts +314 -314
- package/src/workers/lint.worker.ts +16 -16
- package/src/workers/server-runtime.worker.ts +157 -157
- package/src/workers/server.worker.ts +572 -572
- package/templates/add-client/__CLIENT__/package.json.hbs +1 -1
- package/templates/add-server/__SERVER__/package.json.hbs +2 -2
- package/templates/init/package.json.hbs +3 -3
- package/tests/config-editor.spec.ts +160 -0
- package/tests/copy-src.spec.ts +50 -0
- package/tests/get-compiler-options-for-package.spec.ts +139 -0
- package/tests/get-package-source-files.spec.ts +181 -0
- package/tests/get-types-from-package-json.spec.ts +107 -0
- package/tests/infra/ResultCollector.spec.ts +39 -0
- package/tests/infra/SignalHandler.spec.ts +38 -0
- package/tests/infra/WorkerManager.spec.ts +97 -0
- package/tests/load-ignore-patterns.spec.ts +188 -0
- package/tests/load-sd-config.spec.ts +137 -0
- package/tests/package-utils.spec.ts +188 -0
- package/tests/parse-root-tsconfig.spec.ts +89 -0
- package/tests/replace-deps.spec.ts +308 -0
- package/tests/run-lint.spec.ts +415 -0
- package/tests/run-typecheck.spec.ts +653 -0
- package/tests/run-watch.spec.ts +75 -0
- package/tests/sd-cli.spec.ts +330 -0
- package/tests/tailwind-config-deps.spec.ts +30 -0
- package/tests/template.spec.ts +70 -0
- package/tests/utils/rebuild-manager.spec.ts +43 -0
- package/tests/write-changed-output-files.spec.ts +97 -0
package/src/commands/publish.ts
CHANGED
|
@@ -20,21 +20,21 @@ const { Client: SshClient, utils } = ssh2;
|
|
|
20
20
|
//#region Types
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Publish
|
|
23
|
+
* Publish command options
|
|
24
24
|
*/
|
|
25
25
|
export interface PublishOptions {
|
|
26
|
-
/**
|
|
26
|
+
/** Filter for packages to deploy (empty array deploys all packages with publish config) */
|
|
27
27
|
targets: string[];
|
|
28
|
-
/**
|
|
28
|
+
/** Deploy without building (dangerous) */
|
|
29
29
|
noBuild: boolean;
|
|
30
|
-
/**
|
|
30
|
+
/** Simulate deployment without actually deploying */
|
|
31
31
|
dryRun: boolean;
|
|
32
|
-
/** sd.config.ts
|
|
32
|
+
/** Additional options to pass to sd.config.ts */
|
|
33
33
|
options: string[];
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* package.json
|
|
37
|
+
* package.json type (required fields only)
|
|
38
38
|
*/
|
|
39
39
|
interface PackageJson {
|
|
40
40
|
name: string;
|
|
@@ -50,8 +50,8 @@ interface PackageJson {
|
|
|
50
50
|
//#region Utilities
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
54
|
-
* @throws
|
|
53
|
+
* Replace environment variables (%VAR% format)
|
|
54
|
+
* @throws throws an error if any variable substitution is empty
|
|
55
55
|
*/
|
|
56
56
|
function replaceEnvVariables(str: string, version: string, projectPath: string): string {
|
|
57
57
|
const result = str.replace(/%([^%]+)%/g, (match, envName: string) => {
|
|
@@ -64,16 +64,16 @@ function replaceEnvVariables(str: string, version: string, projectPath: string):
|
|
|
64
64
|
return (env[envName] as string | undefined) ?? match;
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
//
|
|
67
|
+
// Throw error if any unsubstituted environment variables remain
|
|
68
68
|
if (/%[^%]+%/.test(result)) {
|
|
69
|
-
throw new Error(
|
|
69
|
+
throw new Error(`Environment variable substitution failed: ${str} → ${result}`);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
return result;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
76
|
+
* Wait with countdown
|
|
77
77
|
*/
|
|
78
78
|
async function waitWithCountdown(message: string, seconds: number): Promise<void> {
|
|
79
79
|
for (let i = seconds; i > 0; i--) {
|
|
@@ -93,24 +93,24 @@ async function waitWithCountdown(message: string, seconds: number): Promise<void
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
|
-
*
|
|
96
|
+
* Pre-verify and configure SSH key authentication
|
|
97
97
|
*
|
|
98
|
-
*
|
|
99
|
-
* 1. SSH
|
|
100
|
-
* 2.
|
|
98
|
+
* For SFTP servers without pass:
|
|
99
|
+
* 1. Generate SSH key file if it doesn't exist
|
|
100
|
+
* 2. Test key authentication, and if it fails, register public key using password
|
|
101
101
|
*/
|
|
102
102
|
async function ensureSshAuth(
|
|
103
103
|
publishPackages: Array<{ name: string; config: SdPublishConfig }>,
|
|
104
104
|
logger: ReturnType<typeof consola.withTag>,
|
|
105
105
|
): Promise<void> {
|
|
106
|
-
//
|
|
106
|
+
// Collect SFTP servers without pass (deduplicate user@host)
|
|
107
107
|
const sshTargets = new Map<string, { host: string; port?: number; user: string }>();
|
|
108
108
|
for (const pkg of publishPackages) {
|
|
109
109
|
if (pkg.config === "npm") continue;
|
|
110
110
|
if (pkg.config.type !== "sftp") continue;
|
|
111
111
|
if (pkg.config.pass != null) continue;
|
|
112
112
|
if (pkg.config.user == null) {
|
|
113
|
-
throw new Error(`[${pkg.name}] SFTP
|
|
113
|
+
throw new Error(`[${pkg.name}] SFTP config missing user.`);
|
|
114
114
|
}
|
|
115
115
|
const key = `${pkg.config.user}@${pkg.config.host}`;
|
|
116
116
|
sshTargets.set(key, {
|
|
@@ -122,13 +122,13 @@ async function ensureSshAuth(
|
|
|
122
122
|
|
|
123
123
|
if (sshTargets.size === 0) return;
|
|
124
124
|
|
|
125
|
-
// SSH
|
|
125
|
+
// Check/create SSH key file
|
|
126
126
|
const sshDir = path.join(os.homedir(), ".ssh");
|
|
127
127
|
const keyPath = path.join(sshDir, "id_ed25519");
|
|
128
128
|
const pubKeyPath = path.join(sshDir, "id_ed25519.pub");
|
|
129
129
|
|
|
130
130
|
if (!fs.existsSync(keyPath)) {
|
|
131
|
-
logger.info("SSH
|
|
131
|
+
logger.info("SSH key not found. Creating one...");
|
|
132
132
|
|
|
133
133
|
if (!fs.existsSync(sshDir)) {
|
|
134
134
|
fs.mkdirSync(sshDir, { mode: 0o700 });
|
|
@@ -138,41 +138,41 @@ async function ensureSshAuth(
|
|
|
138
138
|
fs.writeFileSync(keyPath, keyPair.private, { mode: 0o600 });
|
|
139
139
|
fs.writeFileSync(pubKeyPath, keyPair.public + "\n", { mode: 0o644 });
|
|
140
140
|
|
|
141
|
-
logger.info(`SSH
|
|
141
|
+
logger.info(`SSH key created: ${keyPath}`);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
const privateKeyData = fs.readFileSync(keyPath);
|
|
145
145
|
const publicKey = fs.readFileSync(pubKeyPath, "utf-8").trim();
|
|
146
146
|
|
|
147
|
-
// privateKey
|
|
147
|
+
// Check if privateKey is encrypted
|
|
148
148
|
const parsed = utils.parseKey(privateKeyData);
|
|
149
149
|
const isKeyEncrypted = parsed instanceof Error;
|
|
150
150
|
const sshAgent = process.env["SSH_AUTH_SOCK"];
|
|
151
151
|
|
|
152
|
-
//
|
|
152
|
+
// Verify key authentication for each server
|
|
153
153
|
for (const [label, target] of sshTargets) {
|
|
154
154
|
const canAuth = await testSshKeyAuth(target, {
|
|
155
155
|
privateKey: isKeyEncrypted ? undefined : privateKeyData,
|
|
156
156
|
agent: sshAgent,
|
|
157
157
|
});
|
|
158
158
|
if (canAuth) {
|
|
159
|
-
logger.debug(`SSH
|
|
159
|
+
logger.debug(`SSH key authentication verified: ${label}`);
|
|
160
160
|
continue;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
//
|
|
164
|
-
logger.info(`${label}: SSH
|
|
163
|
+
// Key authentication failed → register public key using password
|
|
164
|
+
logger.info(`${label}: SSH key not registered on server.`);
|
|
165
165
|
const pass = await passwordPrompt({
|
|
166
|
-
message: `${label}
|
|
166
|
+
message: `${label} password (to register public key):`,
|
|
167
167
|
});
|
|
168
168
|
|
|
169
169
|
await registerSshPublicKey(target, pass, publicKey);
|
|
170
|
-
logger.info(`SSH
|
|
170
|
+
logger.info(`SSH public key registered: ${label}`);
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
/**
|
|
175
|
-
* SSH
|
|
175
|
+
* Test SSH key authentication (connect and immediately disconnect)
|
|
176
176
|
*/
|
|
177
177
|
function testSshKeyAuth(
|
|
178
178
|
target: { host: string; port?: number; user: string },
|
|
@@ -203,7 +203,7 @@ function testSshKeyAuth(
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
/**
|
|
206
|
-
*
|
|
206
|
+
* Connect to server with password and register SSH public key
|
|
207
207
|
*/
|
|
208
208
|
function registerSshPublicKey(
|
|
209
209
|
target: { host: string; port?: number; user: string },
|
|
@@ -213,7 +213,7 @@ function registerSshPublicKey(
|
|
|
213
213
|
return new Promise((resolve, reject) => {
|
|
214
214
|
const conn = new SshClient();
|
|
215
215
|
conn.on("ready", () => {
|
|
216
|
-
//
|
|
216
|
+
// Add public key to authorized_keys
|
|
217
217
|
const escapedKey = publicKey.replace(/'/g, "'\\''");
|
|
218
218
|
const cmd = [
|
|
219
219
|
"mkdir -p ~/.ssh",
|
|
@@ -225,19 +225,19 @@ function registerSshPublicKey(
|
|
|
225
225
|
conn.exec(cmd, (err, stream) => {
|
|
226
226
|
if (err) {
|
|
227
227
|
conn.end();
|
|
228
|
-
reject(new Error(`
|
|
228
|
+
reject(new Error(`Failed to execute SSH command: ${err.message}`));
|
|
229
229
|
return;
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
let stderr = "";
|
|
233
|
-
stream.on("data", () => {}); // stdout
|
|
233
|
+
stream.on("data", () => {}); // Consume stdout (unconsumed stream won't close)
|
|
234
234
|
stream.stderr.on("data", (data: Uint8Array) => {
|
|
235
235
|
stderr += data.toString();
|
|
236
236
|
});
|
|
237
237
|
stream.on("exit", (code: number | null) => {
|
|
238
238
|
conn.end();
|
|
239
239
|
if (code !== 0) {
|
|
240
|
-
reject(new Error(`SSH
|
|
240
|
+
reject(new Error(`Failed to register SSH public key (exit code: ${code}): ${stderr}`));
|
|
241
241
|
} else {
|
|
242
242
|
resolve();
|
|
243
243
|
}
|
|
@@ -245,7 +245,7 @@ function registerSshPublicKey(
|
|
|
245
245
|
});
|
|
246
246
|
});
|
|
247
247
|
conn.on("error", (err) => {
|
|
248
|
-
reject(new Error(`SSH
|
|
248
|
+
reject(new Error(`SSH connection failed (${target.host}): ${err.message}`));
|
|
249
249
|
});
|
|
250
250
|
conn.connect({
|
|
251
251
|
host: target.host,
|
|
@@ -262,8 +262,8 @@ function registerSshPublicKey(
|
|
|
262
262
|
//#region Version Upgrade
|
|
263
263
|
|
|
264
264
|
/**
|
|
265
|
-
*
|
|
266
|
-
* @param dryRun true
|
|
265
|
+
* Upgrade project and package versions
|
|
266
|
+
* @param dryRun if true, only calculate new version without modifying files
|
|
267
267
|
*/
|
|
268
268
|
async function upgradeVersion(
|
|
269
269
|
cwd: string,
|
|
@@ -277,14 +277,14 @@ async function upgradeVersion(
|
|
|
277
277
|
const currentVersion = projPkg.version;
|
|
278
278
|
const prereleaseInfo = semver.prerelease(currentVersion);
|
|
279
279
|
|
|
280
|
-
//
|
|
280
|
+
// Determine increment strategy based on prerelease status
|
|
281
281
|
const newVersion =
|
|
282
282
|
prereleaseInfo !== null
|
|
283
283
|
? semver.inc(currentVersion, "prerelease")!
|
|
284
284
|
: semver.inc(currentVersion, "patch")!;
|
|
285
285
|
|
|
286
286
|
if (dryRun) {
|
|
287
|
-
// dry-run:
|
|
287
|
+
// dry-run: return new version without modifying files
|
|
288
288
|
return { version: newVersion, changedFiles: [] };
|
|
289
289
|
}
|
|
290
290
|
|
|
@@ -292,7 +292,7 @@ async function upgradeVersion(
|
|
|
292
292
|
await fsWrite(projPkgPath, jsonStringify(projPkg, { space: 2 }) + "\n");
|
|
293
293
|
changedFiles.push(projPkgPath);
|
|
294
294
|
|
|
295
|
-
//
|
|
295
|
+
// Set version in each package's package.json
|
|
296
296
|
for (const pkgPath of allPkgPaths) {
|
|
297
297
|
const pkgJsonPath = path.resolve(pkgPath, "package.json");
|
|
298
298
|
const pkgJson = await fsReadJson<PackageJson>(pkgJsonPath);
|
|
@@ -301,7 +301,7 @@ async function upgradeVersion(
|
|
|
301
301
|
changedFiles.push(pkgJsonPath);
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
-
//
|
|
304
|
+
// Synchronize @simplysm package version in template files
|
|
305
305
|
const templateFiles = await fsGlob(path.resolve(cwd, "packages/sd-cli/templates/**/*.hbs"));
|
|
306
306
|
const versionRegex = /("@simplysm\/[^"]+"\s*:\s*)"~[^"]+"/g;
|
|
307
307
|
|
|
@@ -323,8 +323,8 @@ async function upgradeVersion(
|
|
|
323
323
|
//#region Package Publishing
|
|
324
324
|
|
|
325
325
|
/**
|
|
326
|
-
*
|
|
327
|
-
* @param dryRun true
|
|
326
|
+
* Publish individual package
|
|
327
|
+
* @param dryRun if true, simulate deployment without actually publishing
|
|
328
328
|
*/
|
|
329
329
|
async function publishPackage(
|
|
330
330
|
pkgPath: string,
|
|
@@ -354,27 +354,27 @@ async function publishPackage(
|
|
|
354
354
|
|
|
355
355
|
await execa("pnpm", args, { cwd: pkgPath });
|
|
356
356
|
} else if (publishConfig.type === "local-directory") {
|
|
357
|
-
//
|
|
357
|
+
// Copy to local directory
|
|
358
358
|
const targetPath = replaceEnvVariables(publishConfig.path, version, projectPath);
|
|
359
359
|
const distPath = path.resolve(pkgPath, "dist");
|
|
360
360
|
|
|
361
361
|
if (dryRun) {
|
|
362
|
-
logger.info(`[DRY-RUN] [${pkgName}]
|
|
362
|
+
logger.info(`[DRY-RUN] [${pkgName}] copy to local: ${distPath} → ${targetPath}`);
|
|
363
363
|
} else {
|
|
364
|
-
logger.debug(`[${pkgName}]
|
|
364
|
+
logger.debug(`[${pkgName}] copy to local: ${distPath} → ${targetPath}`);
|
|
365
365
|
await fsCopy(distPath, targetPath);
|
|
366
366
|
}
|
|
367
367
|
} else {
|
|
368
|
-
//
|
|
368
|
+
// Upload to storage
|
|
369
369
|
const distPath = path.resolve(pkgPath, "dist");
|
|
370
370
|
const remotePath = publishConfig.path ?? "/";
|
|
371
371
|
|
|
372
372
|
if (dryRun) {
|
|
373
373
|
logger.info(
|
|
374
|
-
`[DRY-RUN] [${pkgName}] ${publishConfig.type}
|
|
374
|
+
`[DRY-RUN] [${pkgName}] ${publishConfig.type} upload: ${distPath} → ${remotePath}`,
|
|
375
375
|
);
|
|
376
376
|
} else {
|
|
377
|
-
logger.debug(`[${pkgName}] ${publishConfig.type}
|
|
377
|
+
logger.debug(`[${pkgName}] ${publishConfig.type} upload: ${distPath} → ${remotePath}`);
|
|
378
378
|
await StorageFactory.connect(
|
|
379
379
|
publishConfig.type,
|
|
380
380
|
{
|
|
@@ -396,15 +396,15 @@ async function publishPackage(
|
|
|
396
396
|
//#region Dependency Levels
|
|
397
397
|
|
|
398
398
|
/**
|
|
399
|
-
*
|
|
400
|
-
*
|
|
399
|
+
* Calculate dependency levels for packages to publish.
|
|
400
|
+
* Packages with no dependencies → Level 0, depends only on Level 0 → Level 1, ...
|
|
401
401
|
*/
|
|
402
402
|
async function computePublishLevels(
|
|
403
403
|
publishPkgs: Array<{ name: string; path: string; config: SdPublishConfig }>,
|
|
404
404
|
): Promise<Array<Array<{ name: string; path: string; config: SdPublishConfig }>>> {
|
|
405
405
|
const pkgNames = new Set(publishPkgs.map((p) => p.name));
|
|
406
406
|
|
|
407
|
-
//
|
|
407
|
+
// Collect workspace dependencies for each package
|
|
408
408
|
const depsMap = new Map<string, Set<string>>();
|
|
409
409
|
for (const pkg of publishPkgs) {
|
|
410
410
|
const pkgJson = await fsReadJson<PackageJson>(path.resolve(pkg.path, "package.json"));
|
|
@@ -424,7 +424,7 @@ async function computePublishLevels(
|
|
|
424
424
|
depsMap.set(pkg.name, workspaceDeps);
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
-
//
|
|
427
|
+
// Topological sort to classify into levels
|
|
428
428
|
const levels: Array<Array<{ name: string; path: string; config: SdPublishConfig }>> = [];
|
|
429
429
|
const assigned = new Set<string>();
|
|
430
430
|
const remaining = new Map(publishPkgs.map((p) => [p.name, p]));
|
|
@@ -439,7 +439,7 @@ async function computePublishLevels(
|
|
|
439
439
|
}
|
|
440
440
|
|
|
441
441
|
if (level.length === 0) {
|
|
442
|
-
//
|
|
442
|
+
// Circular dependency — place all remaining packages in final level
|
|
443
443
|
levels.push([...remaining.values()]);
|
|
444
444
|
break;
|
|
445
445
|
}
|
|
@@ -459,15 +459,15 @@ async function computePublishLevels(
|
|
|
459
459
|
//#region Main
|
|
460
460
|
|
|
461
461
|
/**
|
|
462
|
-
* publish
|
|
462
|
+
* Execute publish command.
|
|
463
463
|
*
|
|
464
|
-
*
|
|
465
|
-
* 1.
|
|
466
|
-
* 2.
|
|
467
|
-
* 3.
|
|
468
|
-
* 4. Git
|
|
469
|
-
* 5. pnpm
|
|
470
|
-
* 6. postPublish (
|
|
464
|
+
* **Deployment order (safety first):**
|
|
465
|
+
* 1. Pre-validation (npm auth, Git status)
|
|
466
|
+
* 2. Version upgrade (package.json + templates)
|
|
467
|
+
* 3. Build
|
|
468
|
+
* 4. Git commit/tag/push (explicitly stage only changed files)
|
|
469
|
+
* 5. pnpm deployment
|
|
470
|
+
* 6. postPublish (continue even if it fails)
|
|
471
471
|
*/
|
|
472
472
|
export async function runPublish(options: PublishOptions): Promise<void> {
|
|
473
473
|
const { targets, noBuild, dryRun } = options;
|
|
@@ -475,27 +475,27 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
475
475
|
const logger = consola.withTag("sd:cli:publish");
|
|
476
476
|
|
|
477
477
|
if (dryRun) {
|
|
478
|
-
logger.info("[DRY-RUN]
|
|
478
|
+
logger.info("[DRY-RUN] Simulation mode - no actual deployment");
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
-
logger.debug("
|
|
481
|
+
logger.debug("publish start", { targets, noBuild, dryRun });
|
|
482
482
|
|
|
483
|
-
// sd.config.ts
|
|
483
|
+
// Load sd.config.ts
|
|
484
484
|
let sdConfig: SdConfig;
|
|
485
485
|
try {
|
|
486
486
|
sdConfig = await loadSdConfig({ cwd, dev: false, opt: options.options });
|
|
487
|
-
logger.debug("sd.config.ts
|
|
487
|
+
logger.debug("sd.config.ts loaded");
|
|
488
488
|
} catch (err) {
|
|
489
|
-
logger.error(`sd.config.ts
|
|
489
|
+
logger.error(`Failed to load sd.config.ts: ${err instanceof Error ? err.message : err}`);
|
|
490
490
|
process.exitCode = 1;
|
|
491
491
|
return;
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
-
// package.json
|
|
494
|
+
// Load package.json
|
|
495
495
|
const projPkgPath = path.resolve(cwd, "package.json");
|
|
496
496
|
const projPkg = await fsReadJson<PackageJson>(projPkgPath);
|
|
497
497
|
|
|
498
|
-
// pnpm-workspace.yaml
|
|
498
|
+
// Collect workspace package paths from pnpm-workspace.yaml
|
|
499
499
|
const workspaceYamlPath = path.resolve(cwd, "pnpm-workspace.yaml");
|
|
500
500
|
const workspaceGlobs: string[] = [];
|
|
501
501
|
if (await fsExists(workspaceYamlPath)) {
|
|
@@ -509,7 +509,7 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
509
509
|
.flat()
|
|
510
510
|
.filter((item) => !path.basename(item).includes("."));
|
|
511
511
|
|
|
512
|
-
//
|
|
512
|
+
// Filter packages with publish configuration
|
|
513
513
|
const publishPackages: Array<{
|
|
514
514
|
name: string;
|
|
515
515
|
path: string;
|
|
@@ -523,12 +523,12 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
523
523
|
const pkgConfig = config;
|
|
524
524
|
if (pkgConfig.publish == null) continue;
|
|
525
525
|
|
|
526
|
-
// targets
|
|
526
|
+
// If targets is specified, include only those packages
|
|
527
527
|
if (targets.length > 0 && !targets.includes(name)) continue;
|
|
528
528
|
|
|
529
529
|
const pkgPath = allPkgPaths.find((p) => path.basename(p) === name);
|
|
530
530
|
if (pkgPath == null) {
|
|
531
|
-
logger.warn(
|
|
531
|
+
logger.warn(`Package not found: ${name}`);
|
|
532
532
|
continue;
|
|
533
533
|
}
|
|
534
534
|
|
|
@@ -540,59 +540,59 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
540
540
|
}
|
|
541
541
|
|
|
542
542
|
if (publishPackages.length === 0) {
|
|
543
|
-
process.stdout.write("✔
|
|
543
|
+
process.stdout.write("✔ No packages to deploy.\n");
|
|
544
544
|
return;
|
|
545
545
|
}
|
|
546
546
|
|
|
547
547
|
logger.debug(
|
|
548
|
-
"
|
|
548
|
+
"Target packages to deploy",
|
|
549
549
|
publishPackages.map((p) => p.name),
|
|
550
550
|
);
|
|
551
551
|
|
|
552
|
-
// Git
|
|
552
|
+
// Check if Git is available
|
|
553
553
|
const hasGit = await fsExists(path.resolve(cwd, ".git"));
|
|
554
554
|
|
|
555
|
-
//#region Phase 1:
|
|
555
|
+
//#region Phase 1: Pre-validation
|
|
556
556
|
|
|
557
|
-
// npm
|
|
557
|
+
// Verify npm authentication (if npm publish config exists)
|
|
558
558
|
if (publishPackages.some((p) => p.config === "npm")) {
|
|
559
|
-
logger.debug("npm
|
|
559
|
+
logger.debug("Verifying npm authentication...");
|
|
560
560
|
try {
|
|
561
561
|
const { stdout: whoami } = await execa("npm", ["whoami"]);
|
|
562
562
|
if (whoami.trim() === "") {
|
|
563
|
-
throw new Error("npm
|
|
563
|
+
throw new Error("npm login information not found.");
|
|
564
564
|
}
|
|
565
|
-
logger.debug(`npm
|
|
565
|
+
logger.debug(`npm login verified: ${whoami.trim()}`);
|
|
566
566
|
} catch (err) {
|
|
567
|
-
logger.error(`npm whoami
|
|
567
|
+
logger.error(`npm whoami failed:`, err);
|
|
568
568
|
/*logger.error(
|
|
569
|
-
"npm
|
|
570
|
-
"https://www.npmjs.com/settings/~/tokens
|
|
571
|
-
" npm config set //registry.npmjs.org/:_authToken
|
|
569
|
+
"npm token is invalid or expired.\n" +
|
|
570
|
+
"Create a Granular Access Token at https://www.npmjs.com/settings/~/tokens, then:\n" +
|
|
571
|
+
" npm config set //registry.npmjs.org/:_authToken <token>",
|
|
572
572
|
);*/
|
|
573
573
|
process.exitCode = 1;
|
|
574
574
|
return;
|
|
575
575
|
}
|
|
576
576
|
}
|
|
577
577
|
|
|
578
|
-
// SSH
|
|
578
|
+
// Verify SSH key authentication (if SFTP publish config without pass exists)
|
|
579
579
|
try {
|
|
580
580
|
await ensureSshAuth(publishPackages, logger);
|
|
581
581
|
} catch (err) {
|
|
582
|
-
logger.error(`
|
|
582
|
+
logger.error(`Failed to setup SSH authentication: ${err instanceof Error ? err.message : err}`);
|
|
583
583
|
process.exitCode = 1;
|
|
584
584
|
return;
|
|
585
585
|
}
|
|
586
586
|
|
|
587
|
-
//
|
|
587
|
+
// Check for uncommitted changes and attempt auto-commit (unless noBuild is set)
|
|
588
588
|
if (!noBuild && hasGit) {
|
|
589
|
-
logger.debug("
|
|
589
|
+
logger.debug("Checking git commit status...");
|
|
590
590
|
try {
|
|
591
591
|
const { stdout: diff } = await execa("git", ["diff", "--name-only"]);
|
|
592
592
|
const { stdout: stagedDiff } = await execa("git", ["diff", "--cached", "--name-only"]);
|
|
593
593
|
|
|
594
594
|
if (diff.trim() !== "" || stagedDiff.trim() !== "") {
|
|
595
|
-
logger.info("
|
|
595
|
+
logger.info("Uncommitted changes detected. Attempting auto-commit with claude...");
|
|
596
596
|
try {
|
|
597
597
|
await execa("claude", [
|
|
598
598
|
"-p",
|
|
@@ -603,20 +603,20 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
603
603
|
]);
|
|
604
604
|
} catch (e) {
|
|
605
605
|
throw new Error(
|
|
606
|
-
"
|
|
606
|
+
"Auto-commit failed. Please commit manually and try again.\n" +
|
|
607
607
|
(e instanceof Error ? e.message : String(e)),
|
|
608
608
|
);
|
|
609
609
|
}
|
|
610
610
|
|
|
611
|
-
//
|
|
611
|
+
// Re-verify after commit
|
|
612
612
|
const { stdout: recheckDiff } = await execa("git", ["diff", "--name-only"]);
|
|
613
613
|
const { stdout: recheckStaged } = await execa("git", ["diff", "--cached", "--name-only"]);
|
|
614
614
|
if (recheckDiff.trim() !== "" || recheckStaged.trim() !== "") {
|
|
615
615
|
throw new Error(
|
|
616
|
-
"
|
|
616
|
+
"Uncommitted changes still remain after auto-commit.\n" + recheckDiff + recheckStaged,
|
|
617
617
|
);
|
|
618
618
|
}
|
|
619
|
-
logger.info("
|
|
619
|
+
logger.info("Auto-commit completed.");
|
|
620
620
|
}
|
|
621
621
|
} catch (err) {
|
|
622
622
|
logger.error(err instanceof Error ? err.message : err);
|
|
@@ -627,31 +627,31 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
627
627
|
|
|
628
628
|
//#endregion
|
|
629
629
|
|
|
630
|
-
//#region Phase 2 & 3:
|
|
630
|
+
//#region Phase 2 & 3: Build or noBuild warning
|
|
631
631
|
|
|
632
632
|
let version = projPkg.version;
|
|
633
633
|
|
|
634
634
|
if (noBuild) {
|
|
635
|
-
// noBuild
|
|
636
|
-
logger.warn("
|
|
637
|
-
await waitWithCountdown("
|
|
635
|
+
// noBuild warning
|
|
636
|
+
logger.warn("Deploying without building is quite dangerous.");
|
|
637
|
+
await waitWithCountdown("Press 'CTRL+C' to stop the process.", 5);
|
|
638
638
|
} else {
|
|
639
|
-
//
|
|
640
|
-
logger.debug("
|
|
639
|
+
// Version upgrade
|
|
640
|
+
logger.debug("Upgrading version...");
|
|
641
641
|
const upgradeResult = await upgradeVersion(cwd, allPkgPaths, dryRun);
|
|
642
642
|
version = upgradeResult.version;
|
|
643
643
|
const _changedFiles = upgradeResult.changedFiles;
|
|
644
644
|
if (dryRun) {
|
|
645
|
-
logger.info(`[DRY-RUN]
|
|
645
|
+
logger.info(`[DRY-RUN] Version upgrade: ${projPkg.version} → ${version} (files not modified)`);
|
|
646
646
|
} else {
|
|
647
|
-
logger.info(
|
|
647
|
+
logger.info(`Version upgrade: ${projPkg.version} → ${version}`);
|
|
648
648
|
}
|
|
649
649
|
|
|
650
|
-
//
|
|
650
|
+
// Run build
|
|
651
651
|
if (dryRun) {
|
|
652
|
-
logger.info("[DRY-RUN]
|
|
652
|
+
logger.info("[DRY-RUN] Starting build (validation only)...");
|
|
653
653
|
} else {
|
|
654
|
-
logger.debug("
|
|
654
|
+
logger.debug("Starting build...");
|
|
655
655
|
}
|
|
656
656
|
|
|
657
657
|
try {
|
|
@@ -660,17 +660,17 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
660
660
|
options: options.options,
|
|
661
661
|
});
|
|
662
662
|
|
|
663
|
-
//
|
|
663
|
+
// Check build failure
|
|
664
664
|
if (process.exitCode === 1) {
|
|
665
|
-
throw new Error("
|
|
665
|
+
throw new Error("Build failed");
|
|
666
666
|
}
|
|
667
667
|
} catch {
|
|
668
668
|
if (dryRun) {
|
|
669
|
-
logger.error("[DRY-RUN]
|
|
669
|
+
logger.error("[DRY-RUN] Build failed");
|
|
670
670
|
} else {
|
|
671
671
|
logger.error(
|
|
672
|
-
"
|
|
673
|
-
"
|
|
672
|
+
"Build failed. Manual recovery may be necessary:\n" +
|
|
673
|
+
" To revert version changes:\n" +
|
|
674
674
|
" git checkout -- package.json packages/*/package.json packages/sd-cli/templates/",
|
|
675
675
|
);
|
|
676
676
|
}
|
|
@@ -678,34 +678,34 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
678
678
|
return;
|
|
679
679
|
}
|
|
680
680
|
|
|
681
|
-
//#region Phase 3: Git
|
|
681
|
+
//#region Phase 3: Git commit/tag/push
|
|
682
682
|
|
|
683
683
|
if (hasGit) {
|
|
684
684
|
if (dryRun) {
|
|
685
|
-
logger.info("[DRY-RUN] Git
|
|
686
|
-
logger.info(`[DRY-RUN] git add (${_changedFiles.length}
|
|
685
|
+
logger.info("[DRY-RUN] Simulating Git commit/tag/push...");
|
|
686
|
+
logger.info(`[DRY-RUN] git add (${_changedFiles.length} files)`);
|
|
687
687
|
logger.info(`[DRY-RUN] git commit -m "v${version}"`);
|
|
688
688
|
logger.info(`[DRY-RUN] git tag -a v${version} -m "v${version}"`);
|
|
689
689
|
logger.info("[DRY-RUN] git push --dry-run");
|
|
690
690
|
await execa("git", ["push", "--dry-run"]);
|
|
691
691
|
logger.info("[DRY-RUN] git push --tags --dry-run");
|
|
692
692
|
await execa("git", ["push", "--tags", "--dry-run"]);
|
|
693
|
-
logger.info("[DRY-RUN] Git
|
|
693
|
+
logger.info("[DRY-RUN] Git operations simulation completed");
|
|
694
694
|
} else {
|
|
695
|
-
logger.debug("Git
|
|
695
|
+
logger.debug("Git commit/tag/push...");
|
|
696
696
|
try {
|
|
697
697
|
await execa("git", ["add", ..._changedFiles]);
|
|
698
698
|
await execa("git", ["commit", "-m", `v${version}`]);
|
|
699
699
|
await execa("git", ["tag", "-a", `v${version}`, "-m", `v${version}`]);
|
|
700
700
|
await execa("git", ["push"]);
|
|
701
701
|
await execa("git", ["push", "--tags"]);
|
|
702
|
-
logger.debug("Git
|
|
702
|
+
logger.debug("Git operations completed");
|
|
703
703
|
} catch (err) {
|
|
704
704
|
logger.error(
|
|
705
|
-
`Git
|
|
706
|
-
"
|
|
707
|
-
` git revert HEAD #
|
|
708
|
-
` git tag -d v${version} #
|
|
705
|
+
`Git operations failed: ${err instanceof Error ? err.message : err}\n` +
|
|
706
|
+
"Manual recovery may be necessary:\n" +
|
|
707
|
+
` git revert HEAD # Revert version commit\n` +
|
|
708
|
+
` git tag -d v${version} # Delete tag`,
|
|
709
709
|
);
|
|
710
710
|
process.exitCode = 1;
|
|
711
711
|
return;
|
|
@@ -718,20 +718,20 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
718
718
|
|
|
719
719
|
//#endregion
|
|
720
720
|
|
|
721
|
-
//#region Phase 4:
|
|
721
|
+
//#region Phase 4: Deployment (sequential by dependency level, parallel within level)
|
|
722
722
|
|
|
723
723
|
const levels = await computePublishLevels(publishPackages);
|
|
724
724
|
const publishedPackages: string[] = [];
|
|
725
725
|
let publishFailed = false;
|
|
726
726
|
|
|
727
|
-
//
|
|
727
|
+
// Sequential execution per level
|
|
728
728
|
for (let levelIdx = 0; levelIdx < levels.length; levelIdx++) {
|
|
729
729
|
if (publishFailed) break;
|
|
730
730
|
|
|
731
731
|
const levelPkgs = levels[levelIdx];
|
|
732
732
|
logger.start(`Level ${levelIdx + 1}/${levels.length}`);
|
|
733
733
|
|
|
734
|
-
//
|
|
734
|
+
// Parallel execution within level (Promise.allSettled)
|
|
735
735
|
const publishPromises = levelPkgs.map(async (pkg) => {
|
|
736
736
|
const maxRetries = 3;
|
|
737
737
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
@@ -745,8 +745,8 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
745
745
|
const delay = attempt * 5_000;
|
|
746
746
|
logger.debug(
|
|
747
747
|
dryRun
|
|
748
|
-
? `[DRY-RUN] ${pkg.name} (
|
|
749
|
-
: `${pkg.name} (
|
|
748
|
+
? `[DRY-RUN] ${pkg.name} (retry ${attempt + 1}/${maxRetries})`
|
|
749
|
+
: `${pkg.name} (retry ${attempt + 1}/${maxRetries})`,
|
|
750
750
|
);
|
|
751
751
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
752
752
|
} else {
|
|
@@ -754,13 +754,13 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
754
754
|
}
|
|
755
755
|
}
|
|
756
756
|
}
|
|
757
|
-
//
|
|
757
|
+
// Fallback for TypeScript type checker (actually unreachable)
|
|
758
758
|
return { status: "error" as const, name: pkg.name, error: new Error("Unknown error") };
|
|
759
759
|
});
|
|
760
760
|
|
|
761
761
|
const results = await Promise.allSettled(publishPromises);
|
|
762
762
|
|
|
763
|
-
//
|
|
763
|
+
// Check for failures within level
|
|
764
764
|
const levelFailed = results.some((r) => r.status === "rejected");
|
|
765
765
|
if (levelFailed) {
|
|
766
766
|
publishFailed = true;
|
|
@@ -770,23 +770,23 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
770
770
|
}
|
|
771
771
|
}
|
|
772
772
|
|
|
773
|
-
//
|
|
773
|
+
// Check failed packages
|
|
774
774
|
const allPkgNames = publishPackages.map((p) => p.name);
|
|
775
775
|
const failedPkgNames = allPkgNames.filter((n) => !publishedPackages.includes(n));
|
|
776
776
|
|
|
777
777
|
if (failedPkgNames.length > 0) {
|
|
778
778
|
if (publishedPackages.length > 0) {
|
|
779
779
|
logger.error(
|
|
780
|
-
"
|
|
781
|
-
"
|
|
780
|
+
"Error during deployment.\n" +
|
|
781
|
+
"Already deployed packages:\n" +
|
|
782
782
|
publishedPackages.map((n) => ` - ${n}`).join("\n") +
|
|
783
|
-
"\n\
|
|
784
|
-
"npm
|
|
783
|
+
"\n\nManual recovery may be necessary.\n" +
|
|
784
|
+
"npm packages can be deleted within 72 hours with `npm unpublish <pkg>@<version>`.",
|
|
785
785
|
);
|
|
786
786
|
}
|
|
787
787
|
|
|
788
788
|
for (const name of failedPkgNames) {
|
|
789
|
-
logger.error(`[${name}]
|
|
789
|
+
logger.error(`[${name}] Deployment failed`);
|
|
790
790
|
}
|
|
791
791
|
process.exitCode = 1;
|
|
792
792
|
return;
|
|
@@ -798,9 +798,9 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
798
798
|
|
|
799
799
|
if (sdConfig.postPublish != null && sdConfig.postPublish.length > 0) {
|
|
800
800
|
if (dryRun) {
|
|
801
|
-
logger.info("[DRY-RUN] postPublish
|
|
801
|
+
logger.info("[DRY-RUN] Simulating postPublish scripts...");
|
|
802
802
|
} else {
|
|
803
|
-
logger.debug("postPublish
|
|
803
|
+
logger.debug("Running postPublish scripts...");
|
|
804
804
|
}
|
|
805
805
|
|
|
806
806
|
for (const script of sdConfig.postPublish) {
|
|
@@ -809,15 +809,15 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
809
809
|
const args = script.args.map((arg) => replaceEnvVariables(arg, version, cwd));
|
|
810
810
|
|
|
811
811
|
if (dryRun) {
|
|
812
|
-
logger.info(`[DRY-RUN]
|
|
812
|
+
logger.info(`[DRY-RUN] Will execute: ${cmd} ${args.join(" ")}`);
|
|
813
813
|
} else {
|
|
814
|
-
logger.debug(
|
|
814
|
+
logger.debug(`Executing: ${cmd} ${args.join(" ")}`);
|
|
815
815
|
await execa(cmd, args, { cwd });
|
|
816
816
|
}
|
|
817
817
|
} catch (err) {
|
|
818
|
-
// postPublish
|
|
818
|
+
// On postPublish failure, only warn (no deployment rollback possible)
|
|
819
819
|
logger.warn(
|
|
820
|
-
`postPublish
|
|
820
|
+
`postPublish script failed (continuing): ${err instanceof Error ? err.message : err}`,
|
|
821
821
|
);
|
|
822
822
|
}
|
|
823
823
|
}
|
|
@@ -826,9 +826,9 @@ export async function runPublish(options: PublishOptions): Promise<void> {
|
|
|
826
826
|
//#endregion
|
|
827
827
|
|
|
828
828
|
if (dryRun) {
|
|
829
|
-
logger.info(`[DRY-RUN]
|
|
829
|
+
logger.info(`[DRY-RUN] Simulation completed. Actual deployment version: v${version}`);
|
|
830
830
|
} else {
|
|
831
|
-
logger.info(
|
|
831
|
+
logger.info(`All deployments completed. (v${version})`);
|
|
832
832
|
}
|
|
833
833
|
}
|
|
834
834
|
|