@wyxos/zephyr 0.2.5 → 0.2.7
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 +40 -9
- package/src/ssh-utils.mjs +42 -5
package/package.json
CHANGED
package/src/release-node.mjs
CHANGED
|
@@ -221,15 +221,43 @@ async function runTests(skipTests, pkg, rootDir = process.cwd()) {
|
|
|
221
221
|
|
|
222
222
|
logStep('Running test suite...')
|
|
223
223
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
224
|
+
let dotInterval = null
|
|
225
|
+
try {
|
|
226
|
+
// Capture output and show dots as progress
|
|
227
|
+
process.stdout.write(' ')
|
|
228
|
+
dotInterval = setInterval(() => {
|
|
229
|
+
process.stdout.write('.')
|
|
230
|
+
}, 200)
|
|
231
|
+
|
|
232
|
+
// Prefer test:run if available, otherwise use test with --run flag
|
|
233
|
+
if (hasScript(pkg, 'test:run')) {
|
|
234
|
+
await runCommand('npm', ['run', 'test:run'], { capture: true, cwd: rootDir })
|
|
235
|
+
} else {
|
|
236
|
+
// For test script, try to pass --run flag (works with vitest)
|
|
237
|
+
await runCommand('npm', ['test', '--', '--run'], { capture: true, cwd: rootDir })
|
|
238
|
+
}
|
|
231
239
|
|
|
232
|
-
|
|
240
|
+
if (dotInterval) {
|
|
241
|
+
clearInterval(dotInterval)
|
|
242
|
+
dotInterval = null
|
|
243
|
+
}
|
|
244
|
+
process.stdout.write('\n')
|
|
245
|
+
logSuccess('Tests passed.')
|
|
246
|
+
} catch (error) {
|
|
247
|
+
// Clear dots and show error output
|
|
248
|
+
if (dotInterval) {
|
|
249
|
+
clearInterval(dotInterval)
|
|
250
|
+
dotInterval = null
|
|
251
|
+
}
|
|
252
|
+
process.stdout.write('\n')
|
|
253
|
+
if (error.stdout) {
|
|
254
|
+
console.error(error.stdout)
|
|
255
|
+
}
|
|
256
|
+
if (error.stderr) {
|
|
257
|
+
console.error(error.stderr)
|
|
258
|
+
}
|
|
259
|
+
throw error
|
|
260
|
+
}
|
|
233
261
|
}
|
|
234
262
|
|
|
235
263
|
async function runBuild(skipBuild, pkg, rootDir = process.cwd()) {
|
|
@@ -337,7 +365,10 @@ async function publishPackage(pkg, rootDir = process.cwd()) {
|
|
|
337
365
|
const publishArgs = ['publish', '--ignore-scripts'] // Skip prepublishOnly since we already built lib
|
|
338
366
|
|
|
339
367
|
if (pkg.name.startsWith('@')) {
|
|
340
|
-
|
|
368
|
+
// For scoped packages, determine access level from publishConfig
|
|
369
|
+
// Default to 'public' for scoped packages if not specified (free npm accounts require public for scoped packages)
|
|
370
|
+
const access = pkg.publishConfig?.access || 'public'
|
|
371
|
+
publishArgs.push('--access', access)
|
|
341
372
|
}
|
|
342
373
|
|
|
343
374
|
logStep(`Publishing ${pkg.name}@${pkg.version} to npm...`)
|
package/src/ssh-utils.mjs
CHANGED
|
@@ -92,11 +92,11 @@ export async function connectToServer(config, rootDir) {
|
|
|
92
92
|
* @param {NodeSSH} ssh - SSH client instance
|
|
93
93
|
* @param {string} label - Human-readable label for the command
|
|
94
94
|
* @param {string} command - Command to execute
|
|
95
|
-
* @param {Object} options - Options: { cwd, allowFailure, printStdout, bootstrapEnv, rootDir, writeToLogFile }
|
|
95
|
+
* @param {Object} options - Options: { cwd, allowFailure, printStdout, bootstrapEnv, rootDir, writeToLogFile, env }
|
|
96
96
|
* @returns {Promise<Object>} Command result
|
|
97
97
|
*/
|
|
98
98
|
export async function executeRemoteCommand(ssh, label, command, options = {}) {
|
|
99
|
-
const { cwd, allowFailure = false, printStdout = true, bootstrapEnv = true, rootDir = null, writeToLogFile = null } = options
|
|
99
|
+
const { cwd, allowFailure = false, printStdout = true, bootstrapEnv = true, rootDir = null, writeToLogFile = null, env = {} } = options
|
|
100
100
|
|
|
101
101
|
logProcessing(`\n→ ${label}`)
|
|
102
102
|
|
|
@@ -120,14 +120,27 @@ export async function executeRemoteCommand(ssh, label, command, options = {}) {
|
|
|
120
120
|
].join('; ')
|
|
121
121
|
|
|
122
122
|
const escapeForDoubleQuotes = (value) => value.replace(/(["\\$`])/g, '\\$1')
|
|
123
|
+
const escapeForSingleQuotes = (value) => value.replace(/'/g, "'\\''")
|
|
124
|
+
|
|
125
|
+
// Build environment variable exports
|
|
126
|
+
let envExports = ''
|
|
127
|
+
if (Object.keys(env).length > 0) {
|
|
128
|
+
const envPairs = Object.entries(env).map(([key, value]) => {
|
|
129
|
+
const escapedValue = escapeForSingleQuotes(String(value))
|
|
130
|
+
return `${key}='${escapedValue}'`
|
|
131
|
+
})
|
|
132
|
+
envExports = envPairs.join(' ') + ' '
|
|
133
|
+
}
|
|
123
134
|
|
|
124
135
|
let wrappedCommand = command
|
|
125
136
|
let execOptions = { cwd }
|
|
126
137
|
|
|
127
138
|
if (bootstrapEnv && cwd) {
|
|
128
139
|
const cwdForShell = escapeForDoubleQuotes(cwd)
|
|
129
|
-
wrappedCommand = `${profileBootstrap}; cd "${cwdForShell}" && ${command}`
|
|
140
|
+
wrappedCommand = `${profileBootstrap}; cd "${cwdForShell}" && ${envExports}${command}`
|
|
130
141
|
execOptions = {}
|
|
142
|
+
} else if (Object.keys(env).length > 0) {
|
|
143
|
+
wrappedCommand = `${envExports}${command}`
|
|
131
144
|
}
|
|
132
145
|
|
|
133
146
|
const result = await ssh.execCommand(wrappedCommand, execOptions)
|
|
@@ -191,7 +204,13 @@ export async function readRemoteFile(ssh, filePath, remoteCwd) {
|
|
|
191
204
|
}
|
|
192
205
|
|
|
193
206
|
/**
|
|
194
|
-
* Download file from remote server via SFTP
|
|
207
|
+
* Download file from remote server via SFTP with progress
|
|
208
|
+
*
|
|
209
|
+
* Note: Currently uses single-stream download (most reliable).
|
|
210
|
+
* Multi-streaming is technically possible with ssh2-sftp-client's fastGet,
|
|
211
|
+
* but it's unreliable on many servers and can cause data corruption.
|
|
212
|
+
* Single-stream ensures data integrity at the cost of potentially slower speeds.
|
|
213
|
+
*
|
|
195
214
|
* @param {NodeSSH} ssh - SSH client instance
|
|
196
215
|
* @param {string} remotePath - Path to file on remote server
|
|
197
216
|
* @param {string} localPath - Local path to save file
|
|
@@ -206,8 +225,26 @@ export async function downloadRemoteFile(ssh, remotePath, localPath, remoteCwd)
|
|
|
206
225
|
|
|
207
226
|
logProcessing(`Downloading ${absoluteRemotePath} to ${localPath}...`)
|
|
208
227
|
|
|
209
|
-
|
|
228
|
+
let transferred = 0
|
|
229
|
+
const startTime = Date.now()
|
|
230
|
+
|
|
231
|
+
// Single-stream download (most reliable for data integrity)
|
|
232
|
+
await ssh.getFile(localPath, absoluteRemotePath, null, {
|
|
233
|
+
step: (totalTransferred, chunk, total) => {
|
|
234
|
+
transferred = totalTransferred
|
|
235
|
+
const percent = total > 0 ? Math.round((transferred / total) * 100) : 0
|
|
236
|
+
const elapsed = (Date.now() - startTime) / 1000
|
|
237
|
+
const speed = elapsed > 0 ? (transferred / elapsed / 1024 / 1024).toFixed(2) : 0
|
|
238
|
+
const sizeMB = (transferred / 1024 / 1024).toFixed(2)
|
|
239
|
+
const totalMB = total > 0 ? (total / 1024 / 1024).toFixed(2) : '?'
|
|
240
|
+
|
|
241
|
+
// Update progress on same line
|
|
242
|
+
process.stdout.write(`\r Progress: ${percent}% (${sizeMB}MB / ${totalMB}MB) - ${speed} MB/s`)
|
|
243
|
+
}
|
|
244
|
+
})
|
|
210
245
|
|
|
246
|
+
// Clear progress line and show completion
|
|
247
|
+
process.stdout.write('\r' + ' '.repeat(80) + '\r')
|
|
211
248
|
logSuccess(`Downloaded ${absoluteRemotePath} to ${localPath}`)
|
|
212
249
|
}
|
|
213
250
|
|