@wyxos/zephyr 0.2.12 → 0.2.14
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 +104 -104
- package/bin/zephyr.mjs +12 -12
- package/package.json +53 -53
- package/src/index.mjs +2121 -2121
- package/src/release-node.mjs +85 -142
- package/src/release-packagist.mjs +17 -3
- package/src/ssh-utils.mjs +278 -278
package/src/release-node.mjs
CHANGED
|
@@ -1,70 +1,105 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process'
|
|
1
|
+
import { spawn, exec } from 'node:child_process'
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { dirname, join } from 'node:path'
|
|
4
4
|
import { readFile } from 'node:fs/promises'
|
|
5
5
|
import fs from 'node:fs'
|
|
6
6
|
import path from 'node:path'
|
|
7
7
|
import process from 'node:process'
|
|
8
|
-
|
|
9
|
-
const STEP_PREFIX = '→'
|
|
10
|
-
const OK_PREFIX = '✔'
|
|
11
|
-
const WARN_PREFIX = '⚠'
|
|
8
|
+
import chalk from 'chalk'
|
|
12
9
|
|
|
13
10
|
const IS_WINDOWS = process.platform === 'win32'
|
|
14
11
|
|
|
15
12
|
function logStep(message) {
|
|
16
|
-
console.log(
|
|
13
|
+
console.log(chalk.yellow(`→ ${message}`))
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
function logSuccess(message) {
|
|
20
|
-
console.log(
|
|
17
|
+
console.log(chalk.green(`✔ ${message}`))
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
function logWarning(message) {
|
|
24
|
-
console.warn(
|
|
21
|
+
console.warn(chalk.yellow(`⚠ ${message}`))
|
|
25
22
|
}
|
|
26
23
|
|
|
27
24
|
function runCommand(command, args, { cwd = process.cwd(), capture = false, useShell = false } = {}) {
|
|
28
25
|
return new Promise((resolve, reject) => {
|
|
29
|
-
// On Windows, npm-related commands need shell
|
|
26
|
+
// On Windows, npm-related commands need shell to resolve npx.cmd
|
|
30
27
|
// Git commands work fine without shell, so we only use it when explicitly requested
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
const needsShell = useShell || (IS_WINDOWS && (command === 'npm' || command === 'npx'))
|
|
29
|
+
|
|
30
|
+
if (needsShell) {
|
|
31
|
+
// When using shell, use exec to avoid deprecation warning with spawn
|
|
32
|
+
// Properly escape arguments for Windows cmd.exe
|
|
33
|
+
const escapedArgs = args.map(arg => {
|
|
34
|
+
// If arg contains spaces or special chars, wrap in quotes and escape internal quotes
|
|
35
|
+
if (arg.includes(' ') || arg.includes('"') || arg.includes('&') || arg.includes('|')) {
|
|
36
|
+
return `"${arg.replace(/"/g, '\\"')}"`
|
|
37
|
+
}
|
|
38
|
+
return arg
|
|
39
|
+
})
|
|
40
|
+
const commandString = `${command} ${escapedArgs.join(' ')}`
|
|
41
|
+
|
|
42
|
+
exec(commandString, { cwd, encoding: 'utf8' }, (error, stdout, stderr) => {
|
|
43
|
+
if (error) {
|
|
44
|
+
const err = new Error(`Command failed (${error.code}): ${command} ${args.join(' ')}`)
|
|
45
|
+
if (capture) {
|
|
46
|
+
err.stdout = stdout || ''
|
|
47
|
+
err.stderr = stderr || ''
|
|
48
|
+
} else {
|
|
49
|
+
// When not capturing, exec still provides output, so show it
|
|
50
|
+
if (stdout) process.stdout.write(stdout)
|
|
51
|
+
if (stderr) process.stderr.write(stderr)
|
|
52
|
+
}
|
|
53
|
+
err.exitCode = error.code
|
|
54
|
+
reject(err)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
35
57
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
58
|
+
if (capture) {
|
|
59
|
+
resolve({ stdout: stdout.trim(), stderr: stderr.trim() })
|
|
60
|
+
} else {
|
|
61
|
+
// When not capturing, exec still provides output, so show it
|
|
62
|
+
if (stdout) process.stdout.write(stdout)
|
|
63
|
+
if (stderr) process.stderr.write(stderr)
|
|
64
|
+
resolve(undefined)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
} else {
|
|
68
|
+
// Use spawn for commands that don't need shell
|
|
69
|
+
const spawnOptions = {
|
|
70
|
+
cwd,
|
|
71
|
+
stdio: capture ? ['ignore', 'pipe', 'pipe'] : 'inherit'
|
|
72
|
+
}
|
|
39
73
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
74
|
+
const child = spawn(command, args, spawnOptions)
|
|
75
|
+
let stdout = ''
|
|
76
|
+
let stderr = ''
|
|
43
77
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
78
|
+
if (capture) {
|
|
79
|
+
child.stdout.on('data', (chunk) => {
|
|
80
|
+
stdout += chunk
|
|
81
|
+
})
|
|
48
82
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
83
|
+
child.stderr.on('data', (chunk) => {
|
|
84
|
+
stderr += chunk
|
|
85
|
+
})
|
|
86
|
+
}
|
|
53
87
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
88
|
+
child.on('error', reject)
|
|
89
|
+
child.on('close', (code) => {
|
|
90
|
+
if (code === 0) {
|
|
91
|
+
resolve(capture ? { stdout: stdout.trim(), stderr: stderr.trim() } : undefined)
|
|
92
|
+
} else {
|
|
93
|
+
const error = new Error(`Command failed (${code}): ${command} ${args.join(' ')}`)
|
|
94
|
+
if (capture) {
|
|
95
|
+
error.stdout = stdout
|
|
96
|
+
error.stderr = stderr
|
|
97
|
+
}
|
|
98
|
+
error.exitCode = code
|
|
99
|
+
reject(error)
|
|
63
100
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
})
|
|
101
|
+
})
|
|
102
|
+
}
|
|
68
103
|
})
|
|
69
104
|
}
|
|
70
105
|
|
|
@@ -210,29 +245,10 @@ async function runLint(skipLint, pkg, rootDir = process.cwd()) {
|
|
|
210
245
|
|
|
211
246
|
logStep('Running lint...')
|
|
212
247
|
|
|
213
|
-
let dotInterval = null
|
|
214
248
|
try {
|
|
215
|
-
|
|
216
|
-
process.stdout.write(' ')
|
|
217
|
-
dotInterval = setInterval(() => {
|
|
218
|
-
process.stdout.write('.')
|
|
219
|
-
}, 200)
|
|
220
|
-
|
|
221
|
-
await runCommand('npm', ['run', 'lint'], { capture: true, cwd: rootDir })
|
|
222
|
-
|
|
223
|
-
if (dotInterval) {
|
|
224
|
-
clearInterval(dotInterval)
|
|
225
|
-
dotInterval = null
|
|
226
|
-
}
|
|
227
|
-
process.stdout.write('\n')
|
|
249
|
+
await runCommand('npm', ['run', 'lint'], { cwd: rootDir })
|
|
228
250
|
logSuccess('Lint passed.')
|
|
229
251
|
} catch (error) {
|
|
230
|
-
// Clear dots and show error output
|
|
231
|
-
if (dotInterval) {
|
|
232
|
-
clearInterval(dotInterval)
|
|
233
|
-
dotInterval = null
|
|
234
|
-
}
|
|
235
|
-
process.stdout.write('\n')
|
|
236
252
|
if (error.stdout) {
|
|
237
253
|
console.error(error.stdout)
|
|
238
254
|
}
|
|
@@ -257,35 +273,18 @@ async function runTests(skipTests, pkg, rootDir = process.cwd()) {
|
|
|
257
273
|
|
|
258
274
|
logStep('Running test suite...')
|
|
259
275
|
|
|
260
|
-
let dotInterval = null
|
|
261
276
|
try {
|
|
262
|
-
//
|
|
263
|
-
process.stdout.write(' ')
|
|
264
|
-
dotInterval = setInterval(() => {
|
|
265
|
-
process.stdout.write('.')
|
|
266
|
-
}, 200)
|
|
267
|
-
|
|
268
|
-
// Prefer test:run if available, otherwise use test with --run flag
|
|
277
|
+
// Prefer test:run if available, otherwise use test with --run and --reporter flags
|
|
269
278
|
if (hasScript(pkg, 'test:run')) {
|
|
270
|
-
|
|
279
|
+
// Pass reporter flag to test:run script
|
|
280
|
+
await runCommand('npm', ['run', 'test:run', '--', '--reporter=dot'], { cwd: rootDir })
|
|
271
281
|
} else {
|
|
272
|
-
// For test script,
|
|
273
|
-
await runCommand('npm', ['test', '--', '--run'], {
|
|
282
|
+
// For test script, pass --run and --reporter flags (works with vitest)
|
|
283
|
+
await runCommand('npm', ['test', '--', '--run', '--reporter=dot'], { cwd: rootDir })
|
|
274
284
|
}
|
|
275
285
|
|
|
276
|
-
if (dotInterval) {
|
|
277
|
-
clearInterval(dotInterval)
|
|
278
|
-
dotInterval = null
|
|
279
|
-
}
|
|
280
|
-
process.stdout.write('\n')
|
|
281
286
|
logSuccess('Tests passed.')
|
|
282
287
|
} catch (error) {
|
|
283
|
-
// Clear dots and show error output
|
|
284
|
-
if (dotInterval) {
|
|
285
|
-
clearInterval(dotInterval)
|
|
286
|
-
dotInterval = null
|
|
287
|
-
}
|
|
288
|
-
process.stdout.write('\n')
|
|
289
288
|
if (error.stdout) {
|
|
290
289
|
console.error(error.stdout)
|
|
291
290
|
}
|
|
@@ -309,29 +308,10 @@ async function runBuild(skipBuild, pkg, rootDir = process.cwd()) {
|
|
|
309
308
|
|
|
310
309
|
logStep('Building project...')
|
|
311
310
|
|
|
312
|
-
let dotInterval = null
|
|
313
311
|
try {
|
|
314
|
-
|
|
315
|
-
process.stdout.write(' ')
|
|
316
|
-
dotInterval = setInterval(() => {
|
|
317
|
-
process.stdout.write('.')
|
|
318
|
-
}, 200)
|
|
319
|
-
|
|
320
|
-
await runCommand('npm', ['run', 'build'], { capture: true, cwd: rootDir })
|
|
321
|
-
|
|
322
|
-
if (dotInterval) {
|
|
323
|
-
clearInterval(dotInterval)
|
|
324
|
-
dotInterval = null
|
|
325
|
-
}
|
|
326
|
-
process.stdout.write('\n')
|
|
312
|
+
await runCommand('npm', ['run', 'build'], { cwd: rootDir })
|
|
327
313
|
logSuccess('Build completed.')
|
|
328
314
|
} catch (error) {
|
|
329
|
-
// Clear dots and show error output
|
|
330
|
-
if (dotInterval) {
|
|
331
|
-
clearInterval(dotInterval)
|
|
332
|
-
dotInterval = null
|
|
333
|
-
}
|
|
334
|
-
process.stdout.write('\n')
|
|
335
315
|
if (error.stdout) {
|
|
336
316
|
console.error(error.stdout)
|
|
337
317
|
}
|
|
@@ -355,29 +335,10 @@ async function runLibBuild(skipBuild, pkg, rootDir = process.cwd()) {
|
|
|
355
335
|
|
|
356
336
|
logStep('Building library...')
|
|
357
337
|
|
|
358
|
-
let dotInterval = null
|
|
359
338
|
try {
|
|
360
|
-
|
|
361
|
-
process.stdout.write(' ')
|
|
362
|
-
dotInterval = setInterval(() => {
|
|
363
|
-
process.stdout.write('.')
|
|
364
|
-
}, 200)
|
|
365
|
-
|
|
366
|
-
await runCommand('npm', ['run', 'build:lib'], { capture: true, cwd: rootDir })
|
|
367
|
-
|
|
368
|
-
if (dotInterval) {
|
|
369
|
-
clearInterval(dotInterval)
|
|
370
|
-
dotInterval = null
|
|
371
|
-
}
|
|
372
|
-
process.stdout.write('\n')
|
|
339
|
+
await runCommand('npm', ['run', 'build:lib'], { cwd: rootDir })
|
|
373
340
|
logSuccess('Library built.')
|
|
374
341
|
} catch (error) {
|
|
375
|
-
// Clear dots and show error output
|
|
376
|
-
if (dotInterval) {
|
|
377
|
-
clearInterval(dotInterval)
|
|
378
|
-
dotInterval = null
|
|
379
|
-
}
|
|
380
|
-
process.stdout.write('\n')
|
|
381
342
|
if (error.stdout) {
|
|
382
343
|
console.error(error.stdout)
|
|
383
344
|
}
|
|
@@ -488,7 +449,7 @@ async function pushChanges(rootDir = process.cwd()) {
|
|
|
488
449
|
async function publishPackage(pkg, rootDir = process.cwd()) {
|
|
489
450
|
// Check if package is configured as private/restricted
|
|
490
451
|
const isPrivate = pkg.publishConfig?.access === 'restricted'
|
|
491
|
-
|
|
452
|
+
|
|
492
453
|
if (isPrivate) {
|
|
493
454
|
logWarning('Skipping npm publish (package is configured as private/restricted).')
|
|
494
455
|
logWarning('Private packages require npm paid plan. Publish manually or use GitHub Packages.')
|
|
@@ -571,14 +532,7 @@ async function deployGHPages(skipDeploy, pkg, rootDir = process.cwd()) {
|
|
|
571
532
|
|
|
572
533
|
const worktreeDir = path.resolve(rootDir, '.gh-pages')
|
|
573
534
|
|
|
574
|
-
let dotInterval = null
|
|
575
535
|
try {
|
|
576
|
-
// Capture output and show dots as progress
|
|
577
|
-
process.stdout.write(' ')
|
|
578
|
-
dotInterval = setInterval(() => {
|
|
579
|
-
process.stdout.write('.')
|
|
580
|
-
}, 200)
|
|
581
|
-
|
|
582
536
|
try {
|
|
583
537
|
await runCommand('git', ['worktree', 'remove', worktreeDir, '-f'], { capture: true, cwd: rootDir })
|
|
584
538
|
} catch { }
|
|
@@ -606,19 +560,8 @@ async function deployGHPages(skipDeploy, pkg, rootDir = process.cwd()) {
|
|
|
606
560
|
await runCommand('git', ['-C', worktreeDir, 'commit', '-m', `deploy: demo ${new Date().toISOString()}`, '--allow-empty'], { capture: true })
|
|
607
561
|
await runCommand('git', ['-C', worktreeDir, 'push', '-f', 'origin', 'gh-pages'], { capture: true })
|
|
608
562
|
|
|
609
|
-
if (dotInterval) {
|
|
610
|
-
clearInterval(dotInterval)
|
|
611
|
-
dotInterval = null
|
|
612
|
-
}
|
|
613
|
-
process.stdout.write('\n')
|
|
614
563
|
logSuccess('GitHub Pages deployment completed.')
|
|
615
564
|
} catch (error) {
|
|
616
|
-
// Clear dots and show error output
|
|
617
|
-
if (dotInterval) {
|
|
618
|
-
clearInterval(dotInterval)
|
|
619
|
-
dotInterval = null
|
|
620
|
-
}
|
|
621
|
-
process.stdout.write('\n')
|
|
622
565
|
if (error.stdout) {
|
|
623
566
|
console.error(error.stdout)
|
|
624
567
|
}
|
|
@@ -651,11 +594,11 @@ export async function releaseNode() {
|
|
|
651
594
|
|
|
652
595
|
await runLint(skipLint, pkg, rootDir)
|
|
653
596
|
await runTests(skipTests, pkg, rootDir)
|
|
654
|
-
await runBuild(skipBuild, pkg, rootDir)
|
|
655
597
|
await runLibBuild(skipBuild, pkg, rootDir)
|
|
656
598
|
await ensureNpmAuth(rootDir)
|
|
657
599
|
|
|
658
600
|
const updatedPkg = await bumpVersion(releaseType, rootDir)
|
|
601
|
+
await runBuild(skipBuild, updatedPkg, rootDir)
|
|
659
602
|
await pushChanges(rootDir)
|
|
660
603
|
await publishPackage(updatedPkg, rootDir)
|
|
661
604
|
await deployGHPages(skipDeploy, updatedPkg, rootDir)
|
|
@@ -27,16 +27,30 @@ function logWarning(message) {
|
|
|
27
27
|
|
|
28
28
|
function runCommand(command, args, { cwd = process.cwd(), capture = false, useShell = false } = {}) {
|
|
29
29
|
return new Promise((resolve, reject) => {
|
|
30
|
+
const needsShell = useShell || (IS_WINDOWS && (command === 'php' || command === 'composer'))
|
|
31
|
+
|
|
30
32
|
const spawnOptions = {
|
|
31
33
|
cwd,
|
|
32
34
|
stdio: capture ? ['ignore', 'pipe', 'pipe'] : 'inherit'
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
let child
|
|
38
|
+
if (needsShell) {
|
|
39
|
+
// When using shell, construct the command string to avoid deprecation warning
|
|
40
|
+
// Properly escape arguments for Windows cmd.exe
|
|
41
|
+
const escapedArgs = args.map(arg => {
|
|
42
|
+
// If arg contains spaces or special chars, wrap in quotes and escape internal quotes
|
|
43
|
+
if (arg.includes(' ') || arg.includes('"') || arg.includes('&') || arg.includes('|')) {
|
|
44
|
+
return `"${arg.replace(/"/g, '\\"')}"`
|
|
45
|
+
}
|
|
46
|
+
return arg
|
|
47
|
+
})
|
|
48
|
+
const commandString = `${command} ${escapedArgs.join(' ')}`
|
|
36
49
|
spawnOptions.shell = true
|
|
50
|
+
child = spawn(commandString, [], spawnOptions)
|
|
51
|
+
} else {
|
|
52
|
+
child = spawn(command, args, spawnOptions)
|
|
37
53
|
}
|
|
38
|
-
|
|
39
|
-
const child = spawn(command, args, spawnOptions)
|
|
40
54
|
let stdout = ''
|
|
41
55
|
let stderr = ''
|
|
42
56
|
|