fix(cli): zip the publish archive to the same bytes every time (#3358)

adm-zip stamps every entry with `new Date()` as it is constructed, and a ZIP
timestamp resolves to two seconds — so archiving identical content twice gave
different bytes whenever the two runs landed either side of a boundary. The
archive's digest was a function of the clock rather than of its contents, which
is backwards for something `cloud render` uploads and addresses by content.

It surfaced as a CI flake: publishProject.test.ts asserts two archives built
back to back are byte-identical, and both sides are the same expression, so the
only way it can fail is non-determinism. The window is narrow, which is why it
survived since July and why re-running always cleared it.

Entry times are now fixed. Built from local components deliberately:
`fromDate2DOS` reads getFullYear/getMonth/getHours, so a fixed instant would
still encode differently per timezone — verified identical bytes under UTC,
America/Los_Angeles and Asia/Kolkata.

The new test moves the clock across a boundary, which is what reproduces it;
back-to-back builds land in the same bucket almost always, which is exactly how
it hid.
This commit is contained in:
Miguel Ángel
2026-08-19 18:25:04 -04:00
committed by GitHub
parent 228eabd43f
commit d464f60b96
2 changed files with 48 additions and 0 deletions
@@ -304,6 +304,33 @@ describe("createPublishArchive (U6 cloud-render regression guard)", () => {
// `createPublishArchive` is exactly the thin composition of
// `buildPublishFileMap` + `zipPublishFileMap` with no baking hook, and that
// a local video asset's original bytes/HTML pass through unmodified.
it("zips the same files to the same bytes across a two-second boundary", () => {
// The flake this pins: adm-zip stamps each entry with `new Date()` as it is
// constructed, and a ZIP timestamp resolves to two seconds — so two runs
// either side of a boundary produced different bytes for identical content.
// The byte-identity assertion below could only ever fail this way, and did,
// rarely enough to survive since July.
//
// Moving the clock is what reproduces it: back-to-back builds land in the
// same bucket almost always, which is exactly why it hid.
const dir = makeProjectDir();
try {
writeFileSync(join(dir, "index.html"), "<html></html>", "utf-8");
vi.useFakeTimers();
try {
vi.setSystemTime(new Date("2026-01-01T00:00:00.000Z"));
const first = createPublishArchive(dir);
vi.setSystemTime(new Date("2026-01-01T00:00:03.000Z"));
const second = createPublishArchive(dir);
expect(first.buffer.equals(second.buffer)).toBe(true);
} finally {
vi.useRealTimers();
}
} finally {
rmSync(dir, { recursive: true, force: true });
}
});
it("keeps a source video byte-identical and excludes proxies from the cloud-render archive", () => {
const dir = makeProjectDir();
try {