Conversation
Compose a source release candidate in one step: create a reproducible source archive of `HEAD` with `git archive | gzip -n`, compute its SHA-512 checksum and the SWHID of the expanded tree, sign it with the PMC key imported through `crazy-max/ghaction-import-gpg` and upload the files to ATR through the existing `upload-to-atr` action. The shell and Python logic lives in scripts next to `action.yml`, and the `asfswhid` dependency is pinned with hashes and tracked by Dependabot. zizmor is bumped to a version that understands the `$/` self-repository reference used to call `upload-to-atr`. Assisted-By: Claude Fable 5.1 <noreply@anthropic.com>
|
The |
|
I did a quick review, but let's wait for Monday for Sean's review. I have questions about what do you do with binary conveniences and where is the SWID handled / compared with the one ATR generates? |
|
Yes, binaries are out-of-scope, because they are ecosystem-specific. The SWHID is there only to compare it with what ATR shows or, in the future, to compare multiple archive formats. |
| @@ -0,0 +1,84 @@ | |||
| #!/usr/bin/env bash | |||
There was a problem hiding this comment.
Suggestion - maybe we should rewrite those scripts to Python + uv run ? While I am very long time and very experience bash script creator, i absolutely hate lack of readability and testability it has. Also bash is not very portable - especially for MacOS where default bash is very old due to licencing reasons, and people have to replace the MacOS bash with brew version of it to get some modern(ish) features (like 10 years old).
With Python scripts, inline metadata - it has even better portability and readability, and if you make a mini-project with pyproject.toml you can also add unit tests, which are absolutely necessary if we want to leverage agentic work. Test harness is crucial to be able to maintain any piece of software with agents.
| @@ -0,0 +1,77 @@ | |||
| # Licensed to the Apache Software Foundation (ASF) under one | |||
There was a problem hiding this comment.
Use inline metadata, to declare python versions supported and make it uv-runnable
potiuk
left a comment
There was a problem hiding this comment.
Very cool. Some suggestions added.
Agreed, Let's do it gradually and once we get sources figured out, we can do next steps for binaries. There is no need to solve all of that - there are projects (like Magpie) which only release source packages. Step-by-step. I think also if we make the bash script into a portable python script with it's own tests and pyproject toml, this adds the necessary step for local verification. I think creating reproducible sources solves only one part of the reproducibility - part of the reproducibility check is that PMC members prepare the same source packafe locally - from the same commit and see if it's binary identical to the one in ATR. If you extract the "preparation" script into a portable python project runnable with Bash has this problem (and I've been bitten by it many times) - that MacOS by default has very, very, old version of bash - and it's readablity is bad. |
|
BTW. I like the idea of storing and using SHWID. This is way better than specifying repo + commit hash, because it's a unique value we can use also for the future. For example we could store / link somehow SHWID also for binary artifacts preparation - and then make sure that any binary artifacts has the same SHWID as the source artifact. |
|
To be clear. Building of artifacts is outside the scope of ATR because there are many ways to build and many build tools. These are each project's decision. For many reasons a source release must contain only files that are in the source repository, but it need not contain all. Often there are files in the repository it MUST not include. Adding signatures and checksum creation would be a helpful and acceptable extra. The calculation of SWID is only needed if you want to check it with the calculation that ATR already does in the compose phase. (This is a new feature.) If there is a common way to check and possibly generate a cycloneDX SBOM that would be an awesome bonus feature. |
|
@ppkarwasz before proceeding with this approach we need to do some research on which PMCs are currently approved (ATR tells this from the PMC's KEYS file containing the OpenPGP public key for the PMC) and for those which are what types of artifacts do they release. We have admin ability to add such a key as well. Also, reproducibility is a binary artifact concern and not a source concern. We already check that a source release exists in the repository at a declared revision/SHA matches since that is one of the extended checks voters should be doing. |
Oh absolutely - and there is absolutely standard way of excluding those files: .gitattributes. We are using it for years in Airflow and many other projects also use Airflow's .gitattributes file: https://github.com/apache/airflow/blob/main/.gitattributes
I've worked with the author of cdxgen https://github.com/cdxgen/cdxgen - we fixed a few performance issues few years ago - and we are using them for airflow's SBOMS and it works very well. However - this is much more complex - as opposed to .gitattributes export-ignore directives that make |
|
I've assigned #39 to answer the what do enabled PMCs actually release question. |
I think quite the opposite - it would alleviate easily all the complaints about "do not know what you are checking". Perfect reproducibility of the source artifacts that are published in ATR and ones that PMC can generate locally is the only real way for the PMC to know what they are voting on - this for example would completely invalidate any issues that Sebb raised before - because the PMC could use reproducibility check to know that Release manager did not modify uploaded source artitcat. Which is exactly the scenario that 'xz` issue hed. The release manager modified the .tar.gz artifact - it was not produced from the specific commit - it had few lines injected in build script. And reproducibility check by independent PMC members -> recreating the same .tar.gz file is the fastest and most certain way to verify that release manager preparing the artifact was not roque, blackmailed, or that someone stole their credentials and wanted to inject stuff. |
|
In the current workflow we do not have prevention against roque release manager "xz-like" , I think - and this one provides pretty much 100% protection againts it, if 2 more PMC members can reproduce the very same .tar.gz file from the same commit. |
Yes, a binary release and not source. ATR does checks to see that source releases have no binaries and that every file matches the same file in the source repository which is a superset of the source release.
For binaries we count on voters inspecting artifacts for surprises by doing their own builds from source. That is a key component to preventing the BTW - this discussion should be on a mailing list. |
But can a PMC verify that? I think very basic issue here is that we have to trust something else than personal check done by a few people
The xz was actually source artifact - not binary. Look it up. In case of
Which one? I am happy to start it - but I just want to say - as one of the users of the tools - that I would find this one as an extremely useful tool from tooling - and very usable. But if you think we should have a mailing list discussion on that - I think it's a good idea to tell Piotr where he can raise it and how. I will chime-in as a user. |
|
You need to listen to what I'm describing as checks that are already in ATR's compose phase.
We allow CI if the release has reproducible builds. Builds are by definition convenience binaries. The agreement is that PMC members do their own builds. SWHID is a way to compare without having bit by bit comparison. Instructions on how to perform these tests are the responsibility of the PMC. Let's wait until Monday for further discussion after we survey what PMCs that are enabled to do reproducible build are actually doing. |
…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)
|
Indeed - this action does not have to be part of ATR. The apache/magpie#1296 has alll the reproducibility checks, SHWID, capability to create an action for GH release + following process to approve it with Infra as part of it's "ASF release" family of skills. So regardless if it's going to be reusable action - I will use it to drive the release process of Magpie itself for now. |
…and ASF automated signing (#1296) * feat(release): reproducible source archives, reproducibility checks, 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) * feat(release): project-specific convenience artefacts, gated on reproducibility 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) * feat(release): record the source tree's SWHID next to the commit and 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) * test(reproducible-archive): prove symlink modes do not leak the packer'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)

Summary
New composite action
upload-source-to-atrthat composes a source release candidate in one step:HEADwithgit archive | gzip -n, following the reproducible archives guidance,asfswhid,crazy-max/ghaction-import-gpg,upload-to-atraction, referenced with the$/self-repository syntax.Inputs are the ATR project and version, an optional archive prefix (default
apache-<project>-<version>-src) and top-level directory, the GPG key and passphrase from the repository secrets, and the expected fingerprint from a repository or organization variable.This generalizes the workflow @tisonkun wrote in apache/asyncband#319 so that other projects do not have to copy it.
Other changes
action.yml; the license-header hook now covers.shand.pyfiles.asfswhidis pinned with hashes inrequirements.txtand tracked by Dependabot.$/reference.Testing
pre-commit run --all-filespasses.ubuntu:24.04container with a throwaway key; the archive is byte-for-byte reproducible and the SWHID matches the git tree id unless.gitattributesalter the export.🤖 Generated with Claude Code