@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/release-node.mjs +115 -75
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wyxos/zephyr",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "A streamlined deployment tool for web applications with intelligent Laravel project detection",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -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
- function logStep(message) {
16
- console.log(`${STEP_PREFIX} ${message}`)
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
- logStep(`Fetching latest updates from ${remoteName}/${remoteBranch}...`)
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
- logStep(`Fast-forwarding ${branch} with ${upstreamRef}...`)
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
- logStep('Skipping lint (no lint script found in package.json).')
197
+ logProcessing('Skipping lint (no lint script found in package.json).')
202
198
  return
203
199
  }
204
200
 
205
- logStep('Running lint...')
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
- logStep('Skipping tests (no test or test:run script found in package.json).')
214
+ logProcessing('Skipping tests (no test or test:run script found in package.json).')
219
215
  return
220
216
  }
221
217
 
222
- logStep('Running test suite...')
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
- console.error(error.stdout)
250
+ logError(error.stdout)
255
251
  }
256
252
  if (error.stderr) {
257
- console.error(error.stderr)
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
- logStep('Skipping build (no build script found in package.json).')
266
+ logProcessing('Skipping build (no build script found in package.json).')
271
267
  return
272
268
  }
273
269
 
274
- logStep('Building project...')
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
- logStep('Skipping library build (no build:lib script found in package.json).')
282
+ logProcessing('Skipping library build (no build:lib script found in package.json).')
287
283
  return false
288
284
  }
289
285
 
290
- logStep('Building library...')
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
- logStep('Committing lib build artifacts...')
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
- logStep('Confirming npm authentication...')
313
- await runCommand('npm', ['whoami'], { cwd: rootDir })
314
- logSuccess('npm authenticated.')
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
- logStep(`Bumping package version...`)
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
- logStep('Stashing lib build artifacts...')
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
- logStep('Restoring lib build artifacts...')
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
- logStep('Pushing commits and tags to origin...')
360
- await runCommand('git', ['push', '--follow-tags'], { cwd: rootDir })
361
- logSuccess('Git push completed.')
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
- logStep(`Publishing ${pkg.name}@${pkg.version} to npm...`)
375
- await runCommand('npm', publishArgs, { cwd: rootDir })
376
- logSuccess('npm publish completed.')
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
- logStep('Skipping GitHub Pages deployment (no dist directory found).')
442
+ logProcessing('Skipping GitHub Pages deployment (no dist directory found).')
409
443
  return
410
444
  }
411
445
 
412
- logStep('Deploying to GitHub Pages...')
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
- const { releaseType, skipTests, skipLint, skipBuild, skipDeploy } = parseArgs()
463
- const rootDir = process.cwd()
496
+ try {
497
+ const { releaseType, skipTests, skipLint, skipBuild, skipDeploy } = parseArgs()
498
+ const rootDir = process.cwd()
464
499
 
465
- logStep('Reading package metadata...')
466
- const pkg = await readPackage(rootDir)
500
+ logProcessing('Reading package metadata...')
501
+ const pkg = await readPackage(rootDir)
467
502
 
468
- logStep('Checking working tree status...')
469
- await ensureCleanWorkingTree(rootDir)
503
+ logProcessing('Checking working tree status...')
504
+ await ensureCleanWorkingTree(rootDir)
470
505
 
471
- const branch = await getCurrentBranch(rootDir)
472
- if (!branch) {
473
- throw new Error('Unable to determine current branch.')
474
- }
506
+ const branch = await getCurrentBranch(rootDir)
507
+ if (!branch) {
508
+ throw new Error('Unable to determine current branch.')
509
+ }
475
510
 
476
- logStep(`Current branch: ${branch}`)
477
- const upstreamRef = await getUpstreamRef(rootDir)
478
- await ensureUpToDateWithUpstream(branch, upstreamRef, rootDir)
511
+ logProcessing(`Current branch: ${branch}`)
512
+ const upstreamRef = await getUpstreamRef(rootDir)
513
+ await ensureUpToDateWithUpstream(branch, upstreamRef, rootDir)
479
514
 
480
- await runLint(skipLint, pkg, rootDir)
481
- await runTests(skipTests, pkg, rootDir)
482
- await runBuild(skipBuild, pkg, rootDir)
483
- await runLibBuild(skipBuild, pkg, rootDir)
484
- await ensureNpmAuth(rootDir)
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
- const updatedPkg = await bumpVersion(releaseType, rootDir)
487
- await pushChanges(rootDir)
488
- await publishPackage(updatedPkg, rootDir)
489
- await deployGHPages(skipDeploy, updatedPkg, rootDir)
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
- logSuccess(`Release workflow completed for ${updatedPkg.name}@${updatedPkg.version}.`)
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