Skip to content

Make CI artifacts reproducible#317601

Draft
hediet wants to merge 6 commits into
mainfrom
hediet/b/low-lobster
Draft

Make CI artifacts reproducible#317601
hediet wants to merge 6 commits into
mainfrom
hediet/b/low-lobster

Conversation

@hediet

@hediet hediet commented May 20, 2026

Copy link
Copy Markdown
Member

WIP. Routes archive creation through new shared helpers (reproducible-tar.sh, reproducible-zip.sh, reproducible-zip.ps1) that pin entry order, ownership, and timestamps to the HEAD commit time; sorts readdir output in the built-in extensions scanner; pins linuxPackageRevision, Credits.rtf, the macOS policy plist date, and the snap BUILD_VERSION to the commit time; passes SOURCE_DATE_EPOCH to dpkg-deb and rpmbuild.

Driven by an audit of two CI runs of the same SHA producing byte-different artifacts.

Copilot AI review requested due to automatic review settings May 20, 2026 19:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to make CI-produced distribution artifacts byte-reproducible across runs of the same commit by normalizing archive creation (ordering, ownership, timestamps) and by using the HEAD commit time as the canonical build timestamp throughout packaging steps.

Changes:

  • Introduces SOURCE_DATE_EPOCH helpers and uses commit time instead of wall-clock time for several build metadata fields.
  • Routes .tar.gz/.zip archive creation in CI through new “reproducible” wrapper scripts.
  • Makes built-in extension scanning deterministic by sorting readdir results.
Show a summary per file
File Description
build/lib/sourceDateEpoch.ts New helper to compute SOURCE_DATE_EPOCH (env override → git HEAD committer time → wall clock fallback).
build/lib/policies/render.ts Pins macOS policy manifest pfm_last_modified to the source date instead of new Date().
build/lib/extensions.ts Sorts built-in extension directory traversal for deterministic scanning output.
build/lib/electron.ts Pins darwinCredits date to the source date instead of wall-clock time.
build/gulpfile.vscode.linux.ts Uses commit-time-based revision and passes SOURCE_DATE_EPOCH into dpkg-deb and rpmbuild.
build/azure-pipelines/win32/codesign.ts Switches Windows zipping to the new reproducible PowerShell helper.
build/azure-pipelines/web/product-build-web.yml Switches web tarball creation to reproducible-tar.sh.
build/azure-pipelines/linux/steps/product-build-linux-compile.yml Switches Linux client/server tarballs to reproducible-tar.sh.
build/azure-pipelines/linux/build-snap.sh Pins Snap BUILD_VERSION to the HEAD commit time.
build/azure-pipelines/darwin/steps/product-build-darwin-compile.yml Switches macOS zipping to reproducible-zip.sh.
build/azure-pipelines/darwin/product-build-darwin-universal.yml Switches universal macOS zipping to reproducible-zip.sh.
build/azure-pipelines/common/reproducible-zip.sh New helper to pre-touch entries and zip from a sorted file list.
build/azure-pipelines/common/reproducible-zip.ps1 New helper to pre-stamp timestamps before zipping with 7-Zip on Windows.
build/azure-pipelines/common/reproducible-tar.sh New helper to create normalized tar.gz archives (sorted entries, fixed mtime/owner/group, reproducible gzip header).
build/azure-pipelines/alpine/product-build-alpine.yml Switches Alpine tarball creation to reproducible-tar.sh.

Copilot's findings

  • Files reviewed: 15/15 changed files
  • Comments generated: 4

Comment on lines 23 to 27
const root = path.dirname(import.meta.dirname);
const commit = getVersion(root);

const linuxPackageRevision = Math.floor(new Date().getTime() / 1000);
const linuxPackageRevision = getSourceDateEpoch();

Comment on lines +22 to +25
try {
const cwd = path.dirname(path.dirname(import.meta.dirname));
const out = cp.execSync('git log -1 --pretty=%ct', { cwd, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
const parsed = parseInt(out, 10);
Comment on lines +33 to +37
TOUCH_DATE=$(date -r "$SOURCE_DATE_EPOCH" "+%Y%m%d%H%M.%S")

(
cd "$SOURCE_DIR"
find . -exec touch -h -t "$TOUCH_DATE" {} +
Comment on lines +35 to +39
(
cd "$SOURCE_DIR"
find . -exec touch -h -t "$TOUCH_DATE" {} +
find . -print | LC_ALL=C sort | zip -X -y "$ARCHIVE_PATH" -@
)
hediet added 3 commits May 21, 2026 13:56
- Add build/lib/sourceDateEpoch.ts helper that derives the timestamp from the HEAD commit time (overridable via SOURCE_DATE_EPOCH)
- Add reproducible-tar.sh / reproducible-zip.sh / reproducible-zip.ps1 shared helpers and route the 6 tar + 5 darwin zip + 3 win32 7z invocations through them
- Sort readdir output in scanBuiltinExtensions so the built-in extensions list is deterministic
- Use the commit time for linuxPackageRevision, the macOS Credits.rtf date, the macOS policy plist date, and the snap BUILD_VERSION
- Pass SOURCE_DATE_EPOCH to dpkg-deb and rpmbuild
@hediet hediet force-pushed the hediet/b/low-lobster branch from 2f243e3 to 531bfb3 Compare May 21, 2026 11:56
hediet added 3 commits May 21, 2026 13:57
reproducible-zip.sh now takes <cwd> <pattern>; client zips pass '*' (flat), server zips pass the dir name (wrapped) - matching the pre-refactor layout per call site.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants