@wyxos/zephyr 0.2.7 → 0.2.9
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/package.json +1 -1
- package/src/release-node.mjs +115 -75
package/package.json
CHANGED
package/src/release-node.mjs
CHANGED
|
@@ -5,24 +5,14 @@ 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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function logSuccess(message) {
|
|
20
|
-
console.log(`${OK_PREFIX} ${message}`)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function logWarning(message) {
|
|
24
|
-
console.warn(`${WARN_PREFIX} ${message}`)
|
|
25
|
-
}
|
|
12
|
+
const logProcessing = (message = '') => console.log(chalk.yellow(message))
|
|
13
|
+
const logSuccess = (message = '') => console.log(chalk.green(message))
|
|
14
|
+
const logWarning = (message = '') => console.warn(chalk.yellow(message))
|
|
15
|
+
const logError = (message = '') => console.error(chalk.red(message))
|
|
26
16
|
|
|
27
17
|
function runCommand(command, args, { cwd = process.cwd(), capture = false, useShell = false } = {}) {
|
|
28
18
|
return new Promise((resolve, reject) => {
|
|
@@ -114,10 +104,13 @@ async function ensureUpToDateWithUpstream(branch, upstreamRef, rootDir = process
|
|
|
114
104
|
const remoteBranch = branchParts.join('/')
|
|
115
105
|
|
|
116
106
|
if (remoteName && remoteBranch) {
|
|
117
|
-
|
|
107
|
+
logProcessing(`Fetching latest updates from ${remoteName}/${remoteBranch}...`)
|
|
118
108
|
try {
|
|
119
|
-
await runCommand('git', ['fetch', remoteName, remoteBranch], { cwd: rootDir })
|
|
109
|
+
await runCommand('git', ['fetch', remoteName, remoteBranch], { capture: true, cwd: rootDir })
|
|
120
110
|
} catch (error) {
|
|
111
|
+
if (error.stderr) {
|
|
112
|
+
logError(error.stderr)
|
|
113
|
+
}
|
|
121
114
|
throw new Error(`Failed to fetch ${upstreamRef}: ${error.message}`)
|
|
122
115
|
}
|
|
123
116
|
}
|
|
@@ -136,11 +129,14 @@ async function ensureUpToDateWithUpstream(branch, upstreamRef, rootDir = process
|
|
|
136
129
|
|
|
137
130
|
if (Number.isFinite(behind) && behind > 0) {
|
|
138
131
|
if (remoteName && remoteBranch) {
|
|
139
|
-
|
|
132
|
+
logProcessing(`Fast-forwarding ${branch} with ${upstreamRef}...`)
|
|
140
133
|
|
|
141
134
|
try {
|
|
142
|
-
await runCommand('git', ['pull', '--ff-only', remoteName, remoteBranch], { cwd: rootDir })
|
|
135
|
+
await runCommand('git', ['pull', '--ff-only', remoteName, remoteBranch], { capture: true, cwd: rootDir })
|
|
143
136
|
} catch (error) {
|
|
137
|
+
if (error.stderr) {
|
|
138
|
+
logError(error.stderr)
|
|
139
|
+
}
|
|
144
140
|
throw new Error(
|
|
145
141
|
`Unable to fast-forward ${branch} with ${upstreamRef}. Resolve conflicts manually, then rerun the release.\n${error.message}`
|
|
146
142
|
)
|
|
@@ -198,11 +194,11 @@ async function runLint(skipLint, pkg, rootDir = process.cwd()) {
|
|
|
198
194
|
}
|
|
199
195
|
|
|
200
196
|
if (!hasScript(pkg, 'lint')) {
|
|
201
|
-
|
|
197
|
+
logProcessing('Skipping lint (no lint script found in package.json).')
|
|
202
198
|
return
|
|
203
199
|
}
|
|
204
200
|
|
|
205
|
-
|
|
201
|
+
logProcessing('Running lint...')
|
|
206
202
|
await runCommand('npm', ['run', 'lint'], { cwd: rootDir })
|
|
207
203
|
logSuccess('Lint passed.')
|
|
208
204
|
}
|
|
@@ -215,11 +211,11 @@ async function runTests(skipTests, pkg, rootDir = process.cwd()) {
|
|
|
215
211
|
|
|
216
212
|
// Check for test:run or test script
|
|
217
213
|
if (!hasScript(pkg, 'test:run') && !hasScript(pkg, 'test')) {
|
|
218
|
-
|
|
214
|
+
logProcessing('Skipping tests (no test or test:run script found in package.json).')
|
|
219
215
|
return
|
|
220
216
|
}
|
|
221
217
|
|
|
222
|
-
|
|
218
|
+
logProcessing('Running test suite...')
|
|
223
219
|
|
|
224
220
|
let dotInterval = null
|
|
225
221
|
try {
|
|
@@ -251,10 +247,10 @@ async function runTests(skipTests, pkg, rootDir = process.cwd()) {
|
|
|
251
247
|
}
|
|
252
248
|
process.stdout.write('\n')
|
|
253
249
|
if (error.stdout) {
|
|
254
|
-
|
|
250
|
+
logError(error.stdout)
|
|
255
251
|
}
|
|
256
252
|
if (error.stderr) {
|
|
257
|
-
|
|
253
|
+
logError(error.stderr)
|
|
258
254
|
}
|
|
259
255
|
throw error
|
|
260
256
|
}
|
|
@@ -267,11 +263,11 @@ async function runBuild(skipBuild, pkg, rootDir = process.cwd()) {
|
|
|
267
263
|
}
|
|
268
264
|
|
|
269
265
|
if (!hasScript(pkg, 'build')) {
|
|
270
|
-
|
|
266
|
+
logProcessing('Skipping build (no build script found in package.json).')
|
|
271
267
|
return
|
|
272
268
|
}
|
|
273
269
|
|
|
274
|
-
|
|
270
|
+
logProcessing('Building project...')
|
|
275
271
|
await runCommand('npm', ['run', 'build'], { cwd: rootDir })
|
|
276
272
|
logSuccess('Build completed.')
|
|
277
273
|
}
|
|
@@ -283,11 +279,11 @@ async function runLibBuild(skipBuild, pkg, rootDir = process.cwd()) {
|
|
|
283
279
|
}
|
|
284
280
|
|
|
285
281
|
if (!hasScript(pkg, 'build:lib')) {
|
|
286
|
-
|
|
282
|
+
logProcessing('Skipping library build (no build:lib script found in package.json).')
|
|
287
283
|
return false
|
|
288
284
|
}
|
|
289
285
|
|
|
290
|
-
|
|
286
|
+
logProcessing('Building library...')
|
|
291
287
|
await runCommand('npm', ['run', 'build:lib'], { cwd: rootDir })
|
|
292
288
|
logSuccess('Library built.')
|
|
293
289
|
|
|
@@ -299,9 +295,9 @@ async function runLibBuild(skipBuild, pkg, rootDir = process.cwd()) {
|
|
|
299
295
|
})
|
|
300
296
|
|
|
301
297
|
if (hasLibChanges) {
|
|
302
|
-
|
|
303
|
-
await runCommand('git', ['add', 'lib/'], { cwd: rootDir })
|
|
304
|
-
await runCommand('git', ['commit', '-m', 'chore: build lib artifacts'], { cwd: rootDir })
|
|
298
|
+
logProcessing('Committing lib build artifacts...')
|
|
299
|
+
await runCommand('git', ['add', 'lib/'], { capture: true, cwd: rootDir })
|
|
300
|
+
await runCommand('git', ['commit', '-m', 'chore: build lib artifacts'], { capture: true, cwd: rootDir })
|
|
305
301
|
logSuccess('Lib build artifacts committed.')
|
|
306
302
|
}
|
|
307
303
|
|
|
@@ -309,13 +305,24 @@ async function runLibBuild(skipBuild, pkg, rootDir = process.cwd()) {
|
|
|
309
305
|
}
|
|
310
306
|
|
|
311
307
|
async function ensureNpmAuth(rootDir = process.cwd()) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
308
|
+
logProcessing('Confirming npm authentication...')
|
|
309
|
+
try {
|
|
310
|
+
const result = await runCommand('npm', ['whoami'], { capture: true, cwd: rootDir })
|
|
311
|
+
// Only show username if we captured it, otherwise just show success
|
|
312
|
+
if (result?.stdout) {
|
|
313
|
+
// Silently authenticated - we don't need to show the username
|
|
314
|
+
}
|
|
315
|
+
logSuccess('npm authenticated.')
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (error.stderr) {
|
|
318
|
+
logError(error.stderr)
|
|
319
|
+
}
|
|
320
|
+
throw error
|
|
321
|
+
}
|
|
315
322
|
}
|
|
316
323
|
|
|
317
324
|
async function bumpVersion(releaseType, rootDir = process.cwd()) {
|
|
318
|
-
|
|
325
|
+
logProcessing(`Bumping package version...`)
|
|
319
326
|
|
|
320
327
|
// Lib changes should already be committed by runLibBuild, but check anyway
|
|
321
328
|
const { stdout: statusBefore } = await runCommand('git', ['status', '--porcelain'], { capture: true, cwd: rootDir })
|
|
@@ -325,22 +332,29 @@ async function bumpVersion(releaseType, rootDir = process.cwd()) {
|
|
|
325
332
|
})
|
|
326
333
|
|
|
327
334
|
if (hasLibChanges) {
|
|
328
|
-
|
|
329
|
-
await runCommand('git', ['stash', 'push', '-u', '-m', 'temp: lib build artifacts', 'lib/'], { cwd: rootDir })
|
|
335
|
+
logProcessing('Stashing lib build artifacts...')
|
|
336
|
+
await runCommand('git', ['stash', 'push', '-u', '-m', 'temp: lib build artifacts', 'lib/'], { capture: true, cwd: rootDir })
|
|
330
337
|
}
|
|
331
338
|
|
|
332
339
|
try {
|
|
333
340
|
// npm version will update package.json and create a commit with default message
|
|
334
|
-
await runCommand('npm', ['version', releaseType], { cwd: rootDir })
|
|
341
|
+
const result = await runCommand('npm', ['version', releaseType], { capture: true, cwd: rootDir })
|
|
342
|
+
// Extract version from output (e.g., "v0.2.8" or "0.2.8")
|
|
343
|
+
if (result?.stdout) {
|
|
344
|
+
const versionMatch = result.stdout.match(/v?(\d+\.\d+\.\d+)/)
|
|
345
|
+
if (versionMatch) {
|
|
346
|
+
// Version is shown in the logSuccess message below, no need to show it here
|
|
347
|
+
}
|
|
348
|
+
}
|
|
335
349
|
} finally {
|
|
336
350
|
// Restore lib changes and ensure they're in the commit
|
|
337
351
|
if (hasLibChanges) {
|
|
338
|
-
|
|
339
|
-
await runCommand('git', ['stash', 'pop'], { cwd: rootDir })
|
|
340
|
-
await runCommand('git', ['add', 'lib/'], { cwd: rootDir })
|
|
352
|
+
logProcessing('Restoring lib build artifacts...')
|
|
353
|
+
await runCommand('git', ['stash', 'pop'], { capture: true, cwd: rootDir })
|
|
354
|
+
await runCommand('git', ['add', 'lib/'], { capture: true, cwd: rootDir })
|
|
341
355
|
const { stdout: statusAfter } = await runCommand('git', ['status', '--porcelain'], { capture: true, cwd: rootDir })
|
|
342
356
|
if (statusAfter.includes('lib/')) {
|
|
343
|
-
await runCommand('git', ['commit', '--amend', '--no-edit'], { cwd: rootDir })
|
|
357
|
+
await runCommand('git', ['commit', '--amend', '--no-edit'], { capture: true, cwd: rootDir })
|
|
344
358
|
}
|
|
345
359
|
}
|
|
346
360
|
}
|
|
@@ -349,16 +363,26 @@ async function bumpVersion(releaseType, rootDir = process.cwd()) {
|
|
|
349
363
|
const commitMessage = `chore: release ${pkg.version}`
|
|
350
364
|
|
|
351
365
|
// Amend the commit message to use our custom format
|
|
352
|
-
await runCommand('git', ['commit', '--amend', '-m', commitMessage], { cwd: rootDir })
|
|
366
|
+
await runCommand('git', ['commit', '--amend', '-m', commitMessage], { capture: true, cwd: rootDir })
|
|
353
367
|
|
|
354
368
|
logSuccess(`Version updated to ${pkg.version}.`)
|
|
355
369
|
return pkg
|
|
356
370
|
}
|
|
357
371
|
|
|
358
372
|
async function pushChanges(rootDir = process.cwd()) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
373
|
+
logProcessing('Pushing commits and tags to origin...')
|
|
374
|
+
try {
|
|
375
|
+
await runCommand('git', ['push', '--follow-tags'], { capture: true, cwd: rootDir })
|
|
376
|
+
logSuccess('Git push completed.')
|
|
377
|
+
} catch (error) {
|
|
378
|
+
if (error.stdout) {
|
|
379
|
+
logError(error.stdout)
|
|
380
|
+
}
|
|
381
|
+
if (error.stderr) {
|
|
382
|
+
logError(error.stderr)
|
|
383
|
+
}
|
|
384
|
+
throw error
|
|
385
|
+
}
|
|
362
386
|
}
|
|
363
387
|
|
|
364
388
|
async function publishPackage(pkg, rootDir = process.cwd()) {
|
|
@@ -371,9 +395,19 @@ async function publishPackage(pkg, rootDir = process.cwd()) {
|
|
|
371
395
|
publishArgs.push('--access', access)
|
|
372
396
|
}
|
|
373
397
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
398
|
+
logProcessing(`Publishing ${pkg.name}@${pkg.version} to npm...`)
|
|
399
|
+
try {
|
|
400
|
+
await runCommand('npm', publishArgs, { capture: true, cwd: rootDir })
|
|
401
|
+
logSuccess('npm publish completed.')
|
|
402
|
+
} catch (error) {
|
|
403
|
+
if (error.stdout) {
|
|
404
|
+
logError(error.stdout)
|
|
405
|
+
}
|
|
406
|
+
if (error.stderr) {
|
|
407
|
+
logError(error.stderr)
|
|
408
|
+
}
|
|
409
|
+
throw error
|
|
410
|
+
}
|
|
377
411
|
}
|
|
378
412
|
|
|
379
413
|
function extractDomainFromHomepage(homepage) {
|
|
@@ -405,11 +439,11 @@ async function deployGHPages(skipDeploy, pkg, rootDir = process.cwd()) {
|
|
|
405
439
|
}
|
|
406
440
|
|
|
407
441
|
if (!distExists) {
|
|
408
|
-
|
|
442
|
+
logProcessing('Skipping GitHub Pages deployment (no dist directory found).')
|
|
409
443
|
return
|
|
410
444
|
}
|
|
411
445
|
|
|
412
|
-
|
|
446
|
+
logProcessing('Deploying to GitHub Pages...')
|
|
413
447
|
|
|
414
448
|
// Write CNAME file to dist if homepage is set
|
|
415
449
|
const cnamePath = path.join(distPath, 'CNAME')
|
|
@@ -459,35 +493,41 @@ async function deployGHPages(skipDeploy, pkg, rootDir = process.cwd()) {
|
|
|
459
493
|
}
|
|
460
494
|
|
|
461
495
|
export async function releaseNode() {
|
|
462
|
-
|
|
463
|
-
|
|
496
|
+
try {
|
|
497
|
+
const { releaseType, skipTests, skipLint, skipBuild, skipDeploy } = parseArgs()
|
|
498
|
+
const rootDir = process.cwd()
|
|
464
499
|
|
|
465
|
-
|
|
466
|
-
|
|
500
|
+
logProcessing('Reading package metadata...')
|
|
501
|
+
const pkg = await readPackage(rootDir)
|
|
467
502
|
|
|
468
|
-
|
|
469
|
-
|
|
503
|
+
logProcessing('Checking working tree status...')
|
|
504
|
+
await ensureCleanWorkingTree(rootDir)
|
|
470
505
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
506
|
+
const branch = await getCurrentBranch(rootDir)
|
|
507
|
+
if (!branch) {
|
|
508
|
+
throw new Error('Unable to determine current branch.')
|
|
509
|
+
}
|
|
475
510
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
511
|
+
logProcessing(`Current branch: ${branch}`)
|
|
512
|
+
const upstreamRef = await getUpstreamRef(rootDir)
|
|
513
|
+
await ensureUpToDateWithUpstream(branch, upstreamRef, rootDir)
|
|
479
514
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
515
|
+
await runLint(skipLint, pkg, rootDir)
|
|
516
|
+
await runTests(skipTests, pkg, rootDir)
|
|
517
|
+
await runBuild(skipBuild, pkg, rootDir)
|
|
518
|
+
await runLibBuild(skipBuild, pkg, rootDir)
|
|
519
|
+
await ensureNpmAuth(rootDir)
|
|
485
520
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
521
|
+
const updatedPkg = await bumpVersion(releaseType, rootDir)
|
|
522
|
+
await pushChanges(rootDir)
|
|
523
|
+
await publishPackage(updatedPkg, rootDir)
|
|
524
|
+
await deployGHPages(skipDeploy, updatedPkg, rootDir)
|
|
490
525
|
|
|
491
|
-
|
|
526
|
+
logSuccess(`Release workflow completed for ${updatedPkg.name}@${updatedPkg.version}.`)
|
|
527
|
+
} catch (error) {
|
|
528
|
+
logError('\nRelease failed:')
|
|
529
|
+
logError(error.message)
|
|
530
|
+
throw error
|
|
531
|
+
}
|
|
492
532
|
}
|
|
493
533
|
|