@git.zone/cli 3.2.1 → 5.0.0
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/.smartconfig.json +0 -1
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/classes.gitzoneconfig.js +1 -1
- package/dist_ts/classes.project.js +1 -1
- package/dist_ts/gitzone.cli.js +1 -1
- package/dist_ts/gitzone.logging.js +1 -1
- package/dist_ts/helpers.changelog.js +1 -1
- package/dist_ts/helpers.climode.js +1 -1
- package/dist_ts/helpers.smartconfig.js +61 -11
- package/dist_ts/helpers.smartconfigmigrations.js +1 -1
- package/dist_ts/helpers.tsdocker.js +1 -1
- package/dist_ts/helpers.workflow.d.ts +0 -2
- package/dist_ts/helpers.workflow.js +23 -12
- package/dist_ts/index.js +1 -1
- package/dist_ts/mod_audit/index.js +1 -1
- package/dist_ts/mod_commit/index.js +1 -1
- package/dist_ts/mod_commit/mod.helpers.d.ts +3 -3
- package/dist_ts/mod_commit/mod.helpers.js +10 -10
- package/dist_ts/mod_commit/mod.plugins.js +1 -1
- package/dist_ts/mod_commit/mod.ui.js +1 -1
- package/dist_ts/mod_config/classes.commitconfig.js +1 -1
- package/dist_ts/mod_config/classes.releaseconfig.js +1 -1
- package/dist_ts/mod_config/index.js +23 -24
- package/dist_ts/mod_config/mod.plugins.js +1 -1
- package/dist_ts/mod_deprecate/index.js +1 -1
- package/dist_ts/mod_deprecate/mod.plugins.js +1 -1
- package/dist_ts/mod_docker/classes.dockerpruner.js +7 -1
- package/dist_ts/mod_docker/index.js +1 -1
- package/dist_ts/mod_docker/mod.plugins.js +1 -1
- package/dist_ts/mod_format/classes.baseformatter.js +1 -1
- package/dist_ts/mod_format/classes.diffreporter.js +1 -1
- package/dist_ts/mod_format/classes.formatcontext.js +1 -1
- package/dist_ts/mod_format/classes.formatplanner.js +1 -1
- package/dist_ts/mod_format/classes.formatstats.js +1 -1
- package/dist_ts/mod_format/formatters/assets.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/cleanup.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/copy.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/gitignore.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/license.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/packagejson.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/prettier.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/readme.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/smartconfig.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/templates.formatter.js +1 -1
- package/dist_ts/mod_format/formatters/tsconfig.formatter.js +1 -1
- package/dist_ts/mod_format/index.js +1 -1
- package/dist_ts/mod_format/interfaces.format.js +1 -1
- package/dist_ts/mod_format/mod.plugins.js +1 -1
- package/dist_ts/mod_helpers/index.js +1 -1
- package/dist_ts/mod_helpers/mod.plugins.js +1 -1
- package/dist_ts/mod_meta/index.js +1 -1
- package/dist_ts/mod_meta/meta.classes.meta.js +1 -1
- package/dist_ts/mod_meta/meta.interfaces.js +1 -1
- package/dist_ts/mod_meta/meta.plugins.js +1 -1
- package/dist_ts/mod_open/index.js +1 -1
- package/dist_ts/mod_open/mod.plugins.js +1 -1
- package/dist_ts/mod_release/helpers.releasebranch.d.ts +46 -0
- package/dist_ts/mod_release/helpers.releasebranch.js +579 -0
- package/dist_ts/mod_release/index.d.ts +10 -0
- package/dist_ts/mod_release/index.js +300 -92
- package/dist_ts/mod_release/mod.plugins.js +1 -1
- package/dist_ts/mod_services/classes.dockercontainer.d.ts +50 -3
- package/dist_ts/mod_services/classes.dockercontainer.js +646 -84
- package/dist_ts/mod_services/classes.globalregistry.d.ts +10 -24
- package/dist_ts/mod_services/classes.globalregistry.js +210 -62
- package/dist_ts/mod_services/classes.globalregistrystore.d.ts +48 -0
- package/dist_ts/mod_services/classes.globalregistrystore.js +156 -0
- package/dist_ts/mod_services/classes.interprocesslock.d.ts +21 -0
- package/dist_ts/mod_services/classes.interprocesslock.js +141 -0
- package/dist_ts/mod_services/classes.serviceconfiguration.d.ts +15 -12
- package/dist_ts/mod_services/classes.serviceconfiguration.js +188 -91
- package/dist_ts/mod_services/classes.servicedatamarker.d.ts +32 -7
- package/dist_ts/mod_services/classes.servicedatamarker.js +145 -49
- package/dist_ts/mod_services/classes.servicemanager.d.ts +70 -28
- package/dist_ts/mod_services/classes.servicemanager.js +995 -421
- package/dist_ts/mod_services/classes.servicenames.d.ts +8 -0
- package/dist_ts/mod_services/classes.servicenames.js +54 -0
- package/dist_ts/mod_services/classes.serviceoptions.js +1 -1
- package/dist_ts/mod_services/classes.servicepruner.d.ts +3 -0
- package/dist_ts/mod_services/classes.servicepruner.js +208 -46
- package/dist_ts/mod_services/helpers.js +1 -1
- package/dist_ts/mod_services/index.d.ts +2 -0
- package/dist_ts/mod_services/index.js +81 -58
- package/dist_ts/mod_services/mod.plugins.js +1 -1
- package/dist_ts/mod_standard/index.js +2 -2
- package/dist_ts/mod_standard/mod.plugins.js +1 -1
- package/dist_ts/mod_start/index.js +1 -1
- package/dist_ts/mod_start/mod.plugins.js +1 -1
- package/dist_ts/mod_template/index.js +1 -1
- package/dist_ts/mod_template/mod.plugins.js +1 -1
- package/dist_ts/mod_tools/classes.packagemanager.js +1 -1
- package/dist_ts/mod_tools/index.js +1 -1
- package/dist_ts/mod_tools/mod.plugins.js +1 -1
- package/dist_ts/paths.js +1 -1
- package/dist_ts/plugins.d.ts +3 -1
- package/dist_ts/plugins.js +4 -2
- package/dist_ts_migration/001.service-selection.d.ts +9 -0
- package/dist_ts_migration/001.service-selection.js +51 -0
- package/dist_ts_migration/002.runtime-config.d.ts +19 -0
- package/dist_ts_migration/002.runtime-config.js +170 -0
- package/dist_ts_migration/003.service-data-marker.d.ts +9 -0
- package/dist_ts_migration/003.service-data-marker.js +156 -0
- package/dist_ts_migration/004.global-registry.d.ts +13 -0
- package/dist_ts_migration/004.global-registry.js +165 -0
- package/dist_ts_migration/index.d.ts +11 -0
- package/dist_ts_migration/index.js +59 -0
- package/package.json +6 -3
- package/readme.hints.md +102 -25
- package/readme.md +144 -41
- package/readme.plan.md +29 -16
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/helpers.smartconfig.ts +63 -10
- package/ts/helpers.workflow.ts +41 -12
- package/ts/mod_commit/mod.helpers.ts +12 -8
- package/ts/mod_config/index.ts +22 -23
- package/ts/mod_docker/classes.dockerpruner.ts +6 -0
- package/ts/mod_release/helpers.releasebranch.ts +1282 -0
- package/ts/mod_release/index.ts +608 -129
- package/ts/mod_services/classes.dockercontainer.ts +912 -96
- package/ts/mod_services/classes.globalregistry.ts +290 -89
- package/ts/mod_services/classes.globalregistrystore.ts +279 -0
- package/ts/mod_services/classes.interprocesslock.ts +189 -0
- package/ts/mod_services/classes.serviceconfiguration.ts +215 -108
- package/ts/mod_services/classes.servicedatamarker.ts +210 -52
- package/ts/mod_services/classes.servicemanager.ts +1332 -512
- package/ts/mod_services/classes.servicenames.ts +76 -0
- package/ts/mod_services/classes.servicepruner.ts +310 -52
- package/ts/mod_services/index.ts +89 -62
- package/ts/mod_standard/index.ts +1 -1
- package/ts/plugins.ts +4 -0
- package/ts_migration/001.service-selection.ts +77 -0
- package/ts_migration/002.runtime-config.ts +218 -0
- package/ts_migration/003.service-data-marker.ts +216 -0
- package/ts_migration/004.global-registry.ts +272 -0
- package/ts_migration/index.ts +83 -0
package/ts/mod_release/index.ts
CHANGED
|
@@ -18,6 +18,16 @@ import {
|
|
|
18
18
|
} from "../helpers.workflow.js";
|
|
19
19
|
import * as commitHelpers from "../mod_commit/mod.helpers.js";
|
|
20
20
|
import { hasInstalledProjectTsdockerBinary } from "../helpers.tsdocker.js";
|
|
21
|
+
import {
|
|
22
|
+
inspectReleaseBranch,
|
|
23
|
+
integrateReleaseBranch,
|
|
24
|
+
prepareReleaseIntegration,
|
|
25
|
+
revalidatePreparedReleaseBranch,
|
|
26
|
+
revalidateReleasePublicationState,
|
|
27
|
+
releaseGitEnv,
|
|
28
|
+
resolveReleaseMergeFlag,
|
|
29
|
+
type IReleaseBranchContext,
|
|
30
|
+
} from "./helpers.releasebranch.js";
|
|
21
31
|
|
|
22
32
|
type TTargetStatus = "success" | "already-published" | "skipped" | "failed";
|
|
23
33
|
|
|
@@ -39,24 +49,49 @@ export const run = async (argvArg: any) => {
|
|
|
39
49
|
if (mode.json) {
|
|
40
50
|
printJson({
|
|
41
51
|
ok: false,
|
|
42
|
-
error:
|
|
52
|
+
error:
|
|
53
|
+
"JSON output is not supported for mutating release workflows yet. Use `gitzone release --plan` for a human-readable plan.",
|
|
43
54
|
});
|
|
44
55
|
return;
|
|
45
56
|
}
|
|
46
57
|
|
|
47
|
-
const workflow = await resolveReleaseWorkflow(argvArg);
|
|
48
|
-
printReleasePlan(workflow);
|
|
49
|
-
if (workflow.confirmation === "plan") {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
58
|
const smartshellInstance = new plugins.smartshell.Smartshell({
|
|
54
59
|
executor: "bash",
|
|
55
60
|
sourceFilePaths: [],
|
|
56
61
|
});
|
|
62
|
+
let mergeRequested: boolean;
|
|
63
|
+
try {
|
|
64
|
+
mergeRequested = resolveReleaseMergeFlag(argvArg);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
logger.log("error", error instanceof Error ? error.message : String(error));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const workflow = await resolveReleaseWorkflow(argvArg);
|
|
71
|
+
let branchContext: IReleaseBranchContext;
|
|
72
|
+
try {
|
|
73
|
+
branchContext = await inspectReleaseBranch({
|
|
74
|
+
smartshell: smartshellInstance,
|
|
75
|
+
cwd: paths.cwd,
|
|
76
|
+
gitRemote: workflow.gitRemote,
|
|
77
|
+
gitTargetActive: workflow.targets.includes("git"),
|
|
78
|
+
pushBranch: workflow.pushBranch,
|
|
79
|
+
pushTags: workflow.pushTags,
|
|
80
|
+
mergeRequested,
|
|
81
|
+
planMode: workflow.confirmation === "plan",
|
|
82
|
+
});
|
|
83
|
+
} catch (error) {
|
|
84
|
+
logger.log("error", error instanceof Error ? error.message : String(error));
|
|
85
|
+
process.exit(1);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
printReleasePlan(workflow, branchContext);
|
|
89
|
+
if (workflow.confirmation === "plan") {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
57
92
|
|
|
58
93
|
const pending = await readPendingChangelog(
|
|
59
|
-
plugins.path.join(
|
|
94
|
+
plugins.path.join(branchContext.sourceCwd, workflow.changelogFile),
|
|
60
95
|
workflow.changelogPendingSection,
|
|
61
96
|
);
|
|
62
97
|
if (pending.isEmpty && !argvArg["allow-empty"] && !argvArg.allowEmpty) {
|
|
@@ -65,18 +100,29 @@ export const run = async (argvArg: any) => {
|
|
|
65
100
|
}
|
|
66
101
|
|
|
67
102
|
const versionType = resolveVersionType(argvArg, pending.block);
|
|
68
|
-
const projectType = await commitHelpers.detectProjectType(
|
|
69
|
-
|
|
70
|
-
|
|
103
|
+
const projectType = await commitHelpers.detectProjectType(
|
|
104
|
+
branchContext.sourceCwd,
|
|
105
|
+
);
|
|
106
|
+
const currentVersion = await commitHelpers.readCurrentVersion(
|
|
107
|
+
projectType,
|
|
108
|
+
branchContext.sourceCwd,
|
|
109
|
+
);
|
|
110
|
+
const plannedVersion = commitHelpers.calculateNewVersion(
|
|
111
|
+
currentVersion,
|
|
112
|
+
versionType,
|
|
113
|
+
);
|
|
71
114
|
|
|
72
115
|
if (workflow.confirmation === "prompt") {
|
|
73
116
|
if (!mode.interactive) {
|
|
74
|
-
throw new Error(
|
|
117
|
+
throw new Error(
|
|
118
|
+
"Release confirmation requires an interactive terminal. Use `-y` or set release.confirmation to `auto`.",
|
|
119
|
+
);
|
|
75
120
|
}
|
|
76
|
-
const confirmed =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
121
|
+
const confirmed =
|
|
122
|
+
await plugins.smartinteract.SmartInteract.getCliConfirmation(
|
|
123
|
+
`Release v${plannedVersion} (${versionType}) now?`,
|
|
124
|
+
true,
|
|
125
|
+
);
|
|
80
126
|
if (!confirmed) {
|
|
81
127
|
logger.log("info", "Release cancelled.");
|
|
82
128
|
return;
|
|
@@ -88,40 +134,171 @@ export const run = async (argvArg: any) => {
|
|
|
88
134
|
const npmResults: ITargetResult[] = [];
|
|
89
135
|
const dockerResults: ITargetResult[] = [];
|
|
90
136
|
|
|
91
|
-
|
|
92
|
-
await
|
|
137
|
+
try {
|
|
138
|
+
branchContext = await prepareReleaseIntegration(
|
|
139
|
+
smartshellInstance,
|
|
140
|
+
branchContext,
|
|
141
|
+
);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
logger.log("error", error instanceof Error ? error.message : String(error));
|
|
144
|
+
process.exit(1);
|
|
145
|
+
return;
|
|
93
146
|
}
|
|
94
147
|
if (workflow.runTests) {
|
|
95
|
-
await runCommandStep(
|
|
148
|
+
await runCommandStep(
|
|
149
|
+
smartshellInstance,
|
|
150
|
+
branchContext.sourceCwd,
|
|
151
|
+
"Running tests",
|
|
152
|
+
workflow.testCommand,
|
|
153
|
+
);
|
|
96
154
|
}
|
|
97
155
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
156
|
+
let releaseCwd = branchContext.sourceCwd;
|
|
157
|
+
let releasePushUrl = branchContext.pushUrl;
|
|
158
|
+
let expectedRemoteMainOid = branchContext.remoteMainOid;
|
|
159
|
+
if (branchContext.sourceBranch === "main") {
|
|
160
|
+
try {
|
|
161
|
+
await revalidatePreparedReleaseBranch(smartshellInstance, branchContext);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
logger.log(
|
|
164
|
+
"error",
|
|
165
|
+
error instanceof Error ? error.message : String(error),
|
|
166
|
+
);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
try {
|
|
172
|
+
const integration = await integrateReleaseBranch(
|
|
173
|
+
smartshellInstance,
|
|
174
|
+
branchContext,
|
|
175
|
+
);
|
|
176
|
+
releaseCwd = integration.releaseCwd;
|
|
177
|
+
releasePushUrl = integration.pushUrl;
|
|
178
|
+
expectedRemoteMainOid = integration.expectedRemoteMainOid;
|
|
179
|
+
} catch (error) {
|
|
180
|
+
logger.log(
|
|
181
|
+
"error",
|
|
182
|
+
error instanceof Error ? error.message : String(error),
|
|
183
|
+
);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
newVersion = await runVersionStep(projectType, versionType, releaseCwd);
|
|
190
|
+
await runChangelogStep(workflow, newVersion, releaseCwd);
|
|
191
|
+
const releaseCommitOid = await runReleaseCommitStep(
|
|
192
|
+
smartshellInstance,
|
|
193
|
+
newVersion,
|
|
194
|
+
releaseCwd,
|
|
195
|
+
);
|
|
196
|
+
await runTagStep(
|
|
197
|
+
smartshellInstance,
|
|
198
|
+
newVersion,
|
|
199
|
+
releaseCommitOid,
|
|
200
|
+
releaseCwd,
|
|
201
|
+
);
|
|
202
|
+
const releaseRefs = await captureReleaseRefs(
|
|
203
|
+
smartshellInstance,
|
|
204
|
+
releaseCwd,
|
|
205
|
+
newVersion,
|
|
206
|
+
releaseCommitOid,
|
|
207
|
+
);
|
|
102
208
|
|
|
103
209
|
if (workflow.runBuild) {
|
|
104
|
-
await runCommandStep(
|
|
105
|
-
|
|
210
|
+
await runCommandStep(
|
|
211
|
+
smartshellInstance,
|
|
212
|
+
releaseCwd,
|
|
213
|
+
"Running release build",
|
|
214
|
+
workflow.buildCommand,
|
|
215
|
+
);
|
|
216
|
+
await verifyCleanTree(
|
|
217
|
+
smartshellInstance,
|
|
218
|
+
releaseCwd,
|
|
219
|
+
"Build produced uncommitted changes. Aborting release.",
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const ensurePublicationState = async (): Promise<boolean> => {
|
|
224
|
+
try {
|
|
225
|
+
await verifyReleaseRefs(
|
|
226
|
+
smartshellInstance,
|
|
227
|
+
releaseCwd,
|
|
228
|
+
newVersion,
|
|
229
|
+
releaseRefs,
|
|
230
|
+
);
|
|
231
|
+
await revalidateReleasePublicationState(
|
|
232
|
+
smartshellInstance,
|
|
233
|
+
branchContext,
|
|
234
|
+
releaseRefs.mainOid,
|
|
235
|
+
expectedRemoteMainOid,
|
|
236
|
+
);
|
|
237
|
+
return true;
|
|
238
|
+
} catch (error) {
|
|
239
|
+
logger.log(
|
|
240
|
+
"error",
|
|
241
|
+
error instanceof Error ? error.message : String(error),
|
|
242
|
+
);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
if (!(await ensurePublicationState())) {
|
|
249
|
+
return;
|
|
106
250
|
}
|
|
107
251
|
|
|
108
252
|
if (workflow.targets.includes("git")) {
|
|
109
|
-
gitResults.push(
|
|
253
|
+
gitResults.push(
|
|
254
|
+
...(await runGitTarget(
|
|
255
|
+
smartshellInstance,
|
|
256
|
+
workflow,
|
|
257
|
+
releaseCwd,
|
|
258
|
+
newVersion,
|
|
259
|
+
releaseRefs,
|
|
260
|
+
releasePushUrl,
|
|
261
|
+
expectedRemoteMainOid,
|
|
262
|
+
)),
|
|
263
|
+
);
|
|
264
|
+
if (gitResults.some((result) => result.status === "failed")) {
|
|
265
|
+
printReleaseSummary(newVersion, gitResults, npmResults, dockerResults);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
expectedRemoteMainOid = releaseRefs.mainOid;
|
|
110
270
|
}
|
|
111
271
|
if (workflow.targets.includes("npm")) {
|
|
112
|
-
|
|
272
|
+
if (!(await ensurePublicationState())) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
npmResults.push(
|
|
276
|
+
...(await runNpmTarget(smartshellInstance, workflow, releaseCwd)),
|
|
277
|
+
);
|
|
113
278
|
}
|
|
114
279
|
if (workflow.targets.includes("docker")) {
|
|
115
|
-
|
|
280
|
+
if (!(await ensurePublicationState())) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
dockerResults.push(
|
|
284
|
+
...(await runDockerTarget(smartshellInstance, workflow, releaseCwd)),
|
|
285
|
+
);
|
|
116
286
|
}
|
|
117
287
|
|
|
118
288
|
printReleaseSummary(newVersion, gitResults, npmResults, dockerResults);
|
|
119
|
-
if (
|
|
289
|
+
if (
|
|
290
|
+
[...gitResults, ...npmResults, ...dockerResults].some(
|
|
291
|
+
(result) => result.status === "failed",
|
|
292
|
+
)
|
|
293
|
+
) {
|
|
120
294
|
process.exit(1);
|
|
121
295
|
}
|
|
122
296
|
};
|
|
123
297
|
|
|
124
|
-
function resolveVersionType(
|
|
298
|
+
function resolveVersionType(
|
|
299
|
+
argvArg: any,
|
|
300
|
+
pendingBlock: string,
|
|
301
|
+
): commitHelpers.VersionType {
|
|
125
302
|
if (argvArg.major) return "major";
|
|
126
303
|
if (argvArg.minor) return "minor";
|
|
127
304
|
if (argvArg.patch) return "patch";
|
|
@@ -130,11 +307,12 @@ function resolveVersionType(argvArg: any, pendingBlock: string): commitHelpers.V
|
|
|
130
307
|
|
|
131
308
|
async function runCommandStep(
|
|
132
309
|
smartshellInstance: plugins.smartshell.Smartshell,
|
|
310
|
+
cwdArg: string,
|
|
133
311
|
label: string,
|
|
134
312
|
command: string,
|
|
135
313
|
): Promise<void> {
|
|
136
314
|
console.log(`\n${label}`);
|
|
137
|
-
const result = await smartshellInstance.exec(command);
|
|
315
|
+
const result = await smartshellInstance.exec(command, { cwd: cwdArg });
|
|
138
316
|
if (result.exitCode !== 0) {
|
|
139
317
|
logger.log("error", `${label} failed. Aborting release.`);
|
|
140
318
|
process.exit(1);
|
|
@@ -144,9 +322,14 @@ async function runCommandStep(
|
|
|
144
322
|
|
|
145
323
|
async function verifyCleanTree(
|
|
146
324
|
smartshellInstance: plugins.smartshell.Smartshell,
|
|
325
|
+
cwdArg: string,
|
|
147
326
|
errorMessage: string,
|
|
148
327
|
): Promise<void> {
|
|
149
|
-
const statusResult = await smartshellInstance.
|
|
328
|
+
const statusResult = await smartshellInstance.execSpawn(
|
|
329
|
+
"git",
|
|
330
|
+
["status", "--porcelain"],
|
|
331
|
+
{ cwd: cwdArg, env: releaseGitEnv },
|
|
332
|
+
);
|
|
150
333
|
if (statusResult.stdout.trim() !== "") {
|
|
151
334
|
logger.log("error", errorMessage);
|
|
152
335
|
console.log(statusResult.stdout);
|
|
@@ -157,24 +340,36 @@ async function verifyCleanTree(
|
|
|
157
340
|
async function runVersionStep(
|
|
158
341
|
projectType: commitHelpers.ProjectType,
|
|
159
342
|
versionType: commitHelpers.VersionType,
|
|
343
|
+
cwdArg: string,
|
|
160
344
|
): Promise<string> {
|
|
161
|
-
const currentVersion = await commitHelpers.readCurrentVersion(
|
|
162
|
-
|
|
345
|
+
const currentVersion = await commitHelpers.readCurrentVersion(
|
|
346
|
+
projectType,
|
|
347
|
+
cwdArg,
|
|
348
|
+
);
|
|
349
|
+
const newVersion = commitHelpers.calculateNewVersion(
|
|
350
|
+
currentVersion,
|
|
351
|
+
versionType,
|
|
352
|
+
);
|
|
163
353
|
logger.log("info", `Bumping version: ${currentVersion} -> ${newVersion}`);
|
|
164
354
|
|
|
165
|
-
const commitInfo = new plugins.commitinfo.CommitInfo(
|
|
355
|
+
const commitInfo = new plugins.commitinfo.CommitInfo(cwdArg, versionType);
|
|
166
356
|
await commitInfo.writeIntoPotentialDirs();
|
|
167
|
-
await commitHelpers.updateProjectVersionFiles(
|
|
357
|
+
await commitHelpers.updateProjectVersionFiles(
|
|
358
|
+
projectType,
|
|
359
|
+
newVersion,
|
|
360
|
+
cwdArg,
|
|
361
|
+
);
|
|
168
362
|
return newVersion;
|
|
169
363
|
}
|
|
170
364
|
|
|
171
365
|
async function runChangelogStep(
|
|
172
366
|
workflow: IResolvedReleaseWorkflow,
|
|
173
367
|
newVersion: string,
|
|
368
|
+
cwdArg: string,
|
|
174
369
|
): Promise<void> {
|
|
175
370
|
const dateString = new Date().toISOString().slice(0, 10);
|
|
176
371
|
await movePendingToVersion(
|
|
177
|
-
plugins.path.join(
|
|
372
|
+
plugins.path.join(cwdArg, workflow.changelogFile),
|
|
178
373
|
workflow.changelogPendingSection,
|
|
179
374
|
workflow.changelogVersionHeading,
|
|
180
375
|
newVersion,
|
|
@@ -185,80 +380,261 @@ async function runChangelogStep(
|
|
|
185
380
|
async function runReleaseCommitStep(
|
|
186
381
|
smartshellInstance: plugins.smartshell.Smartshell,
|
|
187
382
|
newVersion: string,
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
383
|
+
cwdArg: string,
|
|
384
|
+
): Promise<string> {
|
|
385
|
+
const addResult = await smartshellInstance.execSpawn("git", ["add", "-A"], {
|
|
386
|
+
cwd: cwdArg,
|
|
387
|
+
env: releaseGitEnv,
|
|
388
|
+
});
|
|
389
|
+
if (addResult.exitCode !== 0) {
|
|
390
|
+
throw new Error("Staging release metadata failed.");
|
|
391
|
+
}
|
|
392
|
+
const result = await smartshellInstance.execSpawn(
|
|
393
|
+
"git",
|
|
394
|
+
["commit", "-m", `v${newVersion}`],
|
|
395
|
+
{ cwd: cwdArg, env: releaseGitEnv },
|
|
396
|
+
);
|
|
191
397
|
if (result.exitCode !== 0) {
|
|
192
|
-
|
|
193
|
-
|
|
398
|
+
throw new Error("Release commit failed.");
|
|
399
|
+
}
|
|
400
|
+
const branchResult = await smartshellInstance.execSpawn(
|
|
401
|
+
"git",
|
|
402
|
+
["symbolic-ref", "--quiet", "HEAD"],
|
|
403
|
+
{ cwd: cwdArg, env: releaseGitEnv, silent: true },
|
|
404
|
+
);
|
|
405
|
+
if (
|
|
406
|
+
branchResult.exitCode !== 0 ||
|
|
407
|
+
branchResult.stdout.trim() !== "refs/heads/main"
|
|
408
|
+
) {
|
|
409
|
+
throw new Error("The release commit was not created on main.");
|
|
410
|
+
}
|
|
411
|
+
const headOid = await resolveGitObjectOid(
|
|
412
|
+
smartshellInstance,
|
|
413
|
+
cwdArg,
|
|
414
|
+
"HEAD^{commit}",
|
|
415
|
+
);
|
|
416
|
+
const mainOid = await resolveGitObjectOid(
|
|
417
|
+
smartshellInstance,
|
|
418
|
+
cwdArg,
|
|
419
|
+
"refs/heads/main^{commit}",
|
|
420
|
+
);
|
|
421
|
+
if (headOid !== mainOid) {
|
|
422
|
+
throw new Error("HEAD and main diverged after creating the release commit.");
|
|
194
423
|
}
|
|
424
|
+
return mainOid;
|
|
195
425
|
}
|
|
196
426
|
|
|
197
427
|
async function runTagStep(
|
|
198
428
|
smartshellInstance: plugins.smartshell.Smartshell,
|
|
199
429
|
newVersion: string,
|
|
430
|
+
releaseCommitOidArg: string,
|
|
431
|
+
cwdArg: string,
|
|
200
432
|
): Promise<void> {
|
|
201
|
-
const result = await smartshellInstance.
|
|
433
|
+
const result = await smartshellInstance.execSpawn(
|
|
434
|
+
"git",
|
|
435
|
+
[
|
|
436
|
+
"tag",
|
|
437
|
+
"-a",
|
|
438
|
+
`v${newVersion}`,
|
|
439
|
+
"-m",
|
|
440
|
+
`v${newVersion}`,
|
|
441
|
+
releaseCommitOidArg,
|
|
442
|
+
],
|
|
443
|
+
{ cwd: cwdArg, env: releaseGitEnv },
|
|
444
|
+
);
|
|
202
445
|
if (result.exitCode !== 0) {
|
|
203
|
-
|
|
204
|
-
process.exit(1);
|
|
446
|
+
throw new Error("Release tag failed.");
|
|
205
447
|
}
|
|
206
448
|
}
|
|
207
449
|
|
|
450
|
+
interface IReleaseRefs {
|
|
451
|
+
mainOid: string;
|
|
452
|
+
tagOid: string;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const resolveGitObjectOid = async (
|
|
456
|
+
smartshellInstanceArg: plugins.smartshell.Smartshell,
|
|
457
|
+
cwdArg: string,
|
|
458
|
+
revisionArg: string,
|
|
459
|
+
): Promise<string> => {
|
|
460
|
+
const result = await smartshellInstanceArg.execSpawn(
|
|
461
|
+
"git",
|
|
462
|
+
["rev-parse", "--verify", revisionArg],
|
|
463
|
+
{ cwd: cwdArg, env: releaseGitEnv, silent: true },
|
|
464
|
+
);
|
|
465
|
+
const oid = result.stdout.trim();
|
|
466
|
+
if (result.exitCode !== 0 || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(oid)) {
|
|
467
|
+
throw new Error(`Unable to resolve release Git object ${revisionArg}.`);
|
|
468
|
+
}
|
|
469
|
+
return oid;
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
const captureReleaseRefs = async (
|
|
473
|
+
smartshellInstanceArg: plugins.smartshell.Smartshell,
|
|
474
|
+
cwdArg: string,
|
|
475
|
+
newVersionArg: string,
|
|
476
|
+
expectedMainOidArg?: string,
|
|
477
|
+
): Promise<IReleaseRefs> => {
|
|
478
|
+
const mainOid = await resolveGitObjectOid(
|
|
479
|
+
smartshellInstanceArg,
|
|
480
|
+
cwdArg,
|
|
481
|
+
"refs/heads/main^{commit}",
|
|
482
|
+
);
|
|
483
|
+
const tagOid = await resolveGitObjectOid(
|
|
484
|
+
smartshellInstanceArg,
|
|
485
|
+
cwdArg,
|
|
486
|
+
`refs/tags/v${newVersionArg}^{tag}`,
|
|
487
|
+
);
|
|
488
|
+
const tagCommitOid = await resolveGitObjectOid(
|
|
489
|
+
smartshellInstanceArg,
|
|
490
|
+
cwdArg,
|
|
491
|
+
`refs/tags/v${newVersionArg}^{commit}`,
|
|
492
|
+
);
|
|
493
|
+
if (
|
|
494
|
+
(expectedMainOidArg && mainOid !== expectedMainOidArg) ||
|
|
495
|
+
tagCommitOid !== mainOid
|
|
496
|
+
) {
|
|
497
|
+
throw new Error(
|
|
498
|
+
"The release tag is not bound to the captured main release commit.",
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
return { mainOid, tagOid };
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
const verifyReleaseRefs = async (
|
|
505
|
+
smartshellInstanceArg: plugins.smartshell.Smartshell,
|
|
506
|
+
cwdArg: string,
|
|
507
|
+
newVersionArg: string,
|
|
508
|
+
expectedRefsArg: IReleaseRefs,
|
|
509
|
+
): Promise<void> => {
|
|
510
|
+
const currentRefs = await captureReleaseRefs(
|
|
511
|
+
smartshellInstanceArg,
|
|
512
|
+
cwdArg,
|
|
513
|
+
newVersionArg,
|
|
514
|
+
expectedRefsArg.mainOid,
|
|
515
|
+
);
|
|
516
|
+
if (
|
|
517
|
+
currentRefs.mainOid !== expectedRefsArg.mainOid ||
|
|
518
|
+
currentRefs.tagOid !== expectedRefsArg.tagOid
|
|
519
|
+
) {
|
|
520
|
+
throw new Error(
|
|
521
|
+
"Local release refs changed before publication. Inspect main and the release tag before retrying.",
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
|
|
208
526
|
async function runGitTarget(
|
|
209
527
|
smartshellInstance: plugins.smartshell.Smartshell,
|
|
210
528
|
workflow: IResolvedReleaseWorkflow,
|
|
529
|
+
cwdArg: string,
|
|
530
|
+
newVersionArg: string,
|
|
531
|
+
releaseRefsArg: IReleaseRefs,
|
|
532
|
+
pushUrlArg?: string,
|
|
533
|
+
expectedRemoteMainOidArg?: string,
|
|
211
534
|
): Promise<ITargetResult[]> {
|
|
212
|
-
const
|
|
213
|
-
const
|
|
214
|
-
const
|
|
535
|
+
const destination = pushUrlArg || workflow.gitRemote;
|
|
536
|
+
const targets: string[] = [];
|
|
537
|
+
const refspecs: string[] = [];
|
|
215
538
|
if (workflow.pushBranch) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
command: `git push ${workflow.gitRemote} ${currentBranch}`,
|
|
219
|
-
});
|
|
539
|
+
targets.push(`${workflow.gitRemote}/main`);
|
|
540
|
+
refspecs.push("refs/heads/main:refs/heads/main");
|
|
220
541
|
}
|
|
221
542
|
if (workflow.pushTags) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
543
|
+
targets.push(`${workflow.gitRemote}/v${newVersionArg}`);
|
|
544
|
+
refspecs.push(`refs/tags/v${newVersionArg}:refs/tags/v${newVersionArg}`);
|
|
545
|
+
}
|
|
546
|
+
if (refspecs.length === 0) {
|
|
547
|
+
return [];
|
|
226
548
|
}
|
|
227
549
|
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
550
|
+
const args = buildReleaseGitPushArgs({
|
|
551
|
+
destination,
|
|
552
|
+
newVersion: newVersionArg,
|
|
553
|
+
pushBranch: workflow.pushBranch,
|
|
554
|
+
pushTags: workflow.pushTags,
|
|
555
|
+
mainOid: releaseRefsArg.mainOid,
|
|
556
|
+
tagOid: releaseRefsArg.tagOid,
|
|
557
|
+
expectedRemoteMainOid: expectedRemoteMainOidArg,
|
|
558
|
+
});
|
|
559
|
+
const result = await smartshellInstance.execSpawn("git", args, {
|
|
560
|
+
cwd: cwdArg,
|
|
561
|
+
env: releaseGitEnv,
|
|
562
|
+
});
|
|
563
|
+
return targets.map((target) => ({
|
|
564
|
+
target,
|
|
565
|
+
status: result.exitCode === 0 ? "success" : "failed",
|
|
566
|
+
message:
|
|
567
|
+
result.exitCode === 0
|
|
568
|
+
? undefined
|
|
569
|
+
: firstMeaningfulLine(result.combinedOutput),
|
|
570
|
+
}));
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export interface IBuildReleaseGitPushArgsOptions {
|
|
574
|
+
destination: string;
|
|
575
|
+
newVersion: string;
|
|
576
|
+
pushBranch: boolean;
|
|
577
|
+
pushTags: boolean;
|
|
578
|
+
mainOid: string;
|
|
579
|
+
tagOid: string;
|
|
580
|
+
expectedRemoteMainOid?: string;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export function buildReleaseGitPushArgs(
|
|
584
|
+
optionsArg: IBuildReleaseGitPushArgsOptions,
|
|
585
|
+
): string[] {
|
|
586
|
+
const refspecs: string[] = [];
|
|
587
|
+
if (optionsArg.pushBranch) {
|
|
588
|
+
refspecs.push(`${optionsArg.mainOid}:refs/heads/main`);
|
|
236
589
|
}
|
|
237
|
-
|
|
590
|
+
if (optionsArg.pushTags) {
|
|
591
|
+
refspecs.push(`${optionsArg.tagOid}:refs/tags/v${optionsArg.newVersion}`);
|
|
592
|
+
}
|
|
593
|
+
const args = ["push", "--no-follow-tags"];
|
|
594
|
+
if (refspecs.length > 1) {
|
|
595
|
+
args.push("--atomic");
|
|
596
|
+
}
|
|
597
|
+
if (optionsArg.pushBranch && optionsArg.expectedRemoteMainOid) {
|
|
598
|
+
args.push(
|
|
599
|
+
`--force-with-lease=refs/heads/main:${optionsArg.expectedRemoteMainOid}`,
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
args.push(optionsArg.destination, ...refspecs);
|
|
603
|
+
return args;
|
|
238
604
|
}
|
|
239
605
|
|
|
240
606
|
async function runNpmTarget(
|
|
241
607
|
smartshellInstance: plugins.smartshell.Smartshell,
|
|
242
608
|
workflow: IResolvedReleaseWorkflow,
|
|
609
|
+
cwdArg: string,
|
|
243
610
|
): Promise<ITargetResult[]> {
|
|
244
611
|
if (!workflow.npmEnabled) {
|
|
245
612
|
return [{ target: "npm", status: "skipped", message: "disabled" }];
|
|
246
613
|
}
|
|
247
614
|
if (workflow.npmRegistries.length === 0) {
|
|
248
|
-
return [
|
|
615
|
+
return [
|
|
616
|
+
{ target: "npm", status: "failed", message: "no registries configured" },
|
|
617
|
+
];
|
|
249
618
|
}
|
|
250
619
|
|
|
251
620
|
const results: ITargetResult[] = [];
|
|
252
621
|
for (const registry of workflow.npmRegistries) {
|
|
253
622
|
const command = `pnpm publish --registry=${registry} --access=${workflow.npmAccessLevel}`;
|
|
254
|
-
const result = await smartshellInstance.exec(command);
|
|
623
|
+
const result = await smartshellInstance.exec(command, { cwd: cwdArg });
|
|
255
624
|
const output = result.combinedOutput;
|
|
256
625
|
if (result.exitCode === 0) {
|
|
257
626
|
results.push({ target: registry, status: "success" });
|
|
258
|
-
} else if (
|
|
627
|
+
} else if (
|
|
628
|
+
isAlreadyPublishedOutput(output) &&
|
|
629
|
+
workflow.npmAlreadyPublished === "success"
|
|
630
|
+
) {
|
|
259
631
|
results.push({ target: registry, status: "already-published" });
|
|
260
632
|
} else {
|
|
261
|
-
results.push({
|
|
633
|
+
results.push({
|
|
634
|
+
target: registry,
|
|
635
|
+
status: "failed",
|
|
636
|
+
message: firstMeaningfulLine(output),
|
|
637
|
+
});
|
|
262
638
|
}
|
|
263
639
|
}
|
|
264
640
|
return results;
|
|
@@ -267,6 +643,7 @@ async function runNpmTarget(
|
|
|
267
643
|
async function runDockerTarget(
|
|
268
644
|
smartshellInstance: plugins.smartshell.Smartshell,
|
|
269
645
|
workflow: IResolvedReleaseWorkflow,
|
|
646
|
+
cwdArg: string,
|
|
270
647
|
): Promise<ITargetResult[]> {
|
|
271
648
|
if (!workflow.dockerEnabled) {
|
|
272
649
|
return [{ target: "docker", status: "skipped", message: "disabled" }];
|
|
@@ -275,24 +652,33 @@ async function runDockerTarget(
|
|
|
275
652
|
let packageJson: unknown;
|
|
276
653
|
try {
|
|
277
654
|
packageJson = JSON.parse(
|
|
278
|
-
await plugins.fs.readFile(
|
|
655
|
+
await plugins.fs.readFile(
|
|
656
|
+
plugins.path.join(cwdArg, "package.json"),
|
|
657
|
+
"utf8",
|
|
658
|
+
),
|
|
279
659
|
);
|
|
280
660
|
} catch {
|
|
281
661
|
packageJson = undefined;
|
|
282
662
|
}
|
|
283
663
|
if (!getDeclaredTsdockerRange(packageJson)) {
|
|
284
|
-
return [
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
664
|
+
return [
|
|
665
|
+
{
|
|
666
|
+
target: "tsdocker",
|
|
667
|
+
status: "failed",
|
|
668
|
+
message:
|
|
669
|
+
"Docker releases require @git.zone/tsdocker as a project dependency",
|
|
670
|
+
},
|
|
671
|
+
];
|
|
672
|
+
}
|
|
673
|
+
if (!(await hasInstalledProjectTsdockerBinary(cwdArg))) {
|
|
674
|
+
return [
|
|
675
|
+
{
|
|
676
|
+
target: "tsdocker",
|
|
677
|
+
status: "failed",
|
|
678
|
+
message:
|
|
679
|
+
"Docker releases require the installed project-local tSDocker binary",
|
|
680
|
+
},
|
|
681
|
+
];
|
|
296
682
|
}
|
|
297
683
|
|
|
298
684
|
const minimumTsdockerVersion = getTsdockerMinimumVersion(
|
|
@@ -300,33 +686,43 @@ async function runDockerTarget(
|
|
|
300
686
|
workflow.dockerTest,
|
|
301
687
|
);
|
|
302
688
|
if (minimumTsdockerVersion) {
|
|
303
|
-
const versionResult = await smartshellInstance.exec(
|
|
689
|
+
const versionResult = await smartshellInstance.exec(
|
|
690
|
+
"pnpm exec tsdocker --version",
|
|
691
|
+
{ cwd: cwdArg },
|
|
692
|
+
);
|
|
304
693
|
if (
|
|
305
|
-
versionResult.exitCode !== 0
|
|
306
|
-
|
|
694
|
+
versionResult.exitCode !== 0 ||
|
|
695
|
+
!supportsTsdockerMinimumVersion(
|
|
307
696
|
versionResult.combinedOutput,
|
|
308
697
|
minimumTsdockerVersion,
|
|
309
698
|
)
|
|
310
699
|
) {
|
|
311
|
-
const minimumVersion = formatTsdockerMinimumVersion(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
700
|
+
const minimumVersion = formatTsdockerMinimumVersion(
|
|
701
|
+
minimumTsdockerVersion,
|
|
702
|
+
);
|
|
703
|
+
return [
|
|
704
|
+
{
|
|
705
|
+
target: "tsdocker",
|
|
706
|
+
status: "failed",
|
|
707
|
+
message: `Docker release options require project-local @git.zone/tsdocker >= ${minimumVersion}`,
|
|
708
|
+
},
|
|
709
|
+
];
|
|
317
710
|
}
|
|
318
711
|
}
|
|
319
712
|
|
|
320
713
|
const command = buildTsdockerPushCommand(workflow);
|
|
321
|
-
const result = await smartshellInstance.exec(command);
|
|
714
|
+
const result = await smartshellInstance.exec(command, { cwd: cwdArg });
|
|
322
715
|
const output = result.combinedOutput;
|
|
323
|
-
return [
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
716
|
+
return [
|
|
717
|
+
{
|
|
718
|
+
target:
|
|
719
|
+
workflow.dockerPatterns.length > 0
|
|
720
|
+
? `tsdocker:${workflow.dockerPatterns.join(",")}`
|
|
721
|
+
: "tsdocker",
|
|
722
|
+
status: result.exitCode === 0 ? "success" : "failed",
|
|
723
|
+
message: result.exitCode === 0 ? undefined : firstMeaningfulLine(output),
|
|
724
|
+
},
|
|
725
|
+
];
|
|
330
726
|
}
|
|
331
727
|
|
|
332
728
|
type TDockerCommandWorkflow = Pick<
|
|
@@ -341,7 +737,9 @@ type TDockerCommandWorkflow = Pick<
|
|
|
341
737
|
| "dockerPatterns"
|
|
342
738
|
>;
|
|
343
739
|
|
|
344
|
-
export function buildTsdockerPushCommand(
|
|
740
|
+
export function buildTsdockerPushCommand(
|
|
741
|
+
workflow: TDockerCommandWorkflow,
|
|
742
|
+
): string {
|
|
345
743
|
const commandParts = ["pnpm", "exec", "tsdocker", "push"];
|
|
346
744
|
for (const pattern of workflow.dockerPatterns) {
|
|
347
745
|
commandParts.push(shellQuote(pattern));
|
|
@@ -350,7 +748,9 @@ export function buildTsdockerPushCommand(workflow: TDockerCommandWorkflow): stri
|
|
|
350
748
|
commandParts.push(`--registry=${shellQuote(workflow.dockerRegistry)}`);
|
|
351
749
|
}
|
|
352
750
|
if (workflow.dockerBuildRegistries.length > 0) {
|
|
353
|
-
commandParts.push(
|
|
751
|
+
commandParts.push(
|
|
752
|
+
`--build-registries=${shellQuote(workflow.dockerBuildRegistries.join(","))}`,
|
|
753
|
+
);
|
|
354
754
|
}
|
|
355
755
|
if (workflow.dockerTest) {
|
|
356
756
|
commandParts.push("--test");
|
|
@@ -363,7 +763,11 @@ export function buildTsdockerPushCommand(workflow: TDockerCommandWorkflow): stri
|
|
|
363
763
|
}
|
|
364
764
|
if (workflow.dockerParallel === true) {
|
|
365
765
|
commandParts.push("--parallel");
|
|
366
|
-
} else if (
|
|
766
|
+
} else if (
|
|
767
|
+
typeof workflow.dockerParallel === "number" &&
|
|
768
|
+
Number.isFinite(workflow.dockerParallel) &&
|
|
769
|
+
workflow.dockerParallel > 0
|
|
770
|
+
) {
|
|
367
771
|
commandParts.push(`--parallel=${Math.floor(workflow.dockerParallel)}`);
|
|
368
772
|
}
|
|
369
773
|
if (workflow.dockerContext) {
|
|
@@ -373,36 +777,64 @@ export function buildTsdockerPushCommand(workflow: TDockerCommandWorkflow): stri
|
|
|
373
777
|
}
|
|
374
778
|
|
|
375
779
|
function isAlreadyPublishedOutput(output: string): boolean {
|
|
376
|
-
return /previously published versions|cannot publish over|already exists/i.test(
|
|
780
|
+
return /previously published versions|cannot publish over|already exists/i.test(
|
|
781
|
+
output,
|
|
782
|
+
);
|
|
377
783
|
}
|
|
378
784
|
|
|
379
785
|
function firstMeaningfulLine(output: string): string {
|
|
380
|
-
return
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
786
|
+
return (
|
|
787
|
+
output
|
|
788
|
+
.split("\n")
|
|
789
|
+
.map((line) => line.trim())
|
|
790
|
+
.find((line) => line.length > 0) || "command failed"
|
|
791
|
+
);
|
|
384
792
|
}
|
|
385
793
|
|
|
386
794
|
function shellQuote(value: string): string {
|
|
387
795
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
388
796
|
}
|
|
389
797
|
|
|
390
|
-
function printReleasePlan(
|
|
798
|
+
function printReleasePlan(
|
|
799
|
+
workflow: IResolvedReleaseWorkflow,
|
|
800
|
+
branchContextArg: IReleaseBranchContext,
|
|
801
|
+
): void {
|
|
391
802
|
console.log("");
|
|
392
803
|
console.log("gitzone release - resolved workflow");
|
|
393
804
|
console.log(`confirmation: ${workflow.confirmation}`);
|
|
394
805
|
console.log(`plan: ${workflow.plan.join(" -> ")}`);
|
|
395
|
-
console.log(`
|
|
396
|
-
|
|
806
|
+
console.log(`source branch: ${branchContextArg.sourceBranch}`);
|
|
807
|
+
if (branchContextArg.sourceBranch !== "main") {
|
|
808
|
+
console.log("release branch: main (fast-forward integration requested)");
|
|
809
|
+
console.log(
|
|
810
|
+
`remote main ancestry: ${branchContextArg.planAncestryVerified ? "verified" : "deferred until execution"}`,
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
console.log(
|
|
814
|
+
`targets: ${workflow.targets.length > 0 ? workflow.targets.join(", ") : "none"}`,
|
|
815
|
+
);
|
|
816
|
+
console.log(
|
|
817
|
+
`changelog: ${workflow.changelogFile}#${workflow.changelogPendingSection}`,
|
|
818
|
+
);
|
|
397
819
|
if (workflow.targets.includes("npm")) {
|
|
398
|
-
console.log(
|
|
820
|
+
console.log(
|
|
821
|
+
`npm registries: ${workflow.npmRegistries.length > 0 ? workflow.npmRegistries.join(", ") : "none"}`,
|
|
822
|
+
);
|
|
399
823
|
}
|
|
400
824
|
if (workflow.targets.includes("docker")) {
|
|
401
825
|
console.log(`docker engine: ${workflow.dockerEngine}`);
|
|
402
|
-
console.log(
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
console.log(
|
|
826
|
+
console.log(
|
|
827
|
+
`docker registry: ${workflow.dockerRegistry || "all configured registries"}`,
|
|
828
|
+
);
|
|
829
|
+
console.log(
|
|
830
|
+
`docker build authentication: ${workflow.dockerBuildRegistries.length > 0 ? workflow.dockerBuildRegistries.join(", ") : "all configured registries"}`,
|
|
831
|
+
);
|
|
832
|
+
console.log(
|
|
833
|
+
`docker image tests: ${workflow.dockerTest ? "required before destination publication" : "not requested"}`,
|
|
834
|
+
);
|
|
835
|
+
console.log(
|
|
836
|
+
`docker patterns: ${workflow.dockerPatterns.length > 0 ? workflow.dockerPatterns.join(", ") : "all Dockerfiles"}`,
|
|
837
|
+
);
|
|
406
838
|
console.log(`docker options: ${formatDockerOptions(workflow)}`);
|
|
407
839
|
}
|
|
408
840
|
console.log("");
|
|
@@ -410,13 +842,17 @@ function printReleasePlan(workflow: IResolvedReleaseWorkflow): void {
|
|
|
410
842
|
|
|
411
843
|
function formatDockerOptions(workflow: IResolvedReleaseWorkflow): string {
|
|
412
844
|
const options: string[] = [];
|
|
413
|
-
if (workflow.dockerRegistry)
|
|
845
|
+
if (workflow.dockerRegistry)
|
|
846
|
+
options.push(`registry=${workflow.dockerRegistry}`);
|
|
414
847
|
if (workflow.dockerBuildRegistries.length > 0) {
|
|
415
848
|
options.push(`buildRegistries=${workflow.dockerBuildRegistries.join(",")}`);
|
|
416
849
|
}
|
|
417
850
|
if (workflow.dockerTest) options.push("test");
|
|
418
851
|
if (workflow.dockerCached) options.push("cached");
|
|
419
|
-
if (workflow.dockerParallel)
|
|
852
|
+
if (workflow.dockerParallel)
|
|
853
|
+
options.push(
|
|
854
|
+
`parallel=${workflow.dockerParallel === true ? "true" : workflow.dockerParallel}`,
|
|
855
|
+
);
|
|
420
856
|
if (workflow.dockerNoBuild) options.push("no-build");
|
|
421
857
|
if (workflow.dockerContext) options.push(`context=${workflow.dockerContext}`);
|
|
422
858
|
return options.length > 0 ? options.join(", ") : "default";
|
|
@@ -435,21 +871,27 @@ function printReleaseSummary(
|
|
|
435
871
|
if (gitResults.length > 0) {
|
|
436
872
|
console.log("git:");
|
|
437
873
|
for (const result of gitResults) {
|
|
438
|
-
console.log(
|
|
874
|
+
console.log(
|
|
875
|
+
` ${result.target} ${result.status}${result.message ? ` (${result.message})` : ""}`,
|
|
876
|
+
);
|
|
439
877
|
}
|
|
440
878
|
}
|
|
441
879
|
|
|
442
880
|
if (npmResults.length > 0) {
|
|
443
881
|
console.log("npm:");
|
|
444
882
|
for (const result of npmResults) {
|
|
445
|
-
console.log(
|
|
883
|
+
console.log(
|
|
884
|
+
` ${result.target} ${result.status}${result.message ? ` (${result.message})` : ""}`,
|
|
885
|
+
);
|
|
446
886
|
}
|
|
447
887
|
}
|
|
448
888
|
|
|
449
889
|
if (dockerResults.length > 0) {
|
|
450
890
|
console.log("docker:");
|
|
451
891
|
for (const result of dockerResults) {
|
|
452
|
-
console.log(
|
|
892
|
+
console.log(
|
|
893
|
+
` ${result.target} ${result.status}${result.message ? ` (${result.message})` : ""}`,
|
|
894
|
+
);
|
|
453
895
|
}
|
|
454
896
|
}
|
|
455
897
|
}
|
|
@@ -459,17 +901,44 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
459
901
|
printJson({
|
|
460
902
|
command: "release",
|
|
461
903
|
usage: "gitzone release [options]",
|
|
462
|
-
description:
|
|
904
|
+
description:
|
|
905
|
+
"Creates a versioned release from pending changelog entries and publishes configured artifacts.",
|
|
463
906
|
flags: [
|
|
464
|
-
{
|
|
907
|
+
{
|
|
908
|
+
flag: "-y, --yes",
|
|
909
|
+
description: "Run without interactive confirmation",
|
|
910
|
+
},
|
|
465
911
|
{ flag: "-t, --test", description: "Enable release preflight tests" },
|
|
466
|
-
{
|
|
912
|
+
{
|
|
913
|
+
flag: "-b, --build",
|
|
914
|
+
description:
|
|
915
|
+
"Enable the build after local release metadata is created",
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
flag: "--no-build",
|
|
919
|
+
description: "Disable the post-metadata release build",
|
|
920
|
+
},
|
|
467
921
|
{ flag: "-p, --push", description: "Enable the git release target" },
|
|
468
|
-
{
|
|
922
|
+
{
|
|
923
|
+
flag: "--target <names>",
|
|
924
|
+
description: "Release only selected targets: git,npm,docker",
|
|
925
|
+
},
|
|
469
926
|
{ flag: "--npm", description: "Enable the npm release target" },
|
|
470
927
|
{ flag: "--docker", description: "Enable the tsdocker release target" },
|
|
471
|
-
{
|
|
472
|
-
|
|
928
|
+
{
|
|
929
|
+
flag: "--no-publish",
|
|
930
|
+
description: "Run release core and git target only",
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
flag: "--merge",
|
|
934
|
+
description:
|
|
935
|
+
"Fast-forward and lease-push a cleanly rebased feature branch before release metadata",
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
flag: "--plan",
|
|
939
|
+
description:
|
|
940
|
+
"Show the workflow without fetching or mutating refs, files, the index, or worktrees",
|
|
941
|
+
},
|
|
473
942
|
],
|
|
474
943
|
});
|
|
475
944
|
return;
|
|
@@ -483,13 +952,23 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
483
952
|
console.log("Flags:");
|
|
484
953
|
console.log(" -y, --yes Run without interactive confirmation");
|
|
485
954
|
console.log(" -t, --test Enable release preflight tests");
|
|
486
|
-
console.log(
|
|
955
|
+
console.log(
|
|
956
|
+
" -b, --build Build after creating local release metadata",
|
|
957
|
+
);
|
|
958
|
+
console.log(" --no-build Disable the post-metadata release build");
|
|
487
959
|
console.log(" -p, --push Enable the git release target");
|
|
488
|
-
console.log(
|
|
960
|
+
console.log(
|
|
961
|
+
" --target <names> Release only selected targets: git,npm,docker",
|
|
962
|
+
);
|
|
489
963
|
console.log(" --npm Enable the npm release target");
|
|
490
964
|
console.log(" --docker Enable the tsdocker release target");
|
|
491
965
|
console.log(" --no-publish Run release core and git target only");
|
|
966
|
+
console.log(
|
|
967
|
+
" --merge Fast-forward and lease-push a cleanly rebased feature branch before release metadata",
|
|
968
|
+
);
|
|
492
969
|
console.log(" --major|--minor|--patch Override inferred semver level");
|
|
493
|
-
console.log(
|
|
970
|
+
console.log(
|
|
971
|
+
" --plan Show workflow without fetching or mutating refs, files, index, or worktrees",
|
|
972
|
+
);
|
|
494
973
|
console.log("");
|
|
495
974
|
}
|