feat(release): stamp canary Windows builds as yy.mmdd.hh.mmss

This commit is contained in:
ethernet
2026-09-21 21:10:49 -04:00
parent 75dacfcba3
commit fcfd001dc0
4 changed files with 51 additions and 27 deletions

View File

@@ -1,6 +1,6 @@
// msix-shared — the native quad (the build time) is the package version for
// stable and canary both. The git-backed lookup is deterministic here because
// node:child_process.execFileSync is mocked.
// msix-shared — native package versions come from the immutable build time.
// Stable keeps the Store quad; canary uses yy.mmdd.hh.mmss. The git-backed
// lookup is deterministic here because node:child_process.execFileSync is mocked.
import assert from 'node:assert/strict'
import fs from 'node:fs'
import os from 'node:os'
@@ -48,7 +48,7 @@ test('explicit stable tag owns the package version, independent of checkout meta
}
})
test('a stable tag and a canary stamp the same quad when built together', () => {
test('stable and canary use their own native build-time quads', () => {
const desktop = makeFakeDesktop('0.27.1')
const epoch = Math.floor(Date.UTC(2026, 7, 29, 1, 2, 3) / 1000)
execFileSync.mockImplementation((cmd, args) => {
@@ -59,7 +59,7 @@ test('a stable tag and a canary stamp the same quad when built together', () =>
const stable = msix.appIdentity(desktop, 'v0.27.1')
const canary = msix.appIdentity(desktop, 'v0.27.2-canary.20260829010203')
assert.equal(stable.version, '2026.5761.123.0')
assert.equal(canary.version, stable.version)
assert.equal(canary.version, '26.829.1.203')
assert.equal(canary.fileVersion, '0.27.2-canary.20260829010203')
} finally {
fs.rmSync(desktop, { recursive: true, force: true })

View File

@@ -8,24 +8,31 @@ import { nativeQuad } from '../../../scripts/msix-shared.mjs'
// holds 14*60+3 = 843 seconds.
const EPOCH = Date.parse('2026-09-22T00:14:03Z') / 1000
test('a stable release and a canary built at the same instant share one quad', () => {
test('stable keeps the store quad while canary uses yy.mmdd.hh.mmss', () => {
assert.equal(nativeQuad('v0.21.5', EPOCH), '2026.6336.843.0')
assert.equal(nativeQuad('v0.21.4+canary.20260922T001403Z', EPOCH), '2026.6336.843.0')
assert.equal(nativeQuad('v0.21.4+canary.20260922T001403Z', EPOCH), '26.922.0.1403')
})
test('a later build sorts above an earlier one, stable or canary', () => {
const earlier = nativeQuad('v0.21.5', EPOCH).split('.').map(Number)
const later = nativeQuad('v0.21.4+canary.20260922T001404Z', EPOCH + 1).split('.').map(Number)
test('a later canary sorts above an earlier canary across a month boundary', () => {
const earlier = nativeQuad(
'v0.21.4+canary.20260131T235959Z',
Date.parse('2026-01-31T23:59:59Z') / 1000
).split('.').map(Number)
const later = nativeQuad(
'v0.21.4+canary.20260201T000000Z',
Date.parse('2026-02-01T00:00:00Z') / 1000
).split('.').map(Number)
const first = later.findIndex((value, index) => value !== earlier[index])
assert.ok(first >= 0)
assert.ok(later[first] > earlier[first])
})
test('every field stays inside 16 bits and the revision stays zero', () => {
test('every field stays inside 16 bits', () => {
for (const stamp of ['2026-01-01T00:00:00Z', '2026-12-31T23:59:59Z', '2028-12-31T23:59:59Z']) {
const parts = nativeQuad('v0.21.5', Date.parse(stamp) / 1000).split('.').map(Number)
assert.equal(parts.length, 4)
assert.ok(parts.every(value => value >= 0 && value <= 65535))
assert.equal(parts[3], 0)
for (const ref of ['v0.21.5', `v0.21.4+canary.${stamp.replace(/[-:T]/g, '').slice(0, 15)}Z`]) {
const parts = nativeQuad(ref, Date.parse(stamp) / 1000).split('.').map(Number)
assert.equal(parts.length, 4)
assert.ok(parts.every(value => value >= 0 && value <= 65535))
}
}
})

View File

@@ -109,18 +109,35 @@ export function storePackageVersionAt(epochSeconds) {
return `${year}.${hourOfYear}.${secondOfHour}.0`
}
/** The native quad for a release, stable or canary: ``year.hourOfYear.secondOfHour.0``.
/** Canary package quad: ``yy.mmdd.hh.mmss`` in UTC.
* @param {number} epochSeconds immutable build time in UTC
* @returns {string}
*/
export function canaryPackageVersionAt(epochSeconds) {
const date = new Date(epochSeconds * 1000)
if (!Number.isInteger(epochSeconds) || !Number.isFinite(date.getTime())) {
throw new Error('Canary package version needs a valid immutable build timestamp')
}
const yy = date.getUTCFullYear() % 100
const mmdd = (date.getUTCMonth() + 1) * 100 + date.getUTCDate()
const hh = date.getUTCHours()
const mmss = date.getUTCMinutes() * 100 + date.getUTCSeconds()
return `${yy}.${mmdd}.${hh}.${mmss}`
}
One derivation for every Windows consumer. The quad is the build time, so a
later build always sorts above an earlier one and a canary shares its stable's
quad when they are built at the same instant. There is no minutes-since-stable
counter, so there is no 45-day cap.
@param {string} _ref the release ref; accepted so callers name what they stamp
/** The native quad for a release.
Stable Store packages retain ``year.hourOfYear.secondOfHour.0``. Canary uses
``yy.mmdd.hh.mmss`` so its four numeric fields show the UTC build time directly
while remaining monotonic across month boundaries.
@param {string} ref the release ref whose native package is being stamped
@param {number} epochSeconds the build time, UTC
@returns {string}
*/
export function nativeQuad(_ref, epochSeconds) {
return storePackageVersionAt(epochSeconds)
export function nativeQuad(ref, epochSeconds) {
return /(?:\+|-)canary\./.test(ref)
? canaryPackageVersionAt(epochSeconds)
: storePackageVersionAt(epochSeconds)
}
/** The build time of a release tag: the stamp embedded in a canary tag, or

View File

@@ -52,7 +52,7 @@ def canary(tmp_path, r2_server, monkeypatch):
f"import {{nativeQuad}} from {json.dumps((ROOT / 'scripts/msix-shared.mjs').as_uri())};"
f"console.log(nativeQuad({json.dumps(tag)}, Date.parse('2026-09-13T00:10:00Z') / 1000))",
], text=True).strip()
assert windows_version == "2026.6120.600.0"
assert windows_version == "26.913.0.1000"
tools = tmp_path / "tools"
tools.mkdir()
driver = tools / "driver.py"
@@ -222,7 +222,7 @@ def test_published_canary_workflow_advances_only_after_every_gate(canary, r2_ser
assert result.returncode == 0, result.stdout + result.stderr
resolved = ChannelReader(env["CLOUDFLARE_R2_PUBLIC_URL"], repository=env["GITHUB_REPOSITORY"]).resolve("canary")
assert resolved.manifest is not None
assert resolved.manifest["request"]["windowsVersion"] == "2026.6120.600.0"
assert resolved.manifest["request"]["windowsVersion"] == "26.913.0.1000"
assert resolved.manifest["request"]["commit"] == env["RELEASE_COMMIT"]
assert len(resolved.manifest["packages"]) == 4
# Bootstrap reads actual receipt-bound artifacts and the promoted native feeds,
@@ -264,14 +264,14 @@ def test_published_canary_workflow_advances_only_after_every_gate(canary, r2_ser
_git("tag", newer_tag, cwd=clone)
_git("push", "origin", newer_tag, cwd=clone)
newer = {**env, "RELEASE_TAG": newer_tag, "HERMES_PAYLOAD_TAG": newer_tag,
"FIXTURE_WINDOWS_VERSION": "2026.6120.660.0"}
"FIXTURE_WINDOWS_VERSION": "26.913.0.1100"}
stage_canary(clone, identity, newer, run)
release.write_text(json.dumps({**published, "tagName": newer_tag}))
result = run(script, **newer)
assert result.returncode == 0, result.stdout + result.stderr
advanced = ChannelReader(env["CLOUDFLARE_R2_PUBLIC_URL"], repository=env["GITHUB_REPOSITORY"]).resolve("canary")
assert advanced.manifest is not None
assert advanced.manifest["request"]["windowsVersion"] == "2026.6120.660.0"
assert advanced.manifest["request"]["windowsVersion"] == "26.913.0.1100"
assert advanced.terminal["head"]["sequence"] > resolved.terminal["head"]["sequence"]
after = dict(r2_server.store)
release.write_text(json.dumps(published))