@wyxos/zephyr 0.5.0 → 0.7.4
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 +33 -4
- package/package.json +1 -1
- package/src/application/configuration/preset-selection.mjs +0 -6
- package/src/application/configuration/select-deployment-target.mjs +108 -71
- package/src/application/deploy/build-remote-deployment-plan.mjs +13 -1
- package/src/application/deploy/plan-laravel-deployment-tasks.mjs +23 -2
- package/src/application/deploy/prepare-local-deployment.mjs +38 -4
- package/src/application/deploy/run-deployment.mjs +7 -1
- package/src/application/deploy/run-local-deployment-checks.mjs +70 -11
- package/src/application/release/release-node-package.mjs +68 -12
- package/src/application/release/release-packagist-package.mjs +56 -12
- package/src/cli/options.mjs +36 -9
- package/src/config/preset-options.mjs +89 -0
- package/src/config/project.mjs +45 -10
- package/src/deploy/local-repo.mjs +28 -27
- package/src/deploy/preflight.mjs +57 -3
- package/src/main.mjs +64 -6
- package/src/release/commit-message.mjs +8 -173
- package/src/release/shared.mjs +9 -21
- package/src/release-node.mjs +8 -1
- package/src/release-packagist.mjs +10 -3
- package/src/runtime/app-context.mjs +13 -1
package/README.md
CHANGED
|
@@ -40,12 +40,21 @@ Common workflows:
|
|
|
40
40
|
# Deploy an app using the saved preset or the interactive prompts
|
|
41
41
|
zephyr
|
|
42
42
|
|
|
43
|
-
# Deploy an app
|
|
43
|
+
# Deploy an app with a local npm package version bump
|
|
44
44
|
zephyr minor
|
|
45
45
|
|
|
46
|
+
# Deploy an app while bypassing Zephyr's built-in local checks
|
|
47
|
+
zephyr minor --skip-checks
|
|
48
|
+
|
|
46
49
|
# Deploy a configured app non-interactively
|
|
47
50
|
zephyr --non-interactive --preset wyxos-release --maintenance off
|
|
48
51
|
|
|
52
|
+
# Deploy a configured app non-interactively and auto-commit dirty changes
|
|
53
|
+
zephyr --non-interactive --preset wyxos-release --auto-commit
|
|
54
|
+
|
|
55
|
+
# Deploy without mutating the local package version
|
|
56
|
+
zephyr --non-interactive --preset wyxos-release --skip-versioning
|
|
57
|
+
|
|
49
58
|
# Resume a pending non-interactive deployment
|
|
50
59
|
zephyr --non-interactive --preset wyxos-release --resume-pending --maintenance off
|
|
51
60
|
|
|
@@ -60,6 +69,10 @@ zephyr --type node minor
|
|
|
60
69
|
|
|
61
70
|
# Release a Packagist package
|
|
62
71
|
zephyr --type packagist patch
|
|
72
|
+
|
|
73
|
+
# Release the current package/composer version without bumping version files
|
|
74
|
+
zephyr --type node --skip-versioning
|
|
75
|
+
zephyr --type packagist --skip-versioning
|
|
63
76
|
```
|
|
64
77
|
|
|
65
78
|
When `--type node` or `--type vue` is used without a bump argument, Zephyr defaults to `patch`.
|
|
@@ -74,7 +87,7 @@ Non-interactive mode is strict and is intended for already-configured projects:
|
|
|
74
87
|
|
|
75
88
|
- `--non-interactive` fails instead of prompting
|
|
76
89
|
- app deployments require `--preset <name>`
|
|
77
|
-
- Laravel app deployments require `--maintenance on|off
|
|
90
|
+
- Laravel app deployments require either a saved preset maintenance preference, `--maintenance on|off`, or a resumable snapshot that already contains the choice
|
|
78
91
|
- pending deployment snapshots require either `--resume-pending` or `--discard-pending`
|
|
79
92
|
- stale remote locks are never auto-removed in non-interactive mode
|
|
80
93
|
- `--json` is only supported together with `--non-interactive`
|
|
@@ -91,7 +104,11 @@ If Zephyr would normally prompt to:
|
|
|
91
104
|
|
|
92
105
|
then non-interactive mode stops immediately with a clear error instead.
|
|
93
106
|
|
|
94
|
-
For Laravel app deployments, `--maintenance on|off` overrides the maintenance prompt when you want an explicit choice
|
|
107
|
+
For Laravel app deployments, `--maintenance on|off` overrides both the saved preset preference and the maintenance prompt when you want an explicit choice for the current run.
|
|
108
|
+
|
|
109
|
+
`--auto-commit` is available for app deployments and tells Zephyr to let local Codex inspect the repo and generate the dirty-tree commit message instead of prompting for one.
|
|
110
|
+
|
|
111
|
+
`--skip-versioning` keeps Zephyr from mutating `package.json` or `composer.json`. On app deploys it skips the local npm version bump step. On package release workflows it releases the version already present in the manifest and creates the release tag from the current `HEAD`.
|
|
95
112
|
|
|
96
113
|
## AI Agents and Automation
|
|
97
114
|
|
|
@@ -155,6 +172,8 @@ npm run release
|
|
|
155
172
|
- `npm run release` is the recommended app/package entrypoint once the release script has been installed.
|
|
156
173
|
- For `--type node` workflows, Zephyr runs your project's `lint` script when present.
|
|
157
174
|
- For `--type node` workflows, Zephyr runs `test:run` or `test` when present.
|
|
175
|
+
- For fresh Laravel app deploys, Zephyr runs local lint/test checks before creating the version-bump commit so failed checks do not force repeated bump retries.
|
|
176
|
+
- For critical app deploys, `--skip-checks` is shorthand for `--skip-lint --skip-tests`. It skips Zephyr's built-in local checks, but any local `pre-push` hook can still run its own checks during git push unless you also opt into `--skip-git-hooks`.
|
|
158
177
|
- For non-interactive app deploys, use a saved preset name instead of relying on prompt fallback.
|
|
159
178
|
|
|
160
179
|
## Features
|
|
@@ -209,7 +228,15 @@ Deployment targets are stored per-project at `.zephyr/config.json`:
|
|
|
209
228
|
{
|
|
210
229
|
"name": "prod-main",
|
|
211
230
|
"appId": "app_def456",
|
|
212
|
-
"branch": "main"
|
|
231
|
+
"branch": "main",
|
|
232
|
+
"options": {
|
|
233
|
+
"maintenanceMode": true,
|
|
234
|
+
"skipGitHooks": false,
|
|
235
|
+
"skipTests": false,
|
|
236
|
+
"skipLint": false,
|
|
237
|
+
"skipVersioning": false,
|
|
238
|
+
"autoCommit": true
|
|
239
|
+
}
|
|
213
240
|
}
|
|
214
241
|
],
|
|
215
242
|
"apps": [
|
|
@@ -226,6 +253,8 @@ Deployment targets are stored per-project at `.zephyr/config.json`:
|
|
|
226
253
|
}
|
|
227
254
|
```
|
|
228
255
|
|
|
256
|
+
Preset `options` capture repeatable deploy behavior so Zephyr can reuse the same maintenance, dirty-tree, and deploy-check preferences on later runs.
|
|
257
|
+
|
|
229
258
|
### Project Directory Structure
|
|
230
259
|
|
|
231
260
|
Zephyr creates a `.zephyr/` directory in your project with:
|
package/package.json
CHANGED
|
@@ -23,12 +23,6 @@ export async function selectPreset({
|
|
|
23
23
|
const branch = preset.branch || app.branch || 'unknown'
|
|
24
24
|
displayName = `${preset.name} (${serverName} → ${app.projectPath} [${branch}])`
|
|
25
25
|
}
|
|
26
|
-
} else if (preset.key) {
|
|
27
|
-
const keyParts = preset.key.split(':')
|
|
28
|
-
const serverName = keyParts[0]
|
|
29
|
-
const projectPath = keyParts[1]
|
|
30
|
-
const branch = preset.branch || (keyParts.length === 3 ? keyParts[2] : 'unknown')
|
|
31
|
-
displayName = `${preset.name} (${serverName} → ${projectPath} [${branch}])`
|
|
32
26
|
}
|
|
33
27
|
|
|
34
28
|
return {
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import {writeStdoutLine} from '../../utils/output.mjs'
|
|
2
2
|
import {loadServers} from '../../config/servers.mjs'
|
|
3
3
|
import {loadProjectConfig, removePreset, saveProjectConfig} from '../../config/project.mjs'
|
|
4
|
+
import {
|
|
5
|
+
buildPresetOptionsFromExecutionMode,
|
|
6
|
+
normalizePresetOptions,
|
|
7
|
+
presetOptionsEqual
|
|
8
|
+
} from '../../config/preset-options.mjs'
|
|
4
9
|
import {ZephyrError} from '../../runtime/errors.mjs'
|
|
5
10
|
|
|
6
11
|
function findPresetByName(projectConfig, presetName) {
|
|
@@ -8,6 +13,56 @@ function findPresetByName(projectConfig, presetName) {
|
|
|
8
13
|
return presets.find((entry) => entry?.name === presetName) ?? null
|
|
9
14
|
}
|
|
10
15
|
|
|
16
|
+
function createPresetState(rootDir, projectConfig, preset, {
|
|
17
|
+
logSuccess
|
|
18
|
+
} = {}) {
|
|
19
|
+
if (!preset) {
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
name: preset.name,
|
|
25
|
+
get options() {
|
|
26
|
+
return normalizePresetOptions(preset.options)
|
|
27
|
+
},
|
|
28
|
+
async saveOptions(nextOptions, {
|
|
29
|
+
message = null
|
|
30
|
+
} = {}) {
|
|
31
|
+
const normalizedOptions = normalizePresetOptions(nextOptions)
|
|
32
|
+
|
|
33
|
+
if (presetOptionsEqual(preset.options, normalizedOptions)) {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
preset.options = normalizedOptions
|
|
38
|
+
await saveProjectConfig(rootDir, projectConfig)
|
|
39
|
+
|
|
40
|
+
if (message) {
|
|
41
|
+
logSuccess?.(message)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return true
|
|
45
|
+
},
|
|
46
|
+
async applyExecutionMode(executionMode = {}) {
|
|
47
|
+
const nextOptions = buildPresetOptionsFromExecutionMode(executionMode, preset.options)
|
|
48
|
+
return await this.saveOptions(nextOptions)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function promptPresetAutoCommit(runPrompt, enabledByDefault = false) {
|
|
54
|
+
const {autoCommitPreference} = await runPrompt([
|
|
55
|
+
{
|
|
56
|
+
type: 'input',
|
|
57
|
+
name: 'autoCommitPreference',
|
|
58
|
+
message: 'Enable auto-commit for dirty changes on this preset? Leave blank for manual commit prompts.',
|
|
59
|
+
default: enabledByDefault ? 'enabled' : ''
|
|
60
|
+
}
|
|
61
|
+
])
|
|
62
|
+
|
|
63
|
+
return typeof autoCommitPreference === 'string' && autoCommitPreference.trim().length > 0
|
|
64
|
+
}
|
|
65
|
+
|
|
11
66
|
function resolvePresetNonInteractive(projectConfig, servers, preset, presetName) {
|
|
12
67
|
if (!preset) {
|
|
13
68
|
throw new ZephyrError(
|
|
@@ -18,8 +73,8 @@ function resolvePresetNonInteractive(projectConfig, servers, preset, presetName)
|
|
|
18
73
|
|
|
19
74
|
if (!preset.appId) {
|
|
20
75
|
throw new ZephyrError(
|
|
21
|
-
`Zephyr cannot run non-interactively because preset "${preset.name || presetName}"
|
|
22
|
-
{code: '
|
|
76
|
+
`Zephyr cannot run non-interactively because preset "${preset.name || presetName}" is invalid.`,
|
|
77
|
+
{code: 'ZEPHYR_PRESET_INVALID'}
|
|
23
78
|
)
|
|
24
79
|
}
|
|
25
80
|
|
|
@@ -72,6 +127,7 @@ export async function selectDeploymentTarget(rootDir, {
|
|
|
72
127
|
|
|
73
128
|
let server = null
|
|
74
129
|
let appConfig = null
|
|
130
|
+
let activePreset = null
|
|
75
131
|
let isCreatingNewPreset = false
|
|
76
132
|
|
|
77
133
|
const preset = nonInteractive
|
|
@@ -95,11 +151,11 @@ export async function selectDeploymentTarget(rootDir, {
|
|
|
95
151
|
}
|
|
96
152
|
|
|
97
153
|
if (nonInteractive) {
|
|
154
|
+
activePreset = preset
|
|
98
155
|
const resolved = resolvePresetNonInteractive(projectConfig, servers, preset, executionMode.presetName)
|
|
99
156
|
server = resolved.server
|
|
100
|
-
appConfig = resolved.appConfig
|
|
101
157
|
appConfig = {
|
|
102
|
-
...appConfig,
|
|
158
|
+
...resolved.appConfig,
|
|
103
159
|
branch: resolved.branch
|
|
104
160
|
}
|
|
105
161
|
} else if (preset === 'create') {
|
|
@@ -107,64 +163,29 @@ export async function selectDeploymentTarget(rootDir, {
|
|
|
107
163
|
server = await configurationService.selectServer(servers)
|
|
108
164
|
appConfig = await configurationService.selectApp(projectConfig, server, rootDir)
|
|
109
165
|
} else if (preset) {
|
|
110
|
-
|
|
111
|
-
|
|
166
|
+
activePreset = preset
|
|
167
|
+
appConfig = projectConfig.apps?.find((app) => app.id === preset.appId)
|
|
112
168
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (!server) {
|
|
122
|
-
logWarning?.('Preset references a server that no longer exists. Creating a new configuration instead.')
|
|
123
|
-
await removeInvalidPreset()
|
|
124
|
-
server = await configurationService.selectServer(servers)
|
|
125
|
-
appConfig = await configurationService.selectApp(projectConfig, server, rootDir)
|
|
126
|
-
} else if (preset.branch && appConfig.branch !== preset.branch) {
|
|
127
|
-
appConfig.branch = preset.branch
|
|
128
|
-
await saveProjectConfig(rootDir, projectConfig)
|
|
129
|
-
logSuccess?.(`Updated branch to ${preset.branch} from preset.`)
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
} else if (preset.key) {
|
|
133
|
-
const keyParts = preset.key.split(':')
|
|
134
|
-
const serverName = keyParts[0]
|
|
135
|
-
const projectPath = keyParts[1]
|
|
136
|
-
const presetBranch = preset.branch || (keyParts.length === 3 ? keyParts[2] : null)
|
|
137
|
-
|
|
138
|
-
server = servers.find((entry) => entry.serverName === serverName)
|
|
169
|
+
if (!appConfig) {
|
|
170
|
+
logWarning?.('Preset references an application that no longer exists. Creating a new configuration instead.')
|
|
171
|
+
await removeInvalidPreset()
|
|
172
|
+
activePreset = null
|
|
173
|
+
server = await configurationService.selectServer(servers)
|
|
174
|
+
appConfig = await configurationService.selectApp(projectConfig, server, rootDir)
|
|
175
|
+
} else {
|
|
176
|
+
server = servers.find((entry) => entry.id === appConfig.serverId || entry.serverName === appConfig.serverName)
|
|
139
177
|
|
|
140
178
|
if (!server) {
|
|
141
|
-
logWarning?.(
|
|
179
|
+
logWarning?.('Preset references a server that no longer exists. Creating a new configuration instead.')
|
|
142
180
|
await removeInvalidPreset()
|
|
181
|
+
activePreset = null
|
|
143
182
|
server = await configurationService.selectServer(servers)
|
|
144
183
|
appConfig = await configurationService.selectApp(projectConfig, server, rootDir)
|
|
145
|
-
} else {
|
|
146
|
-
appConfig =
|
|
147
|
-
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
if (!appConfig) {
|
|
151
|
-
logWarning?.('Preset references an application that no longer exists. Creating a new configuration instead.')
|
|
152
|
-
await removeInvalidPreset()
|
|
153
|
-
appConfig = await configurationService.selectApp(projectConfig, server, rootDir)
|
|
154
|
-
} else {
|
|
155
|
-
preset.appId = appConfig.id
|
|
156
|
-
if (presetBranch && appConfig.branch !== presetBranch) {
|
|
157
|
-
appConfig.branch = presetBranch
|
|
158
|
-
}
|
|
159
|
-
preset.branch = appConfig.branch
|
|
160
|
-
await saveProjectConfig(rootDir, projectConfig)
|
|
161
|
-
}
|
|
184
|
+
} else if (preset.branch && appConfig.branch !== preset.branch) {
|
|
185
|
+
appConfig.branch = preset.branch
|
|
186
|
+
await saveProjectConfig(rootDir, projectConfig)
|
|
187
|
+
logSuccess?.(`Updated branch to ${preset.branch} from preset.`)
|
|
162
188
|
}
|
|
163
|
-
} else {
|
|
164
|
-
logWarning?.('Preset format is invalid. Creating a new configuration instead.')
|
|
165
|
-
await removeInvalidPreset()
|
|
166
|
-
server = await configurationService.selectServer(servers)
|
|
167
|
-
appConfig = await configurationService.selectApp(projectConfig, server, rootDir)
|
|
168
189
|
}
|
|
169
190
|
} else {
|
|
170
191
|
server = await configurationService.selectServer(servers)
|
|
@@ -173,7 +194,7 @@ export async function selectDeploymentTarget(rootDir, {
|
|
|
173
194
|
|
|
174
195
|
if (nonInteractive && (!appConfig?.sshUser || !appConfig?.sshKey)) {
|
|
175
196
|
throw new ZephyrError(
|
|
176
|
-
`Zephyr cannot run non-interactively because preset "${
|
|
197
|
+
`Zephyr cannot run non-interactively because preset "${activePreset?.name || executionMode.presetName}" is missing SSH details.`,
|
|
177
198
|
{code: 'ZEPHYR_SSH_DETAILS_REQUIRED'}
|
|
178
199
|
)
|
|
179
200
|
}
|
|
@@ -207,7 +228,7 @@ export async function selectDeploymentTarget(rootDir, {
|
|
|
207
228
|
writeStdoutLine(JSON.stringify(deploymentConfig, null, 2))
|
|
208
229
|
}
|
|
209
230
|
|
|
210
|
-
if (!nonInteractive && (isCreatingNewPreset || !
|
|
231
|
+
if (!nonInteractive && (isCreatingNewPreset || !activePreset)) {
|
|
211
232
|
const {presetName} = await runPrompt([
|
|
212
233
|
{
|
|
213
234
|
type: 'input',
|
|
@@ -220,31 +241,47 @@ export async function selectDeploymentTarget(rootDir, {
|
|
|
220
241
|
const trimmedName = presetName?.trim()
|
|
221
242
|
|
|
222
243
|
if (trimmedName && trimmedName.length > 0) {
|
|
223
|
-
const presets = projectConfig.presets ?? []
|
|
224
244
|
const appId = appConfig.id
|
|
225
245
|
|
|
226
246
|
if (!appId) {
|
|
227
247
|
logWarning?.('Cannot save preset: app configuration missing ID.')
|
|
228
248
|
} else {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
249
|
+
const existingPreset = findPresetByName(projectConfig, trimmedName)
|
|
250
|
+
const autoCommitEnabled = await promptPresetAutoCommit(
|
|
251
|
+
runPrompt,
|
|
252
|
+
executionMode.autoCommit === true || existingPreset?.options?.autoCommit === true
|
|
253
|
+
)
|
|
254
|
+
const nextPreset = existingPreset ?? {
|
|
255
|
+
name: trimmedName,
|
|
256
|
+
appId,
|
|
257
|
+
branch: deploymentConfig.branch,
|
|
258
|
+
options: normalizePresetOptions()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
nextPreset.name = trimmedName
|
|
262
|
+
nextPreset.appId = appId
|
|
263
|
+
nextPreset.branch = deploymentConfig.branch
|
|
264
|
+
nextPreset.options = normalizePresetOptions({
|
|
265
|
+
...buildPresetOptionsFromExecutionMode(executionMode, nextPreset.options),
|
|
266
|
+
autoCommit: autoCommitEnabled
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
if (!existingPreset) {
|
|
270
|
+
projectConfig.presets = [...(projectConfig.presets ?? []), nextPreset]
|
|
240
271
|
}
|
|
241
272
|
|
|
242
|
-
projectConfig.presets = presets
|
|
243
273
|
await saveProjectConfig(rootDir, projectConfig)
|
|
244
274
|
logSuccess?.(`Saved preset "${trimmedName}" to .zephyr/config.json`)
|
|
275
|
+
activePreset = nextPreset
|
|
245
276
|
}
|
|
246
277
|
}
|
|
247
278
|
}
|
|
248
279
|
|
|
249
|
-
return {
|
|
280
|
+
return {
|
|
281
|
+
deploymentConfig,
|
|
282
|
+
projectConfig,
|
|
283
|
+
presetState: createPresetState(rootDir, projectConfig, activePreset, {
|
|
284
|
+
logSuccess
|
|
285
|
+
})
|
|
286
|
+
}
|
|
250
287
|
}
|
|
@@ -235,6 +235,7 @@ async function resolveMaintenanceMode({
|
|
|
235
235
|
snapshot,
|
|
236
236
|
remoteIsLaravel,
|
|
237
237
|
runPrompt,
|
|
238
|
+
persistPresetOptions,
|
|
238
239
|
executionMode = {}
|
|
239
240
|
} = {}) {
|
|
240
241
|
if (!remoteIsLaravel) {
|
|
@@ -269,7 +270,14 @@ async function resolveMaintenanceMode({
|
|
|
269
270
|
}
|
|
270
271
|
])
|
|
271
272
|
|
|
272
|
-
|
|
273
|
+
const maintenanceModeEnabled = Boolean(answers?.enableMaintenanceMode)
|
|
274
|
+
await persistPresetOptions?.({
|
|
275
|
+
maintenanceMode: maintenanceModeEnabled
|
|
276
|
+
}, {
|
|
277
|
+
message: 'Saved maintenance mode preference to the selected preset.'
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
return maintenanceModeEnabled
|
|
273
281
|
}
|
|
274
282
|
|
|
275
283
|
async function resolveMaintenanceModePlan({
|
|
@@ -333,6 +341,7 @@ async function resolveMaintenanceModePlan({
|
|
|
333
341
|
export async function resolveRemoteDeploymentState({
|
|
334
342
|
snapshot,
|
|
335
343
|
executionMode = {},
|
|
344
|
+
persistPresetOptions,
|
|
336
345
|
ssh,
|
|
337
346
|
remoteCwd,
|
|
338
347
|
runPrompt,
|
|
@@ -351,6 +360,7 @@ export async function resolveRemoteDeploymentState({
|
|
|
351
360
|
snapshot,
|
|
352
361
|
remoteIsLaravel,
|
|
353
362
|
runPrompt,
|
|
363
|
+
persistPresetOptions,
|
|
354
364
|
executionMode
|
|
355
365
|
})
|
|
356
366
|
|
|
@@ -365,6 +375,7 @@ export async function buildRemoteDeploymentPlan({
|
|
|
365
375
|
snapshot = null,
|
|
366
376
|
requiredPhpVersion = null,
|
|
367
377
|
executionMode = {},
|
|
378
|
+
persistPresetOptions,
|
|
368
379
|
remoteIsLaravel = null,
|
|
369
380
|
maintenanceModeEnabled = null,
|
|
370
381
|
ssh,
|
|
@@ -384,6 +395,7 @@ export async function buildRemoteDeploymentPlan({
|
|
|
384
395
|
: await resolveRemoteDeploymentState({
|
|
385
396
|
snapshot,
|
|
386
397
|
executionMode,
|
|
398
|
+
persistPresetOptions,
|
|
387
399
|
ssh,
|
|
388
400
|
remoteCwd,
|
|
389
401
|
runPrompt,
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
const FRONTEND_BUILD_EXTENSIONS = [
|
|
2
|
+
'.vue',
|
|
3
|
+
'.css',
|
|
4
|
+
'.scss',
|
|
5
|
+
'.js',
|
|
6
|
+
'.jsx',
|
|
7
|
+
'.mjs',
|
|
8
|
+
'.cjs',
|
|
9
|
+
'.ts',
|
|
10
|
+
'.tsx',
|
|
11
|
+
'.less',
|
|
12
|
+
'.svg',
|
|
13
|
+
'.png',
|
|
14
|
+
'.jpg',
|
|
15
|
+
'.jpeg',
|
|
16
|
+
'.gif',
|
|
17
|
+
'.webp',
|
|
18
|
+
'.avif',
|
|
19
|
+
'.ico'
|
|
20
|
+
]
|
|
21
|
+
|
|
1
22
|
export function planLaravelDeploymentTasks({
|
|
2
23
|
branch,
|
|
3
24
|
isLaravel,
|
|
@@ -39,7 +60,7 @@ export function planLaravelDeploymentTasks({
|
|
|
39
60
|
const hasFrontendChanges =
|
|
40
61
|
isLaravel &&
|
|
41
62
|
safeChangedFiles.some((file) =>
|
|
42
|
-
|
|
63
|
+
FRONTEND_BUILD_EXTENSIONS.some((ext) => file.endsWith(ext))
|
|
43
64
|
)
|
|
44
65
|
|
|
45
66
|
const shouldRunBuild = isLaravel && (hasFrontendChanges || shouldRunNpmInstall)
|
|
@@ -116,4 +137,4 @@ export function planLaravelDeploymentTasks({
|
|
|
116
137
|
}
|
|
117
138
|
|
|
118
139
|
return steps
|
|
119
|
-
}
|
|
140
|
+
}
|
|
@@ -10,6 +10,10 @@ export async function prepareLocalDeployment(config, {
|
|
|
10
10
|
rootDir = process.cwd(),
|
|
11
11
|
versionArg = null,
|
|
12
12
|
skipGitHooks = false,
|
|
13
|
+
skipTests = false,
|
|
14
|
+
skipLint = false,
|
|
15
|
+
skipVersioning = false,
|
|
16
|
+
autoCommit = false,
|
|
13
17
|
runPrompt,
|
|
14
18
|
runCommand,
|
|
15
19
|
runCommandCapture,
|
|
@@ -24,26 +28,51 @@ export async function prepareLocalDeployment(config, {
|
|
|
24
28
|
logProcessing,
|
|
25
29
|
logSuccess,
|
|
26
30
|
logWarning,
|
|
27
|
-
skipGitHooks
|
|
31
|
+
skipGitHooks,
|
|
32
|
+
autoCommit
|
|
28
33
|
})
|
|
29
34
|
|
|
30
35
|
const context = await resolveLocalDeploymentContext(rootDir)
|
|
31
36
|
const checkSupport = await resolveLocalDeploymentCheckSupport({
|
|
32
37
|
rootDir,
|
|
33
38
|
isLaravel: context.isLaravel,
|
|
39
|
+
skipTests,
|
|
40
|
+
skipLint,
|
|
34
41
|
runCommandCapture
|
|
35
42
|
})
|
|
36
43
|
|
|
37
44
|
if (!snapshot && context.isLaravel) {
|
|
38
|
-
await
|
|
39
|
-
|
|
45
|
+
await runLocalDeploymentChecks({
|
|
46
|
+
rootDir,
|
|
47
|
+
isLaravel: context.isLaravel,
|
|
48
|
+
hasHook: context.hasHook,
|
|
40
49
|
skipGitHooks,
|
|
50
|
+
skipTests,
|
|
51
|
+
skipLint,
|
|
52
|
+
forceRunWhenHookPresent: true,
|
|
41
53
|
runCommand,
|
|
54
|
+
runCommandCapture,
|
|
42
55
|
logProcessing,
|
|
43
56
|
logSuccess,
|
|
44
|
-
logWarning
|
|
57
|
+
logWarning,
|
|
58
|
+
lintCommand: checkSupport.lintCommand,
|
|
59
|
+
buildCommand: checkSupport.buildCommand,
|
|
60
|
+
testCommand: checkSupport.testCommand
|
|
45
61
|
})
|
|
46
62
|
|
|
63
|
+
if (skipVersioning) {
|
|
64
|
+
logWarning?.('Skipping deployment version update because --skip-versioning flag was provided.')
|
|
65
|
+
} else {
|
|
66
|
+
await bumpLocalPackageVersion(rootDir, {
|
|
67
|
+
versionArg,
|
|
68
|
+
skipGitHooks,
|
|
69
|
+
runCommand,
|
|
70
|
+
logProcessing,
|
|
71
|
+
logSuccess,
|
|
72
|
+
logWarning
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
47
76
|
await ensureCommittedChangesPushed(config.branch, rootDir, {
|
|
48
77
|
runCommand,
|
|
49
78
|
runCommandCapture,
|
|
@@ -52,6 +81,8 @@ export async function prepareLocalDeployment(config, {
|
|
|
52
81
|
logWarning,
|
|
53
82
|
skipGitHooks
|
|
54
83
|
})
|
|
84
|
+
|
|
85
|
+
return context
|
|
55
86
|
}
|
|
56
87
|
|
|
57
88
|
await runLocalDeploymentChecks({
|
|
@@ -59,12 +90,15 @@ export async function prepareLocalDeployment(config, {
|
|
|
59
90
|
isLaravel: context.isLaravel,
|
|
60
91
|
hasHook: context.hasHook,
|
|
61
92
|
skipGitHooks,
|
|
93
|
+
skipTests,
|
|
94
|
+
skipLint,
|
|
62
95
|
runCommand,
|
|
63
96
|
runCommandCapture,
|
|
64
97
|
logProcessing,
|
|
65
98
|
logSuccess,
|
|
66
99
|
logWarning,
|
|
67
100
|
lintCommand: checkSupport.lintCommand,
|
|
101
|
+
buildCommand: checkSupport.buildCommand,
|
|
68
102
|
testCommand: checkSupport.testCommand
|
|
69
103
|
})
|
|
70
104
|
|
|
@@ -248,7 +248,8 @@ export async function runDeployment(config, options = {}) {
|
|
|
248
248
|
snapshot = null,
|
|
249
249
|
rootDir = process.cwd(),
|
|
250
250
|
versionArg = null,
|
|
251
|
-
context
|
|
251
|
+
context,
|
|
252
|
+
presetState = null
|
|
252
253
|
} = options
|
|
253
254
|
|
|
254
255
|
const {
|
|
@@ -366,6 +367,10 @@ export async function runDeployment(config, options = {}) {
|
|
|
366
367
|
rootDir,
|
|
367
368
|
versionArg,
|
|
368
369
|
skipGitHooks: executionMode?.skipGitHooks === true,
|
|
370
|
+
skipTests: executionMode?.skipTests === true,
|
|
371
|
+
skipLint: executionMode?.skipLint === true,
|
|
372
|
+
skipVersioning: executionMode?.skipVersioning === true,
|
|
373
|
+
autoCommit: executionMode?.autoCommit === true,
|
|
369
374
|
runPrompt,
|
|
370
375
|
runCommand,
|
|
371
376
|
runCommandCapture: context.runCommandCapture,
|
|
@@ -410,6 +415,7 @@ export async function runDeployment(config, options = {}) {
|
|
|
410
415
|
rootDir,
|
|
411
416
|
requiredPhpVersion,
|
|
412
417
|
executionMode,
|
|
418
|
+
persistPresetOptions: presetState?.saveOptions,
|
|
413
419
|
remoteIsLaravel: remoteState?.remoteIsLaravel,
|
|
414
420
|
maintenanceModeEnabled: remoteState?.maintenanceModeEnabled,
|
|
415
421
|
ssh,
|