Skip to content

Commit 3becbb0

Browse files
committed
fix(release): reject an unusable pinned version and a missing npm publish time
The pinned-version escape hatch skipped the semver check the registry path applies, so an operator typo shipped a latest.json that every installed client fails to parse. It now answers to the same shape rule. publishedAt fell back to build time when npm's metadata was unreadable, which is exactly the re-anchoring of the rollout window the function's own comment records as the bug. An unreadable timestamp is a failed registry read, and a failed read already fails the build.
1 parent d825318 commit 3becbb0

1 file changed

Lines changed: 24 additions & 6 deletions

File tree

‎apps/site/scripts/build-cdn.mjs‎

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ function parseArgs() {
3030
return { out, skipRg };
3131
}
3232

33+
const RELEASE_VERSION = /^\d+\.\d+\.\d+$/;
34+
3335
/**
3436
* Resolve the version this CDN advertises from a *published* release, never from
3537
* the working tree.
@@ -50,7 +52,18 @@ function parseArgs() {
5052
*/
5153
async function resolvePublishedRelease(packageName) {
5254
const pinned = process.env.PYTHINKER_CDN_VERSION?.trim();
53-
if (pinned) return { version: pinned, publishedAt: new Date().toISOString() };
55+
if (pinned) {
56+
// The override answers to the same shape rule as the registry path below.
57+
// A client rejects a manifest whose `version` is not semver, so an
58+
// unusable override has to stop the build instead of publishing a
59+
// latest.json that every installed client fails to parse.
60+
if (!RELEASE_VERSION.test(pinned)) {
61+
throw new Error(`PYTHINKER_CDN_VERSION is not a release version: ${pinned}`);
62+
}
63+
// Build time is the only timestamp available for a manual pin; the
64+
// registry path below requires npm's own and never stamps one.
65+
return { version: pinned, publishedAt: new Date().toISOString() };
66+
}
5467
const view = JSON.parse(
5568
execFileSync(
5669
'npm',
@@ -59,16 +72,21 @@ async function resolvePublishedRelease(packageName) {
5972
),
6073
);
6174
const version = view['dist-tags']?.latest;
62-
if (typeof version !== 'string' || !/^\d+\.\d+\.\d+$/.test(version)) {
75+
if (typeof version !== 'string' || !RELEASE_VERSION.test(version)) {
6376
throw new Error(
6477
`npm dist-tag latest for ${packageName} is not a release version: ${String(version)}`,
6578
);
6679
}
6780
const publishedAt = view.time?.[version];
68-
return {
69-
version,
70-
publishedAt: typeof publishedAt === 'string' ? publishedAt : new Date().toISOString(),
71-
};
81+
// Stamping build time here is the bug this function documents: it would move
82+
// the rollout anchor on every unrelated site deploy. Unreadable registry
83+
// metadata is a failed read, and a failed read must fail the build.
84+
if (typeof publishedAt !== 'string' || !Number.isFinite(Date.parse(publishedAt))) {
85+
throw new Error(
86+
`npm has no usable publish time for ${packageName}@${version}: ${String(publishedAt)}`,
87+
);
88+
}
89+
return { version, publishedAt };
7290
}
7391

7492
async function copyPlugins(repoRoot, cdnRoot) {

0 commit comments

Comments
 (0)