fix(cli): zip the publish archive to the same bytes every time - #3358
Merged
Conversation
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.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
createPublishArchiveproduced different bytes for identical content depending on when it ran.Why
zipPublishFileMapcallsarchive.addFile(path, content)with noattr, and adm-zip 0.6.0 stamps_time = fromDate2DOS(new Date())at entry construction (headers/entryHeader.js:37), writing it into both the local and central headers. A ZIP timestamp resolves to two seconds, so two builds match only when they land in the same bucket.That makes the archive's digest a function of the clock rather than of its contents — backwards for something
cloud renderuploads and addresses by content.How it surfaced
publishProject.test.ts:321asserts two archives built back-to-back are byte-identical. Both sides are the same expression, so the only way it can fail is non-determinism. It has been failing rarely since July and clearing on every re-run, which is exactly the signature of a narrow timing window.The fix
Entry times are pinned to a fixed value after the entries are added.
Built from local components on purpose:
fromDate2DOSreadsgetFullYear,getMonth,getHoursand friends, so a fixed instant would still encode differently per timezone. Verified identical bytes under three zones:1980-01-01 is the earliest a DOS timestamp can represent (
fromDate2DOSwrites zeroes below 1980).Test
The new test moves the clock across a two-second boundary, because that is what reproduces it — back-to-back builds land in the same bucket almost always, which is how it hid for months. Mutation-checked: removing the pin reproduces the original failure exactly (
expected false to be true), deterministically rather than one run in dozens.Scope
Archive bytes change once, on merge, for every input. That is the point — the same project now zips to one digest instead of a new one every two seconds. No test or caller pinned the previous value;
publish.ts:166andcloud/render.ts:605are the two consumers and both take the buffer as-is.CLI suite: 2783 passing.