Skip to content

feat(release): reproducible source archives, reproducibility checks, and ASF automated signing - #1296

Merged
potiuk merged 4 commits into
apache:mainfrom
potiuk:release-reproducibility
Sep 20, 2026
Merged

potiuk merged 4 commits into
apache:mainfrom
potiuk:release-reproducibility

Conversation

@potiuk

@potiuk potiuk commented Sep 20, 2026

Copy link
Copy Markdown
Member

Summary

  • The source artefact is now a reproducible export of the tag. A new stdlib-only tool, tools/reproducible-archive (repro-archive build | check | compare | recipe | epoch), wraps git archive (tracked files only, .gitattributes export-ignore honoured) and applies every rule from reproducible-builds.org § Archive metadata: one SOURCE_DATE_EPOCH mtime, sorted members, uid/gid 0, a=rX,u+w, no PAX atime/ctime, gzip -n, zip -X. Two builders of the same tag get byte-identical bytes regardless of their git/tar/zip version. release-rc-cut emits it as the source build; the single-file module can be copied verbatim into a CI workflow.
  • Optional reproducibility checks, for source and binaries. release-build.md § Reproducibility checks turns on a rebuild-and-compare in release-rc-cut Step 2b (RM self-check before signing) and release-verify-rc Step 9 (every voter), with identical / content-identical / differs verdicts; binaries by byte-identical or documented-divergence. Motivation: Magpie's own 0.1.0-rc1 got a -1 for a non-reproducible zip -r artefact (PRINCIPLES.md § 11).
  • First-release .gitattributes education step. release-prepare prep Step 2e walks the operator through every top-level path, classifies it, checks references before proposing an export-ignore, confirms each entry, lands .gitattributes in the prep PR and records export_ignore_reviewed; release-rc-cut blocks while the review is outstanding.
  • 🪶 ASF-specific automated release signing (opt-in, gated on organization: ASF). release-prepare automated-signing drafts (never files/sends) the Infra key-request ticket, the Security Team notification and a reproducible-build workflow PR from projects/_template/workflows/release-candidate.yml (no key material, ATR OIDC upload), per Infra § Automated release signing. When enabled, verify-rc's reproducibility check becomes the mandatory bit-by-bit validation on trusted hardware and release-promote blocks without the attestation. Non-ASF adopters never see it.
  • The record carries a SWHID next to the commit and the repository URL. repro-archive build prints swh:1:rev:<commit> and swh:1:dir:<tree> of the archive's expanded content (with origin= / anchor= qualifiers), computed in memory the way git computes a tree id and tested against git write-tree over the extracted tree, so it agrees with asfswhid and with what ATR computes at compose time; a note says whether it equals the repository tree (i.e. whether .gitattributes altered the export). check --swhid and swhid let a voter recompute and compare from the staged bytes; release-verify-rc reports swhid_matches; the [VOTE] record lists repository, commit, SWHID, epoch and sha512. Two more machine-dependent inputs are pinned as well: the builder's core.autocrlf / core.eol (which git archive would apply), and the commit id travels in the archive as git archive does it (global PAX comment / zip comment). Shape follows the discussion on apache/tooling-actions#37.
  • Design doc. docs/designs/2026-09-20-reproducible-releases.md records the decisions, the rejected alternatives and the known limits (compression bytes across zlib builds, export-subst, the ATR SWHID exposure).
  • Convenience artefacts are project-specific and config-declared; reproducibility decides whether one is good. The framework assumes nothing beyond the source. release-build.md § Convenience artefacts lists each artefact a project ships besides the source (binary tarball, wheels, jars, container image, chart, …) with its own build_command, staging / stage_command, reproducibility mode, vote_included flag and publish_channel / publish_command. release-rc-cut builds and stages them under the tag's SOURCE_DATE_EPOCH, release-verify-rc rebuilds and compares each one (a binary cannot be reviewed, only rebuilt — that check is what establishes it is what the voted source produces), release-vote-draft lists them with their vote scope, release-promote publishes only the ones that reproduced and emits a HOLD for any that did not (the source promotion is never held back), and release-announce-draft names their channels. release-verify-rc Step 7 now runs the project's own source_tree_validators instead of hard-coded framework tools.
  • [VOTE] template tells PMC members how to verify. Every release-vote-draft body carries a How to verify this candidate section: the reproducibility record (commit, SOURCE_DATE_EPOCH, sha512), the agentic one-liner (/magpie-release-management:verify-rc <rc>), the human-readable verification page and reproducibility page at the RC tag, the ATR candidate link, and the voter-obligation sentence. New config keys vote_verification_doc_url, reproducibility_doc_url, vote_verification_skill; ATR runbook and the manual voter guide (with a reply template) updated to match.

Type of change

  • Skill change (.claude/skills/<name>/) — eval fixtures updated below
  • Tool / bridge contract (tools/<system>/*.md)
  • Python package (tools/*/ with pyproject.toml)
  • Groovy reference impl
  • Cross-cutting (RFC, AGENTS.md, sandbox, privacy-LLM)
  • Documentation (docs/, README.md, CONTRIBUTING.md)
  • Project template (projects/_template/)
  • CI / dev loop (prek, workflows, validators)
  • Other:

Test plan

  • prek run --all-files passes
  • For Python packages touched: uv run pytest / ruff check / mypy passes — reproducible-archive has 21 tests: every reproducible-builds.org rule has a positive case (the builder applies it) and a negative case (check catches a violation), compare on all three verdicts, and two builds at different wall-clock times under a different umask are byte-identical
  • For Groovy bridges touched: command-line invocation tested end-to-end
  • For skill changes: eval suite passes for the affected skill — run with claude -p: release-prepare 15/15, release-rc-cut 14/14, release-promote 8/8, release-vote-draft step-2 3/3, release-verify-rc 16/17 (the one failure is the untouched step-2-verify-signatures/case-2, where the semantic grader objected to the model picking downloads.apache.org over dist.apache.org for the KEYS URL — pre-existing grader sensitivity, not touched by this PR)
  • For skill behaviour changes: a new or updated eval fixture is included in this PR — new suites release-rc-cut/step-2b-reproducibility (3) and release-verify-rc/step-9-reproducibility (5, incl. a convenience artefact whose rebuild differs); new cases for the unreviewed-archive block, the repro-archive build source command, the first-release archive review, the ASF-only automated-signing gate (blocked for non-ASF, proceeds for ASF), and release-promote publishing a reproduced wheel while holding a container image that did not reproduce; release-vote-draft step-2 now asserts the How to verify section via assertions.json, including the case that must omit the reproducibility record rather than invent it
  • Other: tools/spec-loop/specs/release-management-lifecycle.md updated by hand for the new behaviour; .last-sync not bumped

RFC-AI-0004 compliance

  • HITL — every new state change is a proposal: .gitattributes entries confirmed one by one, the Infra ticket and Security Team mail are drafts, the workflow PR opens as a draft, release-verify-rc never asserts trusted hardware on the committer's behalf (--trusted-hardware is theirs)
  • Sandboxrepro-archive runs offline on the local clone; no new network reach
  • Vendor neutrality — placeholders throughout; the ASF-specific option is resolved from organizations/ASF/organization.mdrelease_process.automated_signing (null in independent) and marked 🪶 wherever it appears
  • Conversational + correctable--allow-unreviewed-archive, --skip-repro-check, --skip-repro, --review-archive are the explicit, logged overrides; config keys default to today's behaviour for source_archive_method: custom
  • Write-access discipline — no autonomous outbound messages; the agent holds neither the RM's key nor the CI key (spec § Boundary 1 extended to say so)
  • Privacy LLM — n/a

Linked issues

Refs the 0.1.0-rc1 -1 recorded in docs/release-management/manual-release-process.md § Caveats.

Notes for reviewers (optional)

  • A new tool-capability value, substrate:release, was added to docs/labels-and-capabilities.md and the validator's TOOL_CAPABILITIES, since none of the existing substrate:* values fit an adopter-facing release helper. Happy to fold it into an existing value if you prefer.
  • The ATR upload action's exact inputs in projects/_template/workflows/release-candidate.yml are placeholders (<COMMIT_SHA>, # files:); the template says to confirm them against apache/tooling-actions when rendering. The workflow deliberately contains no signing step: the mechanism is whatever Infra agrees on the ticket.
  • release-verify-rc renumbered its hand-back step from 9 to 10 to make room for the reproducibility step; no eval referenced the old number.
  • Genericity audit: the only project-specific text found in the release skills was verify-rc Step 7's hard-coded Magpie validators and rc1 story, now replaced by the config-driven source_tree_validators (Magpie's own list moved to projects/magpie/release-build.md). Remaining "Magpie" mentions in the skills are references to the framework (skill names, the shared pre-flight block).

Gen-AI disclosure

Authored with Claude Code (Fable 5.1) driving the edits, tests and eval runs; reviewed and directed by the PR author.

🤖 Generated with Claude Code

@potiuk potiuk added family:tools tools/* family:docs Docs, MISSION.md, READMEs capability:triage Sweep + classify + propose disposition capability:resolve Close-out: invalidate, dedupe, CVE allocate, announcement family:release-management release-* skills labels Sep 20, 2026
@potiuk
potiuk marked this pull request as ready for review September 20, 2026 14:17
…and ASF automated signing

The source artefact is now a reproducible export of the tag. A new
stdlib-only tool, tools/reproducible-archive (repro-archive build |
check | compare | recipe | epoch), wraps `git archive` (tracked files
only, .gitattributes export-ignore honoured) and applies every rule from
reproducible-builds.org/docs/archives/: one SOURCE_DATE_EPOCH mtime,
sorted members, uid/gid 0, a=rX,u+w modes, no PAX atime/ctime, gzip -n,
zip -X. Two builders of the same tag get byte-identical bytes regardless
of their git/tar/zip version. release-rc-cut emits it as the source
build command; release-verify-rc rebuilds and compares.

release-build.md gains "Source archive" (source_archive_method, format,
prefix, export_ignore_reviewed) and "Reproducibility checks"
(reproducibility_source, reproducibility_binaries with byte-identical
or documented-divergence modes). The checks run as release-rc-cut Step
2b (RM self-check before signing) and release-verify-rc Step 9 (every
voter); the hand-back step moved to Step 10.

release-prepare prep Step 2e is the first-release .gitattributes
education step: it classifies every top-level path, checks references
before proposing an export-ignore, confirms each entry with the RM,
lands .gitattributes in the prep PR and records the review marker.
release-rc-cut blocks while the review is outstanding.

ASF-specific (organization: ASF only, marked as such): release-prepare
automated-signing drafts, never files or sends, the Infra key-request
ticket, the Security Team notification and a reproducible-build
workflow PR from projects/_template/workflows/release-candidate.yml
(no key material, ATR OIDC upload), per Infra's automated release
signing policy. When enabled, verify-rc's reproducibility check becomes
the mandatory bit-by-bit validation on trusted hardware and
release-promote blocks without the attestation. The option is resolved
from release_process.automated_signing in the ASF organization
manifest and is null for independent projects.

Every [VOTE] body release-vote-draft produces now carries a "How to
verify this candidate" section: the reproducibility record (commit,
SOURCE_DATE_EPOCH, sha512), the agentic one-liner
(/magpie-release-management:verify-rc), the human-readable verification
page and reproducibility page at the RC tag, the ATR candidate link and
the voter-obligation sentence. New config keys vote_verification_doc_url,
reproducibility_doc_url and vote_verification_skill; the ATR runbook and
the manual voter guide (with a reply template) match.

Docs: new docs/release-management/reproducibility.md; process, spec,
both runbooks, source-release-contents, the capability taxonomy (new
substrate:release value) and the spec-loop spec updated. Evals: new
suites for rc-cut Step 2b and verify-rc Step 9, new cases for the
unreviewed-archive block, the repro-archive build command, the
first-release review and the ASF-only gate; vote-draft step-2 asserts
the verification section.

Generated-by: Claude Code (Fable 5.1)
@potiuk
potiuk force-pushed the release-reproducibility branch from aa0bb2c to 6aa8233 Compare September 20, 2026 14:22
…ducibility

The release skills assumed one shape of "binary": a global
binary_rebuild_command and a hard-coded list of the framework's own
validators in verify-rc Step 7. Both were Magpie-specific. Convenience
artefacts are project-specific by nature — a binary tarball, wheels,
jars, a container image, a chart, each built, staged, voted on and
published its own way — so the framework now assumes none and reads
what each project declares.

release-build.md gains "Convenience artefacts": one entry per artefact
with its own build_command, staging / stage_command, reproducibility
mode (byte-identical | documented-divergence, default
reproducibility_binaries), vote_included flag and publish_channel /
publish_command; and "Source-tree validators": the project's own
integrity checks for verify-rc Step 7 (Magpie's list moves to
projects/magpie/release-build.md).

Each skill consumes the list: release-rc-cut emits every artefact's
build command under the tag's SOURCE_DATE_EPOCH, the per-artefact
rebuild-and-compare self-check, and registry staging commands;
release-verify-rc Step 9 rebuilds and compares every artefact and
names the ones that did not reproduce; release-vote-draft lists the
artefacts, where each is staged and which are in the vote;
release-promote publishes each artefact to its declared channel only
when the recorded verify-rc run reproduced it, and emits a HOLD note
otherwise (the source promotion is never held back);
release-announce-draft names the channels.

Reproducibility is stated, in the config, the docs and the skills, as
the check that decides whether a convenience artefact is good: a
binary cannot be reviewed, only rebuilt, so rebuilding it from the
voted tag and comparing is the one way to establish that it is what
the source produces.

Evals: promote step-2 case-5 (publish the reproduced wheel, hold the
container image that differed), verify-rc step-9 case-5 (source
identical, artefact differs → FAIL), rc-cut step-2b and vote-draft
fixtures updated to the per-artefact shape.

Generated-by: Claude Code (Fable 5.1)
…origin

A commit id names a repository object; the Software Heritage identifier
of the expanded archive content (swh:1:dir:<sha1>) names what shipped.
It is computed from names, modes and contents alone, exactly as git
computes a tree id, so it does not depend on the archive format, the
compression or who packed it: a voter recomputes it from the staged
bytes, ATR computes the same value at compose time, and it equals the
repository tree at the tag unless .gitattributes altered the export —
in which case the difference is itself the record of what was left
out. It also gives a convenience artefact something precise to point at
as the source it was built from.

repro-archive: `build` now prints swhid_rev (swh:1:rev:<commit>),
swhid_dir (the content identifier, qualified with origin=<repo URL>
and anchor=swh:1:rev:…) and a note saying whether the content SWHID
equals the repository tree; `swhid` computes the identifiers for an
archive and/or a ref; `check --swhid` asserts an archive against a
recorded value; `compare` reports both archives' SWHIDs. The in-memory
tree hashing is tested against `git write-tree` over the extracted
tree and against `git rev-parse <ref>^{tree}` when nothing is
export-ignored, so it agrees with asfswhid / swh identify by
construction.

Two more machine-dependent inputs are pinned, following the review of
apache/tooling-actions#37: the builder's core.autocrlf / core.eol
(which `git archive` would apply to text files) are neutralised, and
the commit id travels in the archive the way `git archive` carries it
(a global PAX `comment` in tar, the archive comment in zip; `check`
accepts exactly that comment).

Skills: release-rc-cut records repository URL, commit, SWHIDs, epoch
and sha512 on the planning issue; release-verify-rc checks the staged
archive against the recorded SWHID (`swhid_matches`); the [VOTE] body's
reproducibility record lists repository, commit, SWHID, epoch, sha512,
and its agentic-path paragraph is fixed text. The CI workflow template
prints the record into the job summary.

Docs: docs/designs/2026-09-20-reproducible-releases.md records the
decisions, rejected alternatives and known limits; reproducibility.md
and the tool README explain the record and the extra fixes.

Generated-by: Claude Code (Fable 5.1)
…r's platform

`lstat` reports a symlink as 0755 on macOS and 0777 on Linux. The
writers already pack every symlink as 0777 and the SWHID uses git's
fixed 120000, so a macOS-built and a Linux-built archive of the same
tag are byte-identical; this adds the test that says so, documents the
rule in the README table and the module, and warns in the shell recipe
that not every tar's --mode rewrites symlink modes.

Generated-by: Claude Code (Fable 5.1)
@potiuk

potiuk commented Sep 20, 2026

Copy link
Copy Markdown
Member Author

This one should make our ATR release process smooth :)

@potiuk
potiuk merged commit 17eb47f into apache:main Sep 20, 2026
46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

capability:resolve Close-out: invalidate, dedupe, CVE allocate, announcement capability:triage Sweep + classify + propose disposition family:docs Docs, MISSION.md, READMEs family:release-management release-* skills family:tools tools/*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant