@elmeragroup/internal 0.2.0-canary.0 → 0.2.0-canary.2
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/dist/release.mjs +211 -197
- package/package.json +8 -8
package/dist/release.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { Duration, Effect, Result, Schedule, Schema } from "effect";
|
|
4
|
-
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
@@ -50,157 +50,6 @@ function readJson(path, schema) {
|
|
|
50
50
|
return decodeJson(readFileSync(path, "utf8"), schema, path);
|
|
51
51
|
}
|
|
52
52
|
//#endregion
|
|
53
|
-
//#region ../release/dist/version.js
|
|
54
|
-
const numericIdentifier = /^(?:0|[1-9]\d*)$/;
|
|
55
|
-
const canaryMarker = "-canary.";
|
|
56
|
-
function isNumericIdentifier(value) {
|
|
57
|
-
return numericIdentifier.test(value);
|
|
58
|
-
}
|
|
59
|
-
function stableComponents(version) {
|
|
60
|
-
const [major, minor, patch, ...rest] = version.split(".");
|
|
61
|
-
if (major === void 0 || minor === void 0 || patch === void 0 || rest.length !== 0) return;
|
|
62
|
-
if (!isNumericIdentifier(major) || !isNumericIdentifier(minor) || !isNumericIdentifier(patch)) return;
|
|
63
|
-
return [
|
|
64
|
-
major,
|
|
65
|
-
minor,
|
|
66
|
-
patch
|
|
67
|
-
];
|
|
68
|
-
}
|
|
69
|
-
function canaryComponents(version) {
|
|
70
|
-
const marker = version.indexOf(canaryMarker);
|
|
71
|
-
if (marker === -1) return void 0;
|
|
72
|
-
const base = version.slice(0, marker);
|
|
73
|
-
const suffix = version.slice(marker + 8);
|
|
74
|
-
if (stableComponents(base) === void 0 || !isNumericIdentifier(suffix)) return void 0;
|
|
75
|
-
return {
|
|
76
|
-
base,
|
|
77
|
-
suffix
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
function isStableReleaseVersion(version) {
|
|
81
|
-
return stableComponents(version) !== void 0;
|
|
82
|
-
}
|
|
83
|
-
function isCanaryReleaseVersion(version) {
|
|
84
|
-
return canaryComponents(version) !== void 0;
|
|
85
|
-
}
|
|
86
|
-
function isSupportedReleaseVersion(version) {
|
|
87
|
-
return isStableReleaseVersion(version) || isCanaryReleaseVersion(version);
|
|
88
|
-
}
|
|
89
|
-
function assertReleaseVersion(version) {
|
|
90
|
-
if (!isSupportedReleaseVersion(version)) throw new Error(`Unsupported release version ${version}; expected x.y.z or x.y.z-canary.N without leading zeros`);
|
|
91
|
-
return version;
|
|
92
|
-
}
|
|
93
|
-
function assertCanaryReleaseVersion(version) {
|
|
94
|
-
assertReleaseVersion(version);
|
|
95
|
-
if (!isCanaryReleaseVersion(version)) throw new Error(`Canary publication requires x.y.z-canary.N; received ${version}`);
|
|
96
|
-
return version;
|
|
97
|
-
}
|
|
98
|
-
function assertStableReleaseVersion(version) {
|
|
99
|
-
if (!isStableReleaseVersion(version)) throw new Error(`Expected a stable version; received ${version}`);
|
|
100
|
-
return version;
|
|
101
|
-
}
|
|
102
|
-
function parseStableVersion(version) {
|
|
103
|
-
const components = stableComponents(version);
|
|
104
|
-
if (components === void 0) throw new Error(`Expected a stable version; received ${version}`);
|
|
105
|
-
const [major, minor, patch] = components;
|
|
106
|
-
return {
|
|
107
|
-
major: BigInt(major),
|
|
108
|
-
minor: BigInt(minor),
|
|
109
|
-
patch: BigInt(patch)
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
function parseCanaryVersion(version) {
|
|
113
|
-
const components = canaryComponents(version);
|
|
114
|
-
if (components === void 0) throw new Error(`Canary publication requires x.y.z-canary.N; received ${version}`);
|
|
115
|
-
return {
|
|
116
|
-
base: components.base,
|
|
117
|
-
n: BigInt(components.suffix)
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
function formatStableVersion(version) {
|
|
121
|
-
return `${version.major}.${version.minor}.${version.patch}`;
|
|
122
|
-
}
|
|
123
|
-
function formatCanaryVersion(version) {
|
|
124
|
-
return `${version.base}-canary.${version.n}`;
|
|
125
|
-
}
|
|
126
|
-
function compareParsedStableVersions(left, right) {
|
|
127
|
-
if (left.major !== right.major) return left.major > right.major ? 1 : -1;
|
|
128
|
-
if (left.minor !== right.minor) return left.minor > right.minor ? 1 : -1;
|
|
129
|
-
if (left.patch !== right.patch) return left.patch > right.patch ? 1 : -1;
|
|
130
|
-
return 0;
|
|
131
|
-
}
|
|
132
|
-
function compareStableVersions(left, right) {
|
|
133
|
-
return compareParsedStableVersions(parseStableVersion(left), parseStableVersion(right));
|
|
134
|
-
}
|
|
135
|
-
function nextPatchVersion(version) {
|
|
136
|
-
const parsed = parseStableVersion(version);
|
|
137
|
-
return formatStableVersion({
|
|
138
|
-
...parsed,
|
|
139
|
-
patch: parsed.patch + 1n
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
function compareCanaryVersions(left, right) {
|
|
143
|
-
const parsedLeft = parseCanaryVersion(left);
|
|
144
|
-
const parsedRight = parseCanaryVersion(right);
|
|
145
|
-
const baseOrder = compareStableVersions(parsedLeft.base, parsedRight.base);
|
|
146
|
-
if (baseOrder !== 0) return baseOrder;
|
|
147
|
-
return parsedLeft.n === parsedRight.n ? 0 : parsedLeft.n > parsedRight.n ? 1 : -1;
|
|
148
|
-
}
|
|
149
|
-
//#endregion
|
|
150
|
-
//#region ../release/dist/intent.js
|
|
151
|
-
const releaseRecordOwner = "elmera-release";
|
|
152
|
-
/** Asset name of the recorded archive on its GitHub draft release. */
|
|
153
|
-
const releaseArchiveName = "release.tgz";
|
|
154
|
-
const canaryRecordPrefix = "canary-";
|
|
155
|
-
const IntentDocument = Schema.Struct({
|
|
156
|
-
schema: Schema.Literal(1),
|
|
157
|
-
owner: Schema.optionalKey(Schema.Literal(releaseRecordOwner)),
|
|
158
|
-
channel: Schema.Literals(["stable", "canary"]),
|
|
159
|
-
version: Schema.String,
|
|
160
|
-
commit: Schema.String
|
|
161
|
-
});
|
|
162
|
-
function isCommit(value) {
|
|
163
|
-
return /^[a-f0-9]{40}$/.test(value);
|
|
164
|
-
}
|
|
165
|
-
function assertCommit(commit) {
|
|
166
|
-
if (!isCommit(commit)) throw new Error(`Expected a full commit SHA; received ${commit}`);
|
|
167
|
-
return commit;
|
|
168
|
-
}
|
|
169
|
-
/** Record tag for a canary release of `commit`. */
|
|
170
|
-
function canaryRecordTag(commit) {
|
|
171
|
-
return `${canaryRecordPrefix}${commit}`;
|
|
172
|
-
}
|
|
173
|
-
function releaseTag(intent) {
|
|
174
|
-
return intent.channel === "stable" ? `v${intent.version}` : canaryRecordTag(intent.commit);
|
|
175
|
-
}
|
|
176
|
-
function isReleaseTag(tag) {
|
|
177
|
-
if (tag.startsWith("v")) return isStableReleaseVersion(tag.slice(1));
|
|
178
|
-
return tag.startsWith(canaryRecordPrefix) && isCommit(tag.slice(7));
|
|
179
|
-
}
|
|
180
|
-
function assertReleaseTag(tag) {
|
|
181
|
-
if (!isReleaseTag(tag)) throw new Error("Expected a stable or canary release record tag");
|
|
182
|
-
return tag;
|
|
183
|
-
}
|
|
184
|
-
function parseIntent(text) {
|
|
185
|
-
const value = decodeJson(text, IntentDocument, "release intent");
|
|
186
|
-
if (value.channel === "stable") assertStableReleaseVersion(value.version);
|
|
187
|
-
else assertCanaryReleaseVersion(value.version);
|
|
188
|
-
return {
|
|
189
|
-
channel: value.channel,
|
|
190
|
-
version: value.version,
|
|
191
|
-
commit: assertCommit(value.commit)
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
function serializeIntent(intent) {
|
|
195
|
-
return JSON.stringify({
|
|
196
|
-
schema: 1,
|
|
197
|
-
owner: releaseRecordOwner,
|
|
198
|
-
channel: intent.channel,
|
|
199
|
-
version: intent.version,
|
|
200
|
-
commit: intent.commit
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
//#endregion
|
|
204
53
|
//#region ../release/dist/archive.js
|
|
205
54
|
const PackedManifest = Schema.Struct({
|
|
206
55
|
name: Schema.String,
|
|
@@ -210,6 +59,8 @@ const PackedManifest = Schema.Struct({
|
|
|
210
59
|
channel: Schema.Literals(["canary", "stable"])
|
|
211
60
|
})
|
|
212
61
|
});
|
|
62
|
+
/** Scratch filename inside the scoped temp directory; unrelated to the record's asset name. */
|
|
63
|
+
const scratchArchiveName = "archive.tgz";
|
|
213
64
|
/** Temporary directory removed when the surrounding Effect scope closes. Process kill is not covered. */
|
|
214
65
|
function scratchDirectory() {
|
|
215
66
|
return Effect.acquireRelease(lift(() => mkdtempSync(resolve(tmpdir(), "elmera-release-"))), (directory) => Effect.sync(() => rmSync(directory, {
|
|
@@ -225,7 +76,7 @@ function verifyReleaseArchive(intent, bytes, packageName) {
|
|
|
225
76
|
return Effect.gen(function* () {
|
|
226
77
|
const directory = yield* scratchDirectory();
|
|
227
78
|
return yield* lift(() => {
|
|
228
|
-
const archive = resolve(directory,
|
|
79
|
+
const archive = resolve(directory, scratchArchiveName);
|
|
229
80
|
writeFileSync(archive, bytes);
|
|
230
81
|
const manifest = decodeJson(execFileSync("tar", [
|
|
231
82
|
"-xOzf",
|
|
@@ -356,6 +207,103 @@ function createGitHubClient(environment) {
|
|
|
356
207
|
};
|
|
357
208
|
}
|
|
358
209
|
//#endregion
|
|
210
|
+
//#region ../release/dist/version.js
|
|
211
|
+
const numericIdentifier = /^(?:0|[1-9]\d*)$/;
|
|
212
|
+
const canaryMarker = "-canary.";
|
|
213
|
+
function isNumericIdentifier(value) {
|
|
214
|
+
return numericIdentifier.test(value);
|
|
215
|
+
}
|
|
216
|
+
function stableComponents(version) {
|
|
217
|
+
const [major, minor, patch, ...rest] = version.split(".");
|
|
218
|
+
if (major === void 0 || minor === void 0 || patch === void 0 || rest.length !== 0) return;
|
|
219
|
+
if (!isNumericIdentifier(major) || !isNumericIdentifier(minor) || !isNumericIdentifier(patch)) return;
|
|
220
|
+
return [
|
|
221
|
+
major,
|
|
222
|
+
minor,
|
|
223
|
+
patch
|
|
224
|
+
];
|
|
225
|
+
}
|
|
226
|
+
function canaryComponents(version) {
|
|
227
|
+
const marker = version.indexOf(canaryMarker);
|
|
228
|
+
if (marker === -1) return void 0;
|
|
229
|
+
const base = version.slice(0, marker);
|
|
230
|
+
const suffix = version.slice(marker + 8);
|
|
231
|
+
if (stableComponents(base) === void 0 || !isNumericIdentifier(suffix)) return void 0;
|
|
232
|
+
return {
|
|
233
|
+
base,
|
|
234
|
+
suffix
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function isStableReleaseVersion(version) {
|
|
238
|
+
return stableComponents(version) !== void 0;
|
|
239
|
+
}
|
|
240
|
+
function isCanaryReleaseVersion(version) {
|
|
241
|
+
return canaryComponents(version) !== void 0;
|
|
242
|
+
}
|
|
243
|
+
function isSupportedReleaseVersion(version) {
|
|
244
|
+
return isStableReleaseVersion(version) || isCanaryReleaseVersion(version);
|
|
245
|
+
}
|
|
246
|
+
function assertReleaseVersion(version) {
|
|
247
|
+
if (!isSupportedReleaseVersion(version)) throw new Error(`Unsupported release version ${version}; expected x.y.z or x.y.z-canary.N without leading zeros`);
|
|
248
|
+
return version;
|
|
249
|
+
}
|
|
250
|
+
function assertCanaryReleaseVersion(version) {
|
|
251
|
+
assertReleaseVersion(version);
|
|
252
|
+
if (!isCanaryReleaseVersion(version)) throw new Error(`Canary publication requires x.y.z-canary.N; received ${version}`);
|
|
253
|
+
return version;
|
|
254
|
+
}
|
|
255
|
+
function assertStableReleaseVersion(version) {
|
|
256
|
+
if (!isStableReleaseVersion(version)) throw new Error(`Expected a stable version; received ${version}`);
|
|
257
|
+
return version;
|
|
258
|
+
}
|
|
259
|
+
function parseStableVersion(version) {
|
|
260
|
+
const components = stableComponents(version);
|
|
261
|
+
if (components === void 0) throw new Error(`Expected a stable version; received ${version}`);
|
|
262
|
+
const [major, minor, patch] = components;
|
|
263
|
+
return {
|
|
264
|
+
major: BigInt(major),
|
|
265
|
+
minor: BigInt(minor),
|
|
266
|
+
patch: BigInt(patch)
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function parseCanaryVersion(version) {
|
|
270
|
+
const components = canaryComponents(version);
|
|
271
|
+
if (components === void 0) throw new Error(`Canary publication requires x.y.z-canary.N; received ${version}`);
|
|
272
|
+
return {
|
|
273
|
+
base: components.base,
|
|
274
|
+
n: BigInt(components.suffix)
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function formatStableVersion(version) {
|
|
278
|
+
return `${version.major}.${version.minor}.${version.patch}`;
|
|
279
|
+
}
|
|
280
|
+
function formatCanaryVersion(version) {
|
|
281
|
+
return `${version.base}-canary.${version.n}`;
|
|
282
|
+
}
|
|
283
|
+
function compareParsedStableVersions(left, right) {
|
|
284
|
+
if (left.major !== right.major) return left.major > right.major ? 1 : -1;
|
|
285
|
+
if (left.minor !== right.minor) return left.minor > right.minor ? 1 : -1;
|
|
286
|
+
if (left.patch !== right.patch) return left.patch > right.patch ? 1 : -1;
|
|
287
|
+
return 0;
|
|
288
|
+
}
|
|
289
|
+
function compareStableVersions(left, right) {
|
|
290
|
+
return compareParsedStableVersions(parseStableVersion(left), parseStableVersion(right));
|
|
291
|
+
}
|
|
292
|
+
function nextPatchVersion(version) {
|
|
293
|
+
const parsed = parseStableVersion(version);
|
|
294
|
+
return formatStableVersion({
|
|
295
|
+
...parsed,
|
|
296
|
+
patch: parsed.patch + 1n
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
function compareCanaryVersions(left, right) {
|
|
300
|
+
const parsedLeft = parseCanaryVersion(left);
|
|
301
|
+
const parsedRight = parseCanaryVersion(right);
|
|
302
|
+
const baseOrder = compareStableVersions(parsedLeft.base, parsedRight.base);
|
|
303
|
+
if (baseOrder !== 0) return baseOrder;
|
|
304
|
+
return parsedLeft.n === parsedRight.n ? 0 : parsedLeft.n > parsedRight.n ? 1 : -1;
|
|
305
|
+
}
|
|
306
|
+
//#endregion
|
|
359
307
|
//#region ../release/dist/gate.js
|
|
360
308
|
/** Reads the checked-out manifest's version and requires it to be a stable release version. */
|
|
361
309
|
function readStableVersion(packageDirectory) {
|
|
@@ -393,6 +341,15 @@ function createStableReleaseGate(client, root, packageDirectory, trackedBranch)
|
|
|
393
341
|
});
|
|
394
342
|
}
|
|
395
343
|
//#endregion
|
|
344
|
+
//#region ../release/dist/intent.js
|
|
345
|
+
function isCommit(value) {
|
|
346
|
+
return /^[a-f0-9]{40}$/.test(value);
|
|
347
|
+
}
|
|
348
|
+
function assertCommit(commit) {
|
|
349
|
+
if (!isCommit(commit)) throw new Error(`Expected a full commit SHA; received ${commit}`);
|
|
350
|
+
return commit;
|
|
351
|
+
}
|
|
352
|
+
//#endregion
|
|
396
353
|
//#region ../release/dist/git.js
|
|
397
354
|
function git(cwd, args) {
|
|
398
355
|
return execFileSync("git", args, {
|
|
@@ -535,25 +492,23 @@ function trackedBranchOf(baseBranch) {
|
|
|
535
492
|
/**
|
|
536
493
|
* Asks Changesets which releases the pending changesets in `checkoutRoot` would produce.
|
|
537
494
|
*
|
|
538
|
-
*
|
|
539
|
-
* `
|
|
540
|
-
*
|
|
541
|
-
* `
|
|
542
|
-
*
|
|
543
|
-
* name does not resolve. Passing `--since` is not a substitute: it also filters out every changeset
|
|
544
|
-
* added before that ref, which would silently plan the wrong version.
|
|
495
|
+
* The load-bearing property of the pinned CLI is that it resolves the configured `baseBranch`
|
|
496
|
+
* through `git merge-base`, which is why `.changeset/config.json` names `origin/<branch>` — the
|
|
497
|
+
* publisher runs in a detached checkout of one commit, where a bare branch name does not resolve.
|
|
498
|
+
* Passing `--since` is not a substitute: it also filters out every changeset added before that ref,
|
|
499
|
+
* which would silently plan the wrong version.
|
|
545
500
|
*
|
|
546
501
|
* The CLI binary is resolved from the consuming checkout, never from this package's location.
|
|
547
502
|
*/
|
|
548
503
|
function readReleasePlan(checkoutRoot) {
|
|
549
504
|
const planPath = resolve(checkoutRoot, planFileName);
|
|
550
|
-
mkdirSync(
|
|
505
|
+
mkdirSync(dirname(planPath), { recursive: true });
|
|
551
506
|
try {
|
|
552
507
|
execFileSync(process.execPath, [
|
|
553
508
|
changesetBin(checkoutRoot),
|
|
554
509
|
"status",
|
|
555
510
|
"--output",
|
|
556
|
-
|
|
511
|
+
planPath
|
|
557
512
|
], {
|
|
558
513
|
cwd: checkoutRoot,
|
|
559
514
|
stdio: "pipe"
|
|
@@ -637,19 +592,25 @@ function canarySupersession(commit, base, registry, isAncestor, recordedVersion)
|
|
|
637
592
|
if (highestStable !== void 0 && compareStableVersions(highestStable, base) >= 0) return "stable-superseded";
|
|
638
593
|
return "owned";
|
|
639
594
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
595
|
+
const skipReasons = {
|
|
596
|
+
"canary-superseded": "Skipping a commit superseded by a published canary",
|
|
597
|
+
"stable-superseded": "Skipping a commit superseded by a stable release",
|
|
598
|
+
"regressed-base": "Skipping a commit superseded by a canary on a newer base"
|
|
599
|
+
};
|
|
600
|
+
function skip(kind) {
|
|
601
|
+
return {
|
|
602
|
+
skip: kind,
|
|
603
|
+
reason: skipReasons[kind]
|
|
604
|
+
};
|
|
644
605
|
}
|
|
645
606
|
/**
|
|
646
|
-
*
|
|
647
|
-
*
|
|
607
|
+
* Numbers the next canary for `base`, or reports `undefined` when a published or reserved canary
|
|
608
|
+
* already belongs to a newer base. A regressed base skips; it never blocks publication.
|
|
648
609
|
*/
|
|
649
|
-
function
|
|
610
|
+
function nextCanaryVersion(base, taken) {
|
|
650
611
|
parseStableVersion(base);
|
|
651
612
|
let highest = -1n;
|
|
652
|
-
for (const version of
|
|
613
|
+
for (const version of taken) {
|
|
653
614
|
if (!isCanaryReleaseVersion(version)) continue;
|
|
654
615
|
const candidate = parseCanaryVersion(version);
|
|
655
616
|
if (compareStableVersions(candidate.base, base) > 0) return void 0;
|
|
@@ -660,6 +621,19 @@ function allocateCanary(base, versions) {
|
|
|
660
621
|
n: highest + 1n
|
|
661
622
|
});
|
|
662
623
|
}
|
|
624
|
+
/**
|
|
625
|
+
* Decides the canary for a checked main commit. Ancestry and the planned base settle whether the
|
|
626
|
+
* commit still owns the channel; published and reserved versions together settle the number, so a
|
|
627
|
+
* reservation is taken even when npm has not seen it yet.
|
|
628
|
+
*/
|
|
629
|
+
function decideCanary(target, registry, reserved, isAncestor) {
|
|
630
|
+
if (compareStableVersions(target.base, target.current) <= 0) throw new Error("The planned version must advance the stable version");
|
|
631
|
+
const status = canarySupersession(target.commit, target.base, registry, isAncestor);
|
|
632
|
+
if (status !== "owned") return skip(status);
|
|
633
|
+
const version = nextCanaryVersion(target.base, [...registry.versions.keys(), ...reserved]);
|
|
634
|
+
if (version === void 0) return skip("regressed-base");
|
|
635
|
+
return { cut: version };
|
|
636
|
+
}
|
|
663
637
|
/** Whether npm already carries this exact archive; a same-version mismatch is fatal. */
|
|
664
638
|
function npmIdentity(release, registry) {
|
|
665
639
|
const existing = registry.versions.get(release.version);
|
|
@@ -762,7 +736,52 @@ function publishVerifiedRelease(release, deps) {
|
|
|
762
736
|
});
|
|
763
737
|
}
|
|
764
738
|
//#endregion
|
|
765
|
-
//#region ../release/dist/
|
|
739
|
+
//#region ../release/dist/record.js
|
|
740
|
+
const releaseRecordOwner = "elmera-release";
|
|
741
|
+
/** Asset name of the recorded archive on its GitHub draft release. */
|
|
742
|
+
const releaseArchiveName = "release.tgz";
|
|
743
|
+
const canaryRecordPrefix = "canary-";
|
|
744
|
+
const IntentDocument = Schema.Struct({
|
|
745
|
+
schema: Schema.Literal(1),
|
|
746
|
+
owner: Schema.optionalKey(Schema.Literal(releaseRecordOwner)),
|
|
747
|
+
channel: Schema.Literals(["stable", "canary"]),
|
|
748
|
+
version: Schema.String,
|
|
749
|
+
commit: Schema.String
|
|
750
|
+
});
|
|
751
|
+
/** Record tag for a canary release of `commit`. */
|
|
752
|
+
function canaryRecordTag(commit) {
|
|
753
|
+
return `${canaryRecordPrefix}${commit}`;
|
|
754
|
+
}
|
|
755
|
+
function releaseTag(intent) {
|
|
756
|
+
return intent.channel === "stable" ? `v${intent.version}` : canaryRecordTag(intent.commit);
|
|
757
|
+
}
|
|
758
|
+
function isReleaseTag(tag) {
|
|
759
|
+
if (tag.startsWith("v")) return isStableReleaseVersion(tag.slice(1));
|
|
760
|
+
return tag.startsWith(canaryRecordPrefix) && isCommit(tag.slice(7));
|
|
761
|
+
}
|
|
762
|
+
function assertReleaseTag(tag) {
|
|
763
|
+
if (!isReleaseTag(tag)) throw new Error("Expected a stable or canary release record tag");
|
|
764
|
+
return tag;
|
|
765
|
+
}
|
|
766
|
+
function parseIntent(text) {
|
|
767
|
+
const value = decodeJson(text, IntentDocument, "release intent");
|
|
768
|
+
if (value.channel === "stable") assertStableReleaseVersion(value.version);
|
|
769
|
+
else assertCanaryReleaseVersion(value.version);
|
|
770
|
+
return {
|
|
771
|
+
channel: value.channel,
|
|
772
|
+
version: value.version,
|
|
773
|
+
commit: assertCommit(value.commit)
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
function serializeIntent(intent) {
|
|
777
|
+
return JSON.stringify({
|
|
778
|
+
schema: 1,
|
|
779
|
+
owner: releaseRecordOwner,
|
|
780
|
+
channel: intent.channel,
|
|
781
|
+
version: intent.version,
|
|
782
|
+
commit: intent.commit
|
|
783
|
+
});
|
|
784
|
+
}
|
|
766
785
|
const IntentProbe = Schema.Struct({
|
|
767
786
|
schema: Schema.optionalKey(Schema.Json),
|
|
768
787
|
owner: Schema.optionalKey(Schema.Json)
|
|
@@ -782,6 +801,10 @@ function intentMatchingTag(body, tag) {
|
|
|
782
801
|
function clearlyForeignIdentity(parsed) {
|
|
783
802
|
return parsed !== void 0 && parsed.owner !== void 0 && parsed.owner !== "elmera-release";
|
|
784
803
|
}
|
|
804
|
+
/**
|
|
805
|
+
* Ownership is recognized before intent validation (ADR 0002). An owned or legacy record with an
|
|
806
|
+
* unreadable body throws so it is reported damaged rather than skipped as foreign.
|
|
807
|
+
*/
|
|
785
808
|
function classifyReleaseRecord(tag, body, assetNames) {
|
|
786
809
|
if (!isReleaseTag(tag)) return { kind: "ignored" };
|
|
787
810
|
const parsed = tryParseBody(body);
|
|
@@ -799,17 +822,6 @@ function classifyReleaseRecord(tag, body, assetNames) {
|
|
|
799
822
|
};
|
|
800
823
|
return { kind: "foreign" };
|
|
801
824
|
}
|
|
802
|
-
//#endregion
|
|
803
|
-
//#region ../release/dist/store.js
|
|
804
|
-
function assertSameIntent(saved, intended) {
|
|
805
|
-
if (saved.commit !== intended.commit || saved.version !== intended.version || saved.channel !== intended.channel) throw new Error("Release intent differs from the saved release");
|
|
806
|
-
}
|
|
807
|
-
function releaseBodyText(value) {
|
|
808
|
-
return value.body ?? "";
|
|
809
|
-
}
|
|
810
|
-
function assetNames(value) {
|
|
811
|
-
return value.assets.map((asset) => asset.name);
|
|
812
|
-
}
|
|
813
825
|
/** `starterAllowed` records that an empty placeholder is only legal on a draft release. */
|
|
814
826
|
function classifyReleaseAsset(starterAllowed, asset) {
|
|
815
827
|
if (asset === void 0) return { state: "missing" };
|
|
@@ -827,6 +839,17 @@ function classifyReleaseAsset(starterAllowed, asset) {
|
|
|
827
839
|
id: asset.id
|
|
828
840
|
};
|
|
829
841
|
}
|
|
842
|
+
//#endregion
|
|
843
|
+
//#region ../release/dist/store.js
|
|
844
|
+
function assertSameIntent(saved, intended) {
|
|
845
|
+
if (saved.commit !== intended.commit || saved.version !== intended.version || saved.channel !== intended.channel) throw new Error("Release intent differs from the saved release");
|
|
846
|
+
}
|
|
847
|
+
function releaseBodyText(value) {
|
|
848
|
+
return value.body ?? "";
|
|
849
|
+
}
|
|
850
|
+
function assetNames(value) {
|
|
851
|
+
return value.assets.map((asset) => asset.name);
|
|
852
|
+
}
|
|
830
853
|
function savedReleaseFrom(value, intent) {
|
|
831
854
|
const assets = value.assets.filter((asset) => asset.name === releaseArchiveName);
|
|
832
855
|
if (assets.length > 1) throw new Error("Duplicate release archives");
|
|
@@ -995,10 +1018,6 @@ function createReleaseStore(client, packageName) {
|
|
|
995
1018
|
}
|
|
996
1019
|
//#endregion
|
|
997
1020
|
//#region ../release/dist/engine.js
|
|
998
|
-
function skipReason(status) {
|
|
999
|
-
return status === "canary-superseded" ? "Skipping a commit superseded by a published canary" : "Skipping a commit superseded by a stable release";
|
|
1000
|
-
}
|
|
1001
|
-
const regressedBaseSkip = "Skipping a commit superseded by a canary on a newer base";
|
|
1002
1021
|
/** Reserves the record, then packs, verifies, and uploads its archive. Resuming never repacks. */
|
|
1003
1022
|
function recordRelease(intent, adapter, deps) {
|
|
1004
1023
|
return Effect.gen(function* () {
|
|
@@ -1041,24 +1060,19 @@ function mainReleaseIntent(commit, deps) {
|
|
|
1041
1060
|
if (recorded !== void 0) return recorded.intent;
|
|
1042
1061
|
const base = yield* deps.plannedCanaryBase(line.current);
|
|
1043
1062
|
const registry = yield* deps.readRegistry();
|
|
1044
|
-
const
|
|
1063
|
+
const reserved = yield* deps.store.reservedCanaryVersions();
|
|
1064
|
+
const decision = yield* lift(() => decideCanary({
|
|
1045
1065
|
commit,
|
|
1046
1066
|
current: line.current,
|
|
1047
1067
|
base
|
|
1048
|
-
}, registry, deps.ancestry));
|
|
1049
|
-
if (
|
|
1050
|
-
deps.log(
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
const reserved = yield* deps.store.reservedCanaryVersions();
|
|
1054
|
-
const version = yield* lift(() => allocateCanary(base, [...registry.versions.keys(), ...reserved]));
|
|
1055
|
-
if (version === void 0) {
|
|
1056
|
-
deps.log(regressedBaseSkip);
|
|
1068
|
+
}, registry, reserved, deps.ancestry));
|
|
1069
|
+
if ("skip" in decision) {
|
|
1070
|
+
deps.log(decision.reason);
|
|
1057
1071
|
return;
|
|
1058
1072
|
}
|
|
1059
1073
|
return {
|
|
1060
1074
|
channel: "canary",
|
|
1061
|
-
version,
|
|
1075
|
+
version: decision.cut,
|
|
1062
1076
|
commit
|
|
1063
1077
|
};
|
|
1064
1078
|
});
|
package/package.json
CHANGED
|
@@ -3,22 +3,22 @@
|
|
|
3
3
|
"type": "module",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"effect": "4.0.0-rc.
|
|
6
|
+
"effect": "4.0.0-rc.115",
|
|
7
7
|
"typescript": "7.0.2",
|
|
8
|
-
"@oxlint/plugins": "1.
|
|
8
|
+
"@oxlint/plugins": "1.82.0"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"@types/node": "25.9.5",
|
|
12
|
-
"vitest": "4.1.
|
|
12
|
+
"vitest": "4.1.11",
|
|
13
13
|
"tsdown": "0.23.0",
|
|
14
|
-
"@elmeragroup/typescript-config": "0.0.0",
|
|
15
14
|
"@elmeragroup/api-extractor": "0.0.0",
|
|
16
|
-
"@elmeragroup/
|
|
15
|
+
"@elmeragroup/typescript-config": "0.0.0",
|
|
17
16
|
"@elmeragroup/oxlint-plugin-anti-slop": "0.0.0",
|
|
17
|
+
"@elmeragroup/api-artifacts": "0.0.0",
|
|
18
18
|
"@elmeragroup/release": "0.0.0",
|
|
19
|
-
"@elmeragroup/
|
|
19
|
+
"@elmeragroup/oxlint-plugin": "0.0.0"
|
|
20
20
|
},
|
|
21
|
-
"version": "0.2.0-canary.
|
|
21
|
+
"version": "0.2.0-canary.2",
|
|
22
22
|
"exports": {
|
|
23
23
|
".": {
|
|
24
24
|
"types": "./dist/index.d.mts",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
},
|
|
70
70
|
"sideEffects": false,
|
|
71
71
|
"elmeraRelease": {
|
|
72
|
-
"commit": "
|
|
72
|
+
"commit": "f71523541c8c6ca1a937804a6defa1c518b2d001",
|
|
73
73
|
"channel": "canary"
|
|
74
74
|
},
|
|
75
75
|
"scripts": {
|