@wyxos/zephyr 0.4.8 → 0.4.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/main.mjs +33 -2
- package/src/utils/notifications.mjs +101 -0
package/package.json
CHANGED
package/src/main.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import {selectDeploymentTarget} from './application/configuration/select-deploym
|
|
|
17
17
|
import {resolvePendingSnapshot} from './application/deploy/resolve-pending-snapshot.mjs'
|
|
18
18
|
import {runDeployment} from './application/deploy/run-deployment.mjs'
|
|
19
19
|
import {SKIP_GIT_HOOKS_WARNING} from './utils/git-hooks.mjs'
|
|
20
|
+
import {notifyWorkflowResult} from './utils/notifications.mjs'
|
|
20
21
|
|
|
21
22
|
const RELEASE_SCRIPT_NAME = 'release'
|
|
22
23
|
const RELEASE_SCRIPT_COMMAND = 'npx @wyxos/zephyr@latest'
|
|
@@ -99,6 +100,7 @@ async function runRemoteTasks(config, options = {}) {
|
|
|
99
100
|
|
|
100
101
|
async function main(optionsOrWorkflowType = null, versionArg = null) {
|
|
101
102
|
const options = normalizeMainOptions(optionsOrWorkflowType, versionArg)
|
|
103
|
+
const rootDir = process.cwd()
|
|
102
104
|
|
|
103
105
|
const executionMode = {
|
|
104
106
|
interactive: !options.nonInteractive,
|
|
@@ -171,6 +173,14 @@ async function main(optionsOrWorkflowType = null, versionArg = null) {
|
|
|
171
173
|
workflow: currentExecutionMode.workflow
|
|
172
174
|
}
|
|
173
175
|
})
|
|
176
|
+
if (!currentExecutionMode.json) {
|
|
177
|
+
await notifyWorkflowResult({
|
|
178
|
+
status: 'success',
|
|
179
|
+
workflow: currentExecutionMode.workflow,
|
|
180
|
+
presetName: currentExecutionMode.presetName,
|
|
181
|
+
rootDir
|
|
182
|
+
})
|
|
183
|
+
}
|
|
174
184
|
return
|
|
175
185
|
}
|
|
176
186
|
|
|
@@ -189,11 +199,17 @@ async function main(optionsOrWorkflowType = null, versionArg = null) {
|
|
|
189
199
|
workflow: currentExecutionMode.workflow
|
|
190
200
|
}
|
|
191
201
|
})
|
|
202
|
+
if (!currentExecutionMode.json) {
|
|
203
|
+
await notifyWorkflowResult({
|
|
204
|
+
status: 'success',
|
|
205
|
+
workflow: currentExecutionMode.workflow,
|
|
206
|
+
presetName: currentExecutionMode.presetName,
|
|
207
|
+
rootDir
|
|
208
|
+
})
|
|
209
|
+
}
|
|
192
210
|
return
|
|
193
211
|
}
|
|
194
212
|
|
|
195
|
-
const rootDir = process.cwd()
|
|
196
|
-
|
|
197
213
|
await bootstrap.ensureGitignoreEntry(rootDir, {
|
|
198
214
|
projectConfigDir: PROJECT_CONFIG_DIR,
|
|
199
215
|
runCommand,
|
|
@@ -256,6 +272,14 @@ async function main(optionsOrWorkflowType = null, versionArg = null) {
|
|
|
256
272
|
workflow: currentExecutionMode.workflow
|
|
257
273
|
}
|
|
258
274
|
})
|
|
275
|
+
if (!currentExecutionMode.json) {
|
|
276
|
+
await notifyWorkflowResult({
|
|
277
|
+
status: 'success',
|
|
278
|
+
workflow: currentExecutionMode.workflow,
|
|
279
|
+
presetName: currentExecutionMode.presetName,
|
|
280
|
+
rootDir
|
|
281
|
+
})
|
|
282
|
+
}
|
|
259
283
|
} catch (error) {
|
|
260
284
|
const errorCode = getErrorCode(error)
|
|
261
285
|
emitEvent?.('run_failed', {
|
|
@@ -272,6 +296,13 @@ async function main(optionsOrWorkflowType = null, versionArg = null) {
|
|
|
272
296
|
if (errorCode === 'ZEPHYR_FAILURE' && error.stack) {
|
|
273
297
|
writeStderrLine(error.stack)
|
|
274
298
|
}
|
|
299
|
+
await notifyWorkflowResult({
|
|
300
|
+
status: 'failure',
|
|
301
|
+
workflow: currentExecutionMode.workflow,
|
|
302
|
+
presetName: currentExecutionMode.presetName,
|
|
303
|
+
rootDir,
|
|
304
|
+
message: error.message
|
|
305
|
+
})
|
|
275
306
|
}
|
|
276
307
|
|
|
277
308
|
throw error
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
|
|
4
|
+
import {commandExists, runCommand as runCommandBase} from './command.mjs'
|
|
5
|
+
|
|
6
|
+
const MAX_NOTIFICATION_MESSAGE_LENGTH = 180
|
|
7
|
+
|
|
8
|
+
function escapeAppleScriptString(value = '') {
|
|
9
|
+
return String(value ?? '')
|
|
10
|
+
.replace(/\\/g, '\\\\')
|
|
11
|
+
.replace(/"/g, '\\"')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function humanizeWorkflow(workflow = 'deploy') {
|
|
15
|
+
if (workflow === 'release-node') {
|
|
16
|
+
return 'Node Release'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (workflow === 'release-packagist') {
|
|
20
|
+
return 'Packagist Release'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return 'Deploy'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function truncateNotificationMessage(message = '') {
|
|
27
|
+
const normalized = String(message ?? '').replace(/\s+/g, ' ').trim()
|
|
28
|
+
|
|
29
|
+
if (normalized.length <= MAX_NOTIFICATION_MESSAGE_LENGTH) {
|
|
30
|
+
return normalized
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return `${normalized.slice(0, MAX_NOTIFICATION_MESSAGE_LENGTH - 1).trimEnd()}…`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function buildNotificationPayload({
|
|
37
|
+
status = 'success',
|
|
38
|
+
workflow = 'deploy',
|
|
39
|
+
presetName = null,
|
|
40
|
+
rootDir = process.cwd(),
|
|
41
|
+
message = ''
|
|
42
|
+
} = {}) {
|
|
43
|
+
const isSuccess = status === 'success'
|
|
44
|
+
const repoName = path.basename(rootDir || process.cwd()) || 'project'
|
|
45
|
+
const title = isSuccess ? '🟢 Zephyr Passed' : '🔴 Zephyr Failed'
|
|
46
|
+
const subtitleParts = [humanizeWorkflow(workflow), repoName]
|
|
47
|
+
|
|
48
|
+
if (presetName) {
|
|
49
|
+
subtitleParts.push(presetName)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
title,
|
|
54
|
+
subtitle: subtitleParts.join(' • '),
|
|
55
|
+
message: isSuccess
|
|
56
|
+
? 'Workflow completed successfully.'
|
|
57
|
+
: truncateNotificationMessage(message || 'Workflow failed.'),
|
|
58
|
+
soundName: isSuccess ? 'Glass' : 'Basso'
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function notifyWorkflowResult({
|
|
63
|
+
status = 'success',
|
|
64
|
+
workflow = 'deploy',
|
|
65
|
+
presetName = null,
|
|
66
|
+
rootDir = process.cwd(),
|
|
67
|
+
message = ''
|
|
68
|
+
} = {}, {
|
|
69
|
+
processRef = process,
|
|
70
|
+
commandExistsImpl = commandExists,
|
|
71
|
+
runCommand = runCommandBase
|
|
72
|
+
} = {}) {
|
|
73
|
+
if (processRef.platform !== 'darwin' || !commandExistsImpl('osascript')) {
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const payload = buildNotificationPayload({
|
|
78
|
+
status,
|
|
79
|
+
workflow,
|
|
80
|
+
presetName,
|
|
81
|
+
rootDir,
|
|
82
|
+
message
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const script = [
|
|
86
|
+
`display notification "${escapeAppleScriptString(payload.message)}"`,
|
|
87
|
+
`with title "${escapeAppleScriptString(payload.title)}"`,
|
|
88
|
+
`subtitle "${escapeAppleScriptString(payload.subtitle)}"`,
|
|
89
|
+
`sound name "${escapeAppleScriptString(payload.soundName)}"`
|
|
90
|
+
].join(' ')
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
await runCommand('osascript', ['-e', script], {
|
|
94
|
+
cwd: rootDir,
|
|
95
|
+
stdio: 'ignore'
|
|
96
|
+
})
|
|
97
|
+
return true
|
|
98
|
+
} catch {
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
}
|