@quiltdata/benchling-webhook 0.21.2-20261007T162722Z → 0.21.3
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 +5 -5
- package/dist/package.json +4 -1
- package/dist/scripts/npm-publish.d.ts +37 -0
- package/dist/scripts/npm-publish.d.ts.map +1 -0
- package/dist/scripts/npm-publish.js +186 -0
- package/dist/scripts/npm-publish.js.map +1 -0
- package/dist/scripts/version.d.ts +96 -9
- package/dist/scripts/version.d.ts.map +1 -1
- package/dist/scripts/version.js +592 -233
- package/dist/scripts/version.js.map +1 -1
- package/package.json +4 -1
package/dist/scripts/version.js
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
* Version management
|
|
4
|
+
* Version management and release-tag tooling.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* node bin/version.ts patch # 0.4.7 -> 0.4.8
|
|
9
|
-
* node bin/version.ts minor # 0.4.7 -> 0.5.0
|
|
10
|
-
* node bin/version.ts major # 0.4.7 -> 1.0.0
|
|
11
|
-
* node bin/version.ts sync # Force TOML and YAML to match JSON version
|
|
12
|
-
* node bin/version.ts tag # Create and push production release tag
|
|
13
|
-
* node bin/version.ts tag dev # Create and push dev release tag
|
|
6
|
+
* Version bumps remain explicit commands. Release commands share tag construction,
|
|
7
|
+
* local/remote target verification, and annotated-tag creation semantics.
|
|
14
8
|
*/
|
|
15
9
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
10
|
if (k2 === undefined) k2 = k;
|
|
@@ -46,13 +40,453 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
46
40
|
};
|
|
47
41
|
})();
|
|
48
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.GitReleaseRepository = exports.defaultCommandRunner = exports.AUTOMATIC_RELEASE_TAGGER_EMAIL = exports.AUTOMATIC_RELEASE_TAGGER_NAME = void 0;
|
|
44
|
+
exports.isSemanticVersion = isSemanticVersion;
|
|
45
|
+
exports.parseReleaseVersion = parseReleaseVersion;
|
|
46
|
+
exports.compareReleaseVersions = compareReleaseVersions;
|
|
47
|
+
exports.productionTag = productionTag;
|
|
48
|
+
exports.devTag = devTag;
|
|
49
|
+
exports.annotatedTagMessage = annotatedTagMessage;
|
|
50
|
+
exports.parseTag = parseTag;
|
|
51
|
+
exports.isAutomaticReleaseTag = isAutomaticReleaseTag;
|
|
52
|
+
exports.runAutomaticRelease = runAutomaticRelease;
|
|
53
|
+
exports.validateReleaseContext = validateReleaseContext;
|
|
54
|
+
exports.runProductionGate = runProductionGate;
|
|
55
|
+
exports.main = main;
|
|
49
56
|
const fs = __importStar(require("fs"));
|
|
50
57
|
const path = __importStar(require("path"));
|
|
51
58
|
const child_process_1 = require("child_process");
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const
|
|
59
|
+
const projectRoot = path.join(__dirname, "..");
|
|
60
|
+
const packagePath = path.join(projectRoot, "package.json");
|
|
61
|
+
const pyprojectPath = path.join(projectRoot, "docker", "pyproject.toml");
|
|
62
|
+
const appManifestPath = path.join(projectRoot, "docker", "app-manifest.yaml");
|
|
63
|
+
exports.AUTOMATIC_RELEASE_TAGGER_NAME = "github-actions[bot]";
|
|
64
|
+
exports.AUTOMATIC_RELEASE_TAGGER_EMAIL = "41898282+github-actions[bot]@users.noreply.github.com";
|
|
65
|
+
const CORE_VERSION_PATTERN = "(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)";
|
|
66
|
+
const PLAIN_VERSION_PATTERN = new RegExp(`^${CORE_VERSION_PATTERN}$`);
|
|
67
|
+
const TIMESTAMP_VERSION_PATTERN = new RegExp(`^${CORE_VERSION_PATTERN}-(\\d{8}T\\d{6}Z)$`);
|
|
68
|
+
const SEMVER_PATTERN = new RegExp(`^${CORE_VERSION_PATTERN}(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$`);
|
|
69
|
+
const defaultCommandRunner = (command, args, options) => {
|
|
70
|
+
const result = (0, child_process_1.spawnSync)(command, [...args], {
|
|
71
|
+
cwd: options.cwd,
|
|
72
|
+
encoding: "utf8",
|
|
73
|
+
stdio: options.inherit ? "inherit" : "pipe",
|
|
74
|
+
});
|
|
75
|
+
return {
|
|
76
|
+
status: result.status ?? 127,
|
|
77
|
+
stdout: result.stdout?.toString() ?? "",
|
|
78
|
+
stderr: [result.stderr?.toString(), result.error?.message].filter(Boolean).join("\n"),
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
exports.defaultCommandRunner = defaultCommandRunner;
|
|
82
|
+
function commandFailure(command, args, result) {
|
|
83
|
+
const detail = result.stderr.trim() || result.stdout.trim() || `exit status ${result.status}`;
|
|
84
|
+
return new Error(`${command} ${args.join(" ")} failed: ${detail}`);
|
|
85
|
+
}
|
|
86
|
+
function runChecked(runner, command, args, cwd, inherit = false) {
|
|
87
|
+
const result = runner(command, args, { cwd, inherit });
|
|
88
|
+
if (result.status !== 0) {
|
|
89
|
+
throw commandFailure(command, args, result);
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
function normalizeSha(value, label) {
|
|
94
|
+
const sha = value.trim().toLowerCase();
|
|
95
|
+
if (!/^[0-9a-f]{40,64}$/.test(sha)) {
|
|
96
|
+
throw new Error(`${label} is not a full commit SHA: ${value || "<empty>"}`);
|
|
97
|
+
}
|
|
98
|
+
return sha;
|
|
99
|
+
}
|
|
100
|
+
function parseVersionParts(match) {
|
|
101
|
+
return {
|
|
102
|
+
major: Number.parseInt(match[1], 10),
|
|
103
|
+
minor: Number.parseInt(match[2], 10),
|
|
104
|
+
patch: Number.parseInt(match[3], 10),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function timestampIsValid(timestamp) {
|
|
108
|
+
const match = timestamp.match(/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z$/);
|
|
109
|
+
if (!match) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
const iso = `${match[1]}-${match[2]}-${match[3]}T${match[4]}:${match[5]}:${match[6]}.000Z`;
|
|
113
|
+
const date = new Date(iso);
|
|
114
|
+
return !Number.isNaN(date.valueOf()) && date.toISOString() === iso;
|
|
115
|
+
}
|
|
116
|
+
function isSemanticVersion(version) {
|
|
117
|
+
const match = version.match(SEMVER_PATTERN);
|
|
118
|
+
if (!match) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
const prerelease = match[4];
|
|
122
|
+
if (!prerelease) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
return prerelease.split(".").every((identifier) => !/^\d+$/.test(identifier) || identifier === "0" || !identifier.startsWith("0"));
|
|
126
|
+
}
|
|
127
|
+
function parseReleaseVersion(version) {
|
|
128
|
+
const stableMatch = version.match(PLAIN_VERSION_PATTERN);
|
|
129
|
+
if (stableMatch) {
|
|
130
|
+
return {
|
|
131
|
+
...parseVersionParts(stableMatch),
|
|
132
|
+
raw: version,
|
|
133
|
+
prerelease: false,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const timestampMatch = version.match(TIMESTAMP_VERSION_PATTERN);
|
|
137
|
+
if (timestampMatch && timestampIsValid(timestampMatch[4])) {
|
|
138
|
+
return {
|
|
139
|
+
...parseVersionParts(timestampMatch),
|
|
140
|
+
raw: version,
|
|
141
|
+
prerelease: true,
|
|
142
|
+
timestamp: timestampMatch[4],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
throw new Error(`Unsupported release version: ${version}`);
|
|
146
|
+
}
|
|
147
|
+
function compareReleaseVersions(left, right) {
|
|
148
|
+
const a = parseReleaseVersion(left);
|
|
149
|
+
const b = parseReleaseVersion(right);
|
|
150
|
+
for (const key of ["major", "minor", "patch"]) {
|
|
151
|
+
if (a[key] !== b[key]) {
|
|
152
|
+
return a[key] < b[key] ? -1 : 1;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (a.prerelease !== b.prerelease) {
|
|
156
|
+
return a.prerelease ? -1 : 1;
|
|
157
|
+
}
|
|
158
|
+
if (!a.prerelease) {
|
|
159
|
+
return 0;
|
|
160
|
+
}
|
|
161
|
+
return a.timestamp === b.timestamp ? 0 : a.timestamp < b.timestamp ? -1 : 1;
|
|
162
|
+
}
|
|
163
|
+
function productionTag(version) {
|
|
164
|
+
const parsed = parseReleaseVersion(version);
|
|
165
|
+
if (parsed.prerelease) {
|
|
166
|
+
throw new Error(`Production tags require a plain semantic version: ${version}`);
|
|
167
|
+
}
|
|
168
|
+
return `v${version}`;
|
|
169
|
+
}
|
|
170
|
+
function devTag(version, now = new Date()) {
|
|
171
|
+
productionTag(version);
|
|
172
|
+
const timestamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
173
|
+
return `v${version}-${timestamp}`;
|
|
174
|
+
}
|
|
175
|
+
function annotatedTagMessage(tag) {
|
|
176
|
+
const release = parseTag(tag);
|
|
177
|
+
return release.isPrerelease
|
|
178
|
+
? `Development pre-release (dev) ${tag}\n\nThis is a pre-release for testing purposes.`
|
|
179
|
+
: `Release ${tag}`;
|
|
180
|
+
}
|
|
181
|
+
function parseTag(tag) {
|
|
182
|
+
if (!tag.startsWith("v")) {
|
|
183
|
+
throw new Error(`Release tag must start with v: ${tag}`);
|
|
184
|
+
}
|
|
185
|
+
const version = tag.slice(1);
|
|
186
|
+
const release = parseReleaseVersion(version);
|
|
187
|
+
const baseVersion = `${release.major}.${release.minor}.${release.patch}`;
|
|
188
|
+
return {
|
|
189
|
+
tag,
|
|
190
|
+
version,
|
|
191
|
+
baseVersion,
|
|
192
|
+
isPrerelease: release.prerelease,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
class GitReleaseRepository {
|
|
196
|
+
constructor(cwd, runner = exports.defaultCommandRunner) {
|
|
197
|
+
this.cwd = cwd;
|
|
198
|
+
this.runner = runner;
|
|
199
|
+
}
|
|
200
|
+
git(args, inherit = false) {
|
|
201
|
+
return this.runner("git", args, { cwd: this.cwd, inherit });
|
|
202
|
+
}
|
|
203
|
+
resolveCommit(ref) {
|
|
204
|
+
const args = ["rev-parse", `${ref}^{commit}`];
|
|
205
|
+
const result = this.git(args);
|
|
206
|
+
if (result.status !== 0) {
|
|
207
|
+
throw commandFailure("git", args, result);
|
|
208
|
+
}
|
|
209
|
+
return normalizeSha(result.stdout, ref);
|
|
210
|
+
}
|
|
211
|
+
commitExists(ref) {
|
|
212
|
+
const args = ["cat-file", "-e", `${ref}^{commit}`];
|
|
213
|
+
const result = this.git(args);
|
|
214
|
+
if (result.status === 0) {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
if (result.status === 1 || result.status === 128) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
throw commandFailure("git", args, result);
|
|
221
|
+
}
|
|
222
|
+
readFileAtCommit(commit, file) {
|
|
223
|
+
const args = ["show", `${commit}:${file}`];
|
|
224
|
+
const result = this.git(args);
|
|
225
|
+
if (result.status !== 0) {
|
|
226
|
+
throw commandFailure("git", args, result);
|
|
227
|
+
}
|
|
228
|
+
return result.stdout;
|
|
229
|
+
}
|
|
230
|
+
localTagTarget(tag) {
|
|
231
|
+
const ref = `refs/tags/${tag}`;
|
|
232
|
+
const checkArgs = ["show-ref", "--verify", "--quiet", ref];
|
|
233
|
+
const check = this.git(checkArgs);
|
|
234
|
+
if (check.status === 1) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
if (check.status !== 0) {
|
|
238
|
+
throw commandFailure("git", checkArgs, check);
|
|
239
|
+
}
|
|
240
|
+
return this.resolveCommit(ref);
|
|
241
|
+
}
|
|
242
|
+
fetchedTagAnnotation(tag) {
|
|
243
|
+
const typeArgs = ["cat-file", "-t", "FETCH_HEAD"];
|
|
244
|
+
const typeResult = this.git(typeArgs);
|
|
245
|
+
if (typeResult.status !== 0) {
|
|
246
|
+
throw commandFailure("git", typeArgs, typeResult);
|
|
247
|
+
}
|
|
248
|
+
const objectType = typeResult.stdout.trim();
|
|
249
|
+
if (objectType === "commit") {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
if (objectType !== "tag") {
|
|
253
|
+
throw new Error(`Remote tag ${tag} points to unsupported object type ${objectType}`);
|
|
254
|
+
}
|
|
255
|
+
const contentArgs = ["cat-file", "tag", "FETCH_HEAD"];
|
|
256
|
+
const contentResult = this.git(contentArgs);
|
|
257
|
+
if (contentResult.status !== 0) {
|
|
258
|
+
throw commandFailure("git", contentArgs, contentResult);
|
|
259
|
+
}
|
|
260
|
+
const separator = contentResult.stdout.indexOf("\n\n");
|
|
261
|
+
if (separator < 0) {
|
|
262
|
+
throw new Error(`Remote annotated tag ${tag} has malformed content`);
|
|
263
|
+
}
|
|
264
|
+
const headers = contentResult.stdout.slice(0, separator).split("\n");
|
|
265
|
+
if (!headers.includes(`tag ${tag}`)) {
|
|
266
|
+
throw new Error(`Remote annotated tag ${tag} has a mismatched internal tag name`);
|
|
267
|
+
}
|
|
268
|
+
const taggerHeader = headers.find((line) => line.startsWith("tagger "));
|
|
269
|
+
const tagger = taggerHeader?.match(/^tagger (.*) <([^<>]+)> \d+ [+-]\d{4}$/);
|
|
270
|
+
if (!tagger) {
|
|
271
|
+
throw new Error(`Remote annotated tag ${tag} has a malformed tagger identity`);
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
message: contentResult.stdout.slice(separator + 2).replace(/\n+$/, ""),
|
|
275
|
+
taggerName: tagger[1],
|
|
276
|
+
taggerEmail: tagger[2],
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
remoteTag(tag) {
|
|
280
|
+
const ref = `refs/tags/${tag}`;
|
|
281
|
+
const lookupArgs = ["ls-remote", "--exit-code", "--refs", "origin", ref];
|
|
282
|
+
const lookup = this.git(lookupArgs);
|
|
283
|
+
if (lookup.status === 2) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
if (lookup.status !== 0) {
|
|
287
|
+
throw commandFailure("git", lookupArgs, lookup);
|
|
288
|
+
}
|
|
289
|
+
const lines = lookup.stdout.trim().split("\n").filter(Boolean);
|
|
290
|
+
if (lines.length !== 1 || !lines[0].endsWith(`\t${ref}`)) {
|
|
291
|
+
throw new Error(`Remote lookup for ${tag} returned ambiguous data`);
|
|
292
|
+
}
|
|
293
|
+
const fetchArgs = ["fetch", "--force", "--no-tags", "origin", ref];
|
|
294
|
+
const fetch = this.git(fetchArgs);
|
|
295
|
+
if (fetch.status !== 0) {
|
|
296
|
+
throw commandFailure("git", fetchArgs, fetch);
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
targetSha: this.resolveCommit("FETCH_HEAD"),
|
|
300
|
+
annotation: this.fetchedTagAnnotation(tag),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
remoteTagTarget(tag) {
|
|
304
|
+
return this.remoteTag(tag)?.targetSha ?? null;
|
|
305
|
+
}
|
|
306
|
+
createAnnotatedTag(tag, message, targetSha) {
|
|
307
|
+
runChecked(this.runner, "git", ["tag", "-a", tag, "-m", message, targetSha], this.cwd, true);
|
|
308
|
+
}
|
|
309
|
+
pushTag(tag) {
|
|
310
|
+
const ref = `refs/tags/${tag}`;
|
|
311
|
+
return this.git(["push", "origin", `${ref}:${ref}`], true).status === 0;
|
|
312
|
+
}
|
|
313
|
+
isClean() {
|
|
314
|
+
const args = ["status", "--porcelain", "--untracked-files=normal"];
|
|
315
|
+
const result = this.git(args);
|
|
316
|
+
if (result.status !== 0) {
|
|
317
|
+
throw commandFailure("git", args, result);
|
|
318
|
+
}
|
|
319
|
+
return result.stdout.trim() === "";
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
exports.GitReleaseRepository = GitReleaseRepository;
|
|
323
|
+
function packageVersionFromContent(content, label) {
|
|
324
|
+
let value;
|
|
325
|
+
try {
|
|
326
|
+
value = JSON.parse(content);
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
throw new Error(`Could not parse ${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
330
|
+
}
|
|
331
|
+
if (!value || typeof value !== "object" || typeof value.version !== "string") {
|
|
332
|
+
throw new Error(`${label} does not contain a string version`);
|
|
333
|
+
}
|
|
334
|
+
return value.version;
|
|
335
|
+
}
|
|
336
|
+
function assertSemanticVersion(version, label) {
|
|
337
|
+
if (!isSemanticVersion(version)) {
|
|
338
|
+
throw new Error(`${label} is malformed: ${version}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function assertExpectedTarget(actual, expected, label) {
|
|
342
|
+
if (normalizeSha(actual, label) !== expected) {
|
|
343
|
+
throw new Error(`${label} points to ${actual}, expected ${expected}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function resolveBeforeCommit(repository, beforeSha) {
|
|
347
|
+
const supplied = beforeSha?.trim();
|
|
348
|
+
if (supplied && !/^0+$/.test(supplied) && repository.commitExists(supplied)) {
|
|
349
|
+
return repository.resolveCommit(supplied);
|
|
350
|
+
}
|
|
351
|
+
return repository.resolveCommit("HEAD^");
|
|
352
|
+
}
|
|
353
|
+
function verifiedResult(outcome, releaseRequired, tag, targetSha) {
|
|
354
|
+
return {
|
|
355
|
+
outcome,
|
|
356
|
+
releaseRequired,
|
|
357
|
+
verified: true,
|
|
358
|
+
tag,
|
|
359
|
+
targetSha,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function isAutomaticReleaseTag(tag, remote) {
|
|
363
|
+
const annotation = remote.annotation;
|
|
364
|
+
return annotation !== null
|
|
365
|
+
&& annotation.message === annotatedTagMessage(tag)
|
|
366
|
+
&& annotation.taggerName === exports.AUTOMATIC_RELEASE_TAGGER_NAME
|
|
367
|
+
&& annotation.taggerEmail === exports.AUTOMATIC_RELEASE_TAGGER_EMAIL;
|
|
368
|
+
}
|
|
369
|
+
function inspectExistingTag(repository, tag, targetSha) {
|
|
370
|
+
const local = repository.localTagTarget(tag);
|
|
371
|
+
if (local) {
|
|
372
|
+
assertExpectedTarget(local, targetSha, `Local tag ${tag}`);
|
|
373
|
+
}
|
|
374
|
+
const remote = repository.remoteTag(tag);
|
|
375
|
+
if (remote) {
|
|
376
|
+
assertExpectedTarget(remote.targetSha, targetSha, `Remote tag ${tag}`);
|
|
377
|
+
}
|
|
378
|
+
if (local && !remote) {
|
|
379
|
+
throw new Error(`Local tag ${tag} exists but the exact remote tag is absent`);
|
|
380
|
+
}
|
|
381
|
+
return { local, remote };
|
|
382
|
+
}
|
|
383
|
+
function runVerifiedProductionGate(options, targetSha) {
|
|
384
|
+
options.runProductionGate();
|
|
385
|
+
if (!options.repository.isClean()) {
|
|
386
|
+
throw new Error("Production gate left uncommitted changes in the working tree");
|
|
387
|
+
}
|
|
388
|
+
assertExpectedTarget(options.repository.resolveCommit("HEAD"), targetSha, "HEAD after production gate");
|
|
389
|
+
}
|
|
390
|
+
function runAutomaticRelease(options) {
|
|
391
|
+
const targetSha = normalizeSha(options.repository.resolveCommit("HEAD"), "HEAD");
|
|
392
|
+
const githubSha = normalizeSha(options.githubSha, "GITHUB_SHA");
|
|
393
|
+
if (targetSha !== githubSha) {
|
|
394
|
+
throw new Error(`Checked-out HEAD ${targetSha} does not match GITHUB_SHA ${githubSha}`);
|
|
395
|
+
}
|
|
396
|
+
const beforeCommit = resolveBeforeCommit(options.repository, options.beforeSha);
|
|
397
|
+
const previousVersion = packageVersionFromContent(options.repository.readFileAtCommit(beforeCommit, "package.json"), `package.json at ${beforeCommit}`);
|
|
398
|
+
assertSemanticVersion(previousVersion, "Previous package version");
|
|
399
|
+
assertSemanticVersion(options.currentVersion, "Current package version");
|
|
400
|
+
if (previousVersion === options.currentVersion) {
|
|
401
|
+
return {
|
|
402
|
+
outcome: "no-version-change",
|
|
403
|
+
releaseRequired: false,
|
|
404
|
+
verified: false,
|
|
405
|
+
tag: "",
|
|
406
|
+
targetSha,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
if (!PLAIN_VERSION_PATTERN.test(options.currentVersion)) {
|
|
410
|
+
return {
|
|
411
|
+
outcome: "non-production-version",
|
|
412
|
+
releaseRequired: false,
|
|
413
|
+
verified: false,
|
|
414
|
+
tag: "",
|
|
415
|
+
targetSha,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
const tag = productionTag(options.currentVersion);
|
|
419
|
+
const initial = inspectExistingTag(options.repository, tag, targetSha);
|
|
420
|
+
if (initial.remote) {
|
|
421
|
+
if (!isAutomaticReleaseTag(tag, initial.remote)) {
|
|
422
|
+
return verifiedResult("verified-existing-tag", false, tag, targetSha);
|
|
423
|
+
}
|
|
424
|
+
runVerifiedProductionGate(options, targetSha);
|
|
425
|
+
const recovered = inspectExistingTag(options.repository, tag, targetSha);
|
|
426
|
+
if (!recovered.remote || !isAutomaticReleaseTag(tag, recovered.remote)) {
|
|
427
|
+
throw new Error(`Automatic release tag ${tag} changed during recovery verification`);
|
|
428
|
+
}
|
|
429
|
+
return verifiedResult("verified-recovered-tag", true, tag, targetSha);
|
|
430
|
+
}
|
|
431
|
+
runVerifiedProductionGate(options, targetSha);
|
|
432
|
+
const afterGate = inspectExistingTag(options.repository, tag, targetSha);
|
|
433
|
+
if (afterGate.remote) {
|
|
434
|
+
return verifiedResult("verified-race", true, tag, targetSha);
|
|
435
|
+
}
|
|
436
|
+
if (afterGate.local) {
|
|
437
|
+
throw new Error(`Local tag ${tag} appeared without a verified remote tag`);
|
|
438
|
+
}
|
|
439
|
+
options.repository.createAnnotatedTag(tag, annotatedTagMessage(tag), targetSha);
|
|
440
|
+
const localTarget = options.repository.localTagTarget(tag);
|
|
441
|
+
if (!localTarget) {
|
|
442
|
+
throw new Error(`Created tag ${tag} cannot be resolved locally`);
|
|
443
|
+
}
|
|
444
|
+
assertExpectedTarget(localTarget, targetSha, `Created tag ${tag}`);
|
|
445
|
+
const pushed = options.repository.pushTag(tag);
|
|
446
|
+
const remote = options.repository.remoteTag(tag);
|
|
447
|
+
if (!remote) {
|
|
448
|
+
throw new Error(`Tag push ${pushed ? "reported success" : "failed"}, but remote tag ${tag} is absent`);
|
|
449
|
+
}
|
|
450
|
+
assertExpectedTarget(remote.targetSha, targetSha, `Remote tag ${tag} after push`);
|
|
451
|
+
return verifiedResult(pushed ? "created" : "verified-race", true, tag, targetSha);
|
|
452
|
+
}
|
|
453
|
+
function validateReleaseContext(repository, tag, packageVersion, githubSha) {
|
|
454
|
+
const parsed = parseTag(tag);
|
|
455
|
+
assertSemanticVersion(packageVersion, "Current package version");
|
|
456
|
+
if (parsed.baseVersion !== packageVersion) {
|
|
457
|
+
throw new Error(`Tag ${tag} does not match package version ${packageVersion}`);
|
|
458
|
+
}
|
|
459
|
+
const targetSha = normalizeSha(repository.resolveCommit("HEAD"), "HEAD");
|
|
460
|
+
const expectedSha = normalizeSha(githubSha, "GITHUB_SHA");
|
|
461
|
+
if (targetSha !== expectedSha) {
|
|
462
|
+
throw new Error(`Checked-out HEAD ${targetSha} does not match GITHUB_SHA ${expectedSha}`);
|
|
463
|
+
}
|
|
464
|
+
const local = repository.localTagTarget(tag);
|
|
465
|
+
if (!local) {
|
|
466
|
+
throw new Error(`Release tag ${tag} is not present locally`);
|
|
467
|
+
}
|
|
468
|
+
assertExpectedTarget(local, targetSha, `Local release tag ${tag}`);
|
|
469
|
+
const remote = repository.remoteTag(tag);
|
|
470
|
+
if (!remote) {
|
|
471
|
+
throw new Error(`Release tag ${tag} is not present on origin`);
|
|
472
|
+
}
|
|
473
|
+
assertExpectedTarget(remote.targetSha, targetSha, `Remote release tag ${tag}`);
|
|
474
|
+
return {
|
|
475
|
+
...parsed,
|
|
476
|
+
verified: true,
|
|
477
|
+
targetSha,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
function runProductionGate(repository, cwd = projectRoot, runner = exports.defaultCommandRunner) {
|
|
481
|
+
runChecked(runner, "npm", ["run", "version:verify"], cwd, true);
|
|
482
|
+
runChecked(runner, "npm", ["test"], cwd, true);
|
|
483
|
+
if (!repository.isClean()) {
|
|
484
|
+
throw new Error("Full npm test gate left uncommitted changes in the working tree");
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function readPackage() {
|
|
488
|
+
return JSON.parse(fs.readFileSync(packagePath, "utf8"));
|
|
489
|
+
}
|
|
56
490
|
function readPyprojectVersion() {
|
|
57
491
|
const content = fs.readFileSync(pyprojectPath, "utf8");
|
|
58
492
|
const match = content.match(/^version\s*=\s*"([^"]+)"/m);
|
|
@@ -64,42 +498,38 @@ function readAppManifestVersion() {
|
|
|
64
498
|
return match ? match[1].trim() : null;
|
|
65
499
|
}
|
|
66
500
|
function parseVersion(version) {
|
|
67
|
-
const match = version.match(
|
|
501
|
+
const match = version.match(PLAIN_VERSION_PATTERN);
|
|
68
502
|
if (!match) {
|
|
69
503
|
throw new Error(`Invalid version format: ${version}`);
|
|
70
504
|
}
|
|
71
|
-
return
|
|
72
|
-
major: parseInt(match[1], 10),
|
|
73
|
-
minor: parseInt(match[2], 10),
|
|
74
|
-
patch: parseInt(match[3], 10),
|
|
75
|
-
};
|
|
505
|
+
return parseVersionParts(match);
|
|
76
506
|
}
|
|
77
|
-
function formatVersion(
|
|
78
|
-
return `${
|
|
507
|
+
function formatVersion(version) {
|
|
508
|
+
return `${version.major}.${version.minor}.${version.patch}`;
|
|
79
509
|
}
|
|
80
510
|
function bumpVersion(currentVersion, bumpType) {
|
|
81
|
-
const
|
|
511
|
+
const version = parseVersion(currentVersion);
|
|
82
512
|
switch (bumpType) {
|
|
83
513
|
case "major":
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
514
|
+
version.major++;
|
|
515
|
+
version.minor = 0;
|
|
516
|
+
version.patch = 0;
|
|
87
517
|
break;
|
|
88
518
|
case "minor":
|
|
89
|
-
|
|
90
|
-
|
|
519
|
+
version.minor++;
|
|
520
|
+
version.patch = 0;
|
|
91
521
|
break;
|
|
92
522
|
case "patch":
|
|
93
|
-
|
|
523
|
+
version.patch++;
|
|
94
524
|
break;
|
|
95
525
|
default:
|
|
96
526
|
throw new Error(`Unknown bump type: ${bumpType}`);
|
|
97
527
|
}
|
|
98
|
-
return formatVersion(
|
|
528
|
+
return formatVersion(version);
|
|
99
529
|
}
|
|
100
|
-
function updatePackageVersion(newVersion) {
|
|
530
|
+
function updatePackageVersion(pkg, newVersion) {
|
|
101
531
|
pkg.version = newVersion;
|
|
102
|
-
fs.writeFileSync(packagePath, JSON.stringify(pkg, null, 2)
|
|
532
|
+
fs.writeFileSync(packagePath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
103
533
|
console.log(`✅ Updated package.json to version ${newVersion}`);
|
|
104
534
|
}
|
|
105
535
|
function updatePyprojectVersion(newVersion) {
|
|
@@ -107,17 +537,12 @@ function updatePyprojectVersion(newVersion) {
|
|
|
107
537
|
content = content.replace(/^version\s*=\s*"[^"]+"/m, `version = "${newVersion}"`);
|
|
108
538
|
fs.writeFileSync(pyprojectPath, content);
|
|
109
539
|
console.log(`✅ Updated docker/pyproject.toml to version ${newVersion}`);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
console.log("🔄 Running uv sync to update uv.lock...");
|
|
114
|
-
(0, child_process_1.execSync)("uv sync", { cwd: dockerDir, stdio: "inherit" });
|
|
115
|
-
console.log("✅ Updated docker/uv.lock");
|
|
540
|
+
const result = (0, exports.defaultCommandRunner)("uv", ["sync"], { cwd: path.join(projectRoot, "docker"), inherit: true });
|
|
541
|
+
if (result.status !== 0) {
|
|
542
|
+
console.error("⚠️ Warning: Failed to run uv sync; run it manually in docker/");
|
|
116
543
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
console.error("⚠️ Warning: Failed to run uv sync:", message);
|
|
120
|
-
console.error(" You may need to run 'cd docker && uv sync' manually");
|
|
544
|
+
else {
|
|
545
|
+
console.log("✅ Updated docker/uv.lock");
|
|
121
546
|
}
|
|
122
547
|
}
|
|
123
548
|
function updateAppManifestVersion(newVersion) {
|
|
@@ -126,215 +551,149 @@ function updateAppManifestVersion(newVersion) {
|
|
|
126
551
|
fs.writeFileSync(appManifestPath, content);
|
|
127
552
|
console.log(`✅ Updated docker/app-manifest.yaml to version ${newVersion}`);
|
|
128
553
|
}
|
|
129
|
-
function
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
tagName = `v${version}-${timestamp}`;
|
|
554
|
+
function writeActionsOutputs(values) {
|
|
555
|
+
const lines = Object.entries(values).map(([key, value]) => `${key}=${String(value)}\n`).join("");
|
|
556
|
+
const outputPath = process.env.GITHUB_OUTPUT;
|
|
557
|
+
if (outputPath) {
|
|
558
|
+
fs.appendFileSync(outputPath, lines);
|
|
135
559
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
catch {
|
|
144
|
-
// Tag doesn't exist, continue
|
|
145
|
-
}
|
|
146
|
-
// Create tag
|
|
147
|
-
const message = isDev
|
|
148
|
-
? `Development ${tagType} ${tagName}\n\nThis is a pre-release for testing purposes.`
|
|
149
|
-
: `Release ${tagName}`;
|
|
150
|
-
(0, child_process_1.execSync)(`git tag -a ${tagName} -m "${message}"`, { stdio: "inherit" });
|
|
151
|
-
console.log(`✅ Created git tag ${tagName}`);
|
|
152
|
-
// Push tag unless --no-push is specified
|
|
153
|
-
if (!noPush) {
|
|
154
|
-
console.log("");
|
|
155
|
-
console.log(`Pushing tag ${tagName} to origin...`);
|
|
156
|
-
try {
|
|
157
|
-
(0, child_process_1.execSync)(`git push origin ${tagName}`, { stdio: "inherit" });
|
|
158
|
-
console.log(`✅ Pushed tag ${tagName} to origin`);
|
|
159
|
-
console.log("");
|
|
160
|
-
console.log("CI/CD pipeline will now:");
|
|
161
|
-
console.log(" - Run all tests");
|
|
162
|
-
console.log(" - Build and push Docker image to ECR");
|
|
163
|
-
console.log(" - Create GitHub release");
|
|
164
|
-
if (!isDev) {
|
|
165
|
-
console.log(" - Publish to NPM (production releases only)");
|
|
166
|
-
}
|
|
167
|
-
console.log(" - Publish to GitHub Packages");
|
|
168
|
-
console.log("");
|
|
169
|
-
console.log("Monitor progress at: https://github.com/quiltdata/benchling-webhook/actions");
|
|
170
|
-
}
|
|
171
|
-
catch {
|
|
172
|
-
console.error(`❌ Failed to push tag ${tagName}`);
|
|
173
|
-
console.error("You can manually push with: git push origin " + tagName);
|
|
174
|
-
process.exit(1);
|
|
175
|
-
}
|
|
560
|
+
console.log(lines.trim());
|
|
561
|
+
}
|
|
562
|
+
function createManualTag(repository, version, isDev, noPush) {
|
|
563
|
+
const tag = isDev ? devTag(version) : productionTag(version);
|
|
564
|
+
if (!repository.isClean()) {
|
|
565
|
+
throw new Error("You have uncommitted changes");
|
|
176
566
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
567
|
+
if (repository.localTagTarget(tag)) {
|
|
568
|
+
throw new Error(`Tag ${tag} already exists locally`);
|
|
569
|
+
}
|
|
570
|
+
if (repository.remoteTag(tag)) {
|
|
571
|
+
throw new Error(`Tag ${tag} already exists on origin`);
|
|
572
|
+
}
|
|
573
|
+
const targetSha = repository.resolveCommit("HEAD");
|
|
574
|
+
repository.createAnnotatedTag(tag, annotatedTagMessage(tag), targetSha);
|
|
575
|
+
if (noPush) {
|
|
576
|
+
console.log(`✅ Created ${tag}; push later with: git push origin ${tag}`);
|
|
577
|
+
return tag;
|
|
578
|
+
}
|
|
579
|
+
const pushed = repository.pushTag(tag);
|
|
580
|
+
const remote = repository.remoteTag(tag);
|
|
581
|
+
if (!remote) {
|
|
582
|
+
throw new Error(`Failed to push tag ${tag}; the exact remote tag is absent`);
|
|
583
|
+
}
|
|
584
|
+
assertExpectedTarget(remote.targetSha, normalizeSha(targetSha, "HEAD"), `Remote tag ${tag}`);
|
|
585
|
+
console.log(`✅ ${pushed ? "Pushed" : "Verified concurrent push of"} tag ${tag}`);
|
|
586
|
+
return tag;
|
|
587
|
+
}
|
|
588
|
+
function printHelp(pkg) {
|
|
589
|
+
console.log(`Current version: ${pkg.version}\n`);
|
|
590
|
+
console.log("Usage: npm run version -- [command] [options]\n");
|
|
591
|
+
console.log("Version commands: major, minor, patch, sync");
|
|
592
|
+
console.log("Release commands: tag [dev] [--no-push], auto-tag, release-context");
|
|
593
|
+
console.log("Verification command: verify-clean-git");
|
|
594
|
+
}
|
|
595
|
+
function main(args = process.argv.slice(2)) {
|
|
596
|
+
const pkg = readPackage();
|
|
597
|
+
const repository = new GitReleaseRepository(projectRoot);
|
|
598
|
+
const command = args[0];
|
|
599
|
+
try {
|
|
600
|
+
if (!command) {
|
|
601
|
+
const tomlVersion = readPyprojectVersion();
|
|
602
|
+
const yamlVersion = readAppManifestVersion();
|
|
603
|
+
console.log("Version files:");
|
|
604
|
+
console.log(` package.json: ${pkg.version}`);
|
|
605
|
+
console.log(` docker/pyproject.toml: ${tomlVersion}`);
|
|
606
|
+
console.log(` docker/app-manifest.yaml: ${yamlVersion}`);
|
|
607
|
+
console.log(pkg.version === tomlVersion && pkg.version === yamlVersion
|
|
608
|
+
? "\n✅ All versions are in sync"
|
|
609
|
+
: "\n⚠️ Versions are out of sync! Run npm run version -- sync to fix.");
|
|
610
|
+
return 0;
|
|
196
611
|
}
|
|
197
|
-
|
|
198
|
-
|
|
612
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
613
|
+
printHelp(pkg);
|
|
614
|
+
return 0;
|
|
199
615
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
console.log("Current version:", pkg.version);
|
|
205
|
-
console.log("");
|
|
206
|
-
console.log("Usage: node bin/version.ts [command] [options]");
|
|
207
|
-
console.log("");
|
|
208
|
-
console.log("Version Management Commands:");
|
|
209
|
-
console.log(" (no args) - Display all three version files");
|
|
210
|
-
console.log(" major - Bump major version (1.0.0 -> 2.0.0)");
|
|
211
|
-
console.log(" minor - Bump minor version (0.4.7 -> 0.5.0)");
|
|
212
|
-
console.log(" patch - Bump patch version (0.4.7 -> 0.4.8)");
|
|
213
|
-
console.log(" sync - Force TOML and YAML to match JSON version");
|
|
214
|
-
console.log("");
|
|
215
|
-
console.log("Release Tagging Commands:");
|
|
216
|
-
console.log(" tag - Create and push production release tag");
|
|
217
|
-
console.log(" tag dev - Create and push dev release tag with timestamp");
|
|
218
|
-
console.log(" verify-clean-git - Check for uncommitted changes (pre-flight check)");
|
|
219
|
-
console.log("");
|
|
220
|
-
console.log("Options:");
|
|
221
|
-
console.log(" --no-push - Create tag but do not push to origin (tag command only)");
|
|
222
|
-
console.log("");
|
|
223
|
-
console.log("This script manages version numbers in:");
|
|
224
|
-
console.log(" - package.json");
|
|
225
|
-
console.log(" - docker/pyproject.toml");
|
|
226
|
-
console.log(" - docker/app-manifest.yaml");
|
|
227
|
-
console.log(" - docker/uv.lock (via uv sync)");
|
|
228
|
-
console.log("");
|
|
229
|
-
console.log("Examples:");
|
|
230
|
-
console.log(" npm run version # Show versions");
|
|
231
|
-
console.log(" npm run version patch # Bump patch version");
|
|
232
|
-
console.log(" npm run version:tag # Create production release tag");
|
|
233
|
-
console.log(" npm run version:tag:dev # Create dev release tag");
|
|
234
|
-
process.exit(0);
|
|
235
|
-
}
|
|
236
|
-
const bumpType = args[0];
|
|
237
|
-
// Verify clean git command - check for uncommitted changes
|
|
238
|
-
if (bumpType === "verify-clean-git") {
|
|
239
|
-
try {
|
|
240
|
-
(0, child_process_1.execSync)("git diff-index --quiet HEAD --", { stdio: "ignore" });
|
|
616
|
+
if (command === "verify-clean-git") {
|
|
617
|
+
if (!repository.isClean()) {
|
|
618
|
+
throw new Error("You have uncommitted changes");
|
|
619
|
+
}
|
|
241
620
|
console.log("✅ Git working directory is clean");
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
catch {
|
|
245
|
-
console.error("❌ You have uncommitted changes");
|
|
246
|
-
console.error(" Commit or stash your changes before creating a release");
|
|
247
|
-
process.exit(1);
|
|
621
|
+
return 0;
|
|
248
622
|
}
|
|
249
|
-
|
|
250
|
-
// Sync command - force TOML and YAML to match JSON
|
|
251
|
-
if (bumpType === "sync") {
|
|
252
|
-
const jsonVersion = pkg.version;
|
|
253
|
-
const tomlVersion = readPyprojectVersion();
|
|
254
|
-
const yamlVersion = readAppManifestVersion();
|
|
255
|
-
console.log("Syncing versions to match package.json:");
|
|
256
|
-
console.log(` package.json: ${jsonVersion}`);
|
|
257
|
-
console.log(` docker/pyproject.toml: ${tomlVersion} -> ${jsonVersion}`);
|
|
258
|
-
console.log(` docker/app-manifest.yaml: ${yamlVersion} -> ${jsonVersion}`);
|
|
259
|
-
console.log("");
|
|
260
|
-
try {
|
|
261
|
-
updatePyprojectVersion(jsonVersion);
|
|
262
|
-
updateAppManifestVersion(jsonVersion);
|
|
263
|
-
// Check if version files have changes
|
|
264
|
-
let hasChanges = false;
|
|
623
|
+
if (command === "auto-tag") {
|
|
265
624
|
try {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
625
|
+
const result = runAutomaticRelease({
|
|
626
|
+
repository,
|
|
627
|
+
currentVersion: pkg.version,
|
|
628
|
+
beforeSha: process.env.GITHUB_EVENT_BEFORE,
|
|
629
|
+
githubSha: process.env.GITHUB_SHA ?? "",
|
|
630
|
+
runProductionGate: () => runProductionGate(repository),
|
|
631
|
+
});
|
|
632
|
+
writeActionsOutputs({
|
|
633
|
+
outcome: result.outcome,
|
|
634
|
+
release_required: result.releaseRequired,
|
|
635
|
+
verified: result.verified,
|
|
636
|
+
tag: result.tag,
|
|
637
|
+
target_sha: result.targetSha,
|
|
638
|
+
});
|
|
639
|
+
return 0;
|
|
270
640
|
}
|
|
271
|
-
|
|
272
|
-
(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
641
|
+
catch (error) {
|
|
642
|
+
writeActionsOutputs({
|
|
643
|
+
outcome: "failure",
|
|
644
|
+
release_required: false,
|
|
645
|
+
verified: false,
|
|
646
|
+
tag: "",
|
|
647
|
+
target_sha: "",
|
|
648
|
+
});
|
|
649
|
+
throw error;
|
|
278
650
|
}
|
|
279
651
|
}
|
|
280
|
-
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
652
|
+
if (command === "release-context") {
|
|
653
|
+
const tag = args[1] ?? process.env.RELEASE_TAG ?? "";
|
|
654
|
+
const context = validateReleaseContext(repository, tag, pkg.version, process.env.GITHUB_SHA ?? "");
|
|
655
|
+
writeActionsOutputs({
|
|
656
|
+
verified: context.verified,
|
|
657
|
+
tag: context.tag,
|
|
658
|
+
version: context.version,
|
|
659
|
+
base_version: context.baseVersion,
|
|
660
|
+
target_sha: context.targetSha,
|
|
661
|
+
is_prerelease: context.isPrerelease,
|
|
662
|
+
});
|
|
663
|
+
return 0;
|
|
284
664
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
665
|
+
if (command === "sync") {
|
|
666
|
+
updatePyprojectVersion(pkg.version);
|
|
667
|
+
updateAppManifestVersion(pkg.version);
|
|
668
|
+
const diff = (0, exports.defaultCommandRunner)("git", ["diff", "--quiet", "docker/pyproject.toml", "docker/app-manifest.yaml", "docker/uv.lock"], { cwd: projectRoot });
|
|
669
|
+
if (diff.status !== 0) {
|
|
670
|
+
runChecked(exports.defaultCommandRunner, "git", ["add", "docker/pyproject.toml", "docker/app-manifest.yaml", "docker/uv.lock"], projectRoot, true);
|
|
671
|
+
runChecked(exports.defaultCommandRunner, "git", ["commit", "-m", `chore: sync versions to ${pkg.version}`], projectRoot, true);
|
|
672
|
+
}
|
|
673
|
+
return 0;
|
|
292
674
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
process.exit(1);
|
|
675
|
+
if (command === "tag") {
|
|
676
|
+
createManualTag(repository, pkg.version, args.includes("dev"), args.includes("--no-push"));
|
|
677
|
+
return 0;
|
|
297
678
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
console.log(
|
|
303
|
-
|
|
304
|
-
process.exit(0);
|
|
305
|
-
}
|
|
306
|
-
// Check for uncommitted changes (for version bump commands)
|
|
307
|
-
try {
|
|
308
|
-
(0, child_process_1.execSync)("git diff-index --quiet HEAD --", { stdio: "ignore" });
|
|
309
|
-
}
|
|
310
|
-
catch {
|
|
311
|
-
console.error("❌ You have uncommitted changes");
|
|
312
|
-
console.error(" Commit or stash your changes before bumping version");
|
|
313
|
-
process.exit(1);
|
|
314
|
-
}
|
|
315
|
-
try {
|
|
316
|
-
const currentVersion = pkg.version;
|
|
317
|
-
const newVersion = bumpVersion(currentVersion, bumpType);
|
|
318
|
-
console.log(`Bumping version: ${currentVersion} -> ${newVersion}`);
|
|
319
|
-
console.log("");
|
|
320
|
-
// Update all version files
|
|
321
|
-
updatePackageVersion(newVersion);
|
|
679
|
+
if (!repository.isClean()) {
|
|
680
|
+
throw new Error("You have uncommitted changes");
|
|
681
|
+
}
|
|
682
|
+
const newVersion = bumpVersion(pkg.version, command);
|
|
683
|
+
console.log(`Bumping version: ${pkg.version} -> ${newVersion}`);
|
|
684
|
+
updatePackageVersion(pkg, newVersion);
|
|
322
685
|
updatePyprojectVersion(newVersion);
|
|
323
686
|
updateAppManifestVersion(newVersion);
|
|
324
|
-
|
|
325
|
-
(
|
|
326
|
-
|
|
327
|
-
console.log("✅ Committed version change");
|
|
328
|
-
console.log("");
|
|
329
|
-
console.log("Next steps:");
|
|
330
|
-
console.log(" 1. Push changes: git push");
|
|
331
|
-
console.log(" 2. Create release tag: npm run version:tag (or npm run version:tag:dev for dev release)");
|
|
687
|
+
runChecked(exports.defaultCommandRunner, "git", ["add", "package.json", "docker/pyproject.toml", "docker/app-manifest.yaml", "docker/uv.lock"], projectRoot, true);
|
|
688
|
+
runChecked(exports.defaultCommandRunner, "git", ["commit", "-m", `chore: bump version to ${newVersion}`], projectRoot, true);
|
|
689
|
+
return 0;
|
|
332
690
|
}
|
|
333
691
|
catch (error) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
process.exit(1);
|
|
692
|
+
console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
|
|
693
|
+
return 1;
|
|
337
694
|
}
|
|
338
695
|
}
|
|
339
|
-
main
|
|
696
|
+
if (require.main === module) {
|
|
697
|
+
process.exitCode = main();
|
|
698
|
+
}
|
|
340
699
|
//# sourceMappingURL=version.js.map
|