-
Notifications
You must be signed in to change notification settings - Fork 253
feat(release): add ASF source candidate workflow #3278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
9c64f54
feat(release): add ASF source candidate workflow
M4n5ter f0940e2
fix(release): harden ASF source candidate trust
M4n5ter 57b782c
fix(release): close source candidate review gaps
M4n5ter 3bc3f54
fix(release): make source verification hermetic
M4n5ter 52ae9c3
fix(release): isolate source tooling from ambient options
M4n5ter 8d14f5c
fix(release): harden ASF candidate publication
M4n5ter 3f1d078
fix(release): bind source verification and handoff identity
M4n5ter ac36d4f
refactor(release): remove unsupported mutation defenses
M4n5ter 251728b
fix: complete ASF source candidate review checks
M4n5ter 5d52e8f
fix: validate Cargo notices in ASF source candidates
M4n5ter File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,218 @@ | ||
| # Apache Maka source release runbook | ||
|
|
||
| This runbook prepares the official Apache Incubator source-release component. | ||
| The first Maka release also requires npm and Desktop convenience artifacts, but | ||
| those artifacts have separate build, licensing, signing, and acceptance gates. | ||
| They must be built from the exact IPMC-approved source release produced here. | ||
|
|
||
| The workflow implements release mechanics; it does not establish that a commit | ||
| is legally ready to release. Before starting a vote, the PPMC and mentors must | ||
| confirm the candidate's provenance and release documents. | ||
|
|
||
| ## Candidate contract | ||
|
|
||
| - Archive: `apache-maka-<version>-incubating-src.tar.gz` | ||
| - Archive root: `apache-maka-<version>-incubating/` | ||
| - Checksum: `<archive>.sha512` | ||
| - Detached signature: `<archive>.asc` | ||
| - Staging directory: `<version>-incubating-rc<rc>/` | ||
| - Candidate tag: `v<version>-incubating-rc<rc>` | ||
|
|
||
| The RC number identifies a staging attempt and is not part of the archive name. | ||
| If any candidate byte changes, increment the RC number and restart the vote. Do | ||
| not replace files in a directory that has been presented for a vote. | ||
|
|
||
| The unsigned workflow handoff is identified by its exact source commit, not by | ||
| an RC number. Assign the RC identity only when creating the immutable candidate | ||
| tag and staging directory for the bytes selected by the Release Manager. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| 1. The intended version is committed to the root `package.json` on `main`. | ||
| 2. Normal CI is green for the exact commit. | ||
| 3. The PPMC and mentors have confirmed that provenance, `LICENSE`, `NOTICE`, and | ||
| `DISCLAIMER-WIP` are ready for an Incubator release vote. | ||
| 4. The Release Manager has a public ASF-associated RSA PGP key whose actual | ||
| signing key or subkey is at least 2048 bits and whose full fingerprint can be | ||
| reviewed independently. New keys should be 4096-bit RSA. | ||
| 5. The podling `KEYS` file contains that public key and is published from the | ||
| Apache distribution area, not only from GitHub. | ||
|
|
||
| ## Build and test an unsigned candidate | ||
|
|
||
| Run **Prepare ASF source candidate** from `main`, supplying the exact version. | ||
| The workflow: | ||
|
|
||
| 1. builds the archive from the dispatched Git commit rather than the working | ||
| tree; | ||
| 2. generates and validates SHA-512; | ||
| 3. checks the archive identity and required legal documents; | ||
| 4. extracts the exact archive into a clean directory; | ||
| 5. installs, audits, builds, type-checks, runs release checks, and tests from | ||
| that extracted directory; and | ||
| 6. uploads an unsigned workflow artifact for Release Manager handoff. | ||
|
|
||
| An equivalent unsigned archive can be created locally: | ||
|
|
||
| ```sh | ||
| npm run release:asf:source -- \ | ||
| --version <version> \ | ||
| --revision <full-commit-sha> | ||
|
|
||
| npm run release:asf:verify -- \ | ||
| --artifact release/asf/apache-maka-<version>-incubating-src.tar.gz | ||
| ``` | ||
|
|
||
| Creation refuses to overwrite existing output. Remove or move a private local | ||
| attempt before rebuilding; never overwrite a staged or voted candidate. | ||
| Different gzip implementations may encode the same source tar payload into | ||
| different compressed bytes, so use the workflow artifact as the candidate that | ||
| will be signed rather than substituting a locally compressed archive. | ||
|
|
||
| Before opening the vote, create the candidate tag at the exact archived commit | ||
| and publish it through the normal reviewed Git process: | ||
|
|
||
| ```sh | ||
| git tag -s v<version>-incubating-rc<rc> <full-commit-sha> | ||
| git verify-tag v<version>-incubating-rc<rc> | ||
| git show --no-patch --format=fuller v<version>-incubating-rc<rc>^{commit} | ||
| ``` | ||
|
|
||
| Pushing the tag is a separate authenticated maintainer action. Confirm its | ||
| target and signature before publishing it; the automation does not push tags. | ||
|
|
||
| ## Sign locally | ||
|
|
||
| Never place a Release Manager's private PGP key in GitHub Actions or the | ||
| repository. Download the unsigned workflow artifact and its SHA-512 file onto | ||
| the Release Manager's machine. In a clean, trusted checkout containing the | ||
| verified candidate tag, run: | ||
|
|
||
| ```sh | ||
| npm run release:asf:sign -- \ | ||
| --artifact <candidate-dir>/apache-maka-<version>-incubating-src.tar.gz \ | ||
| --key <full-pgp-fingerprint> \ | ||
| --revision v<version>-incubating-rc<rc> | ||
| ``` | ||
|
|
||
| The signing command first validates the downloaded SHA-512 and archive. It then | ||
| rebuilds the canonical uncompressed Git archive from the specified revision on | ||
|
M4n5ter marked this conversation as resolved.
|
||
| the Release Manager's hardware, isolated from repository-local, user, and system | ||
| Git attributes, and requires that source payload to be byte-for-byte identical | ||
| to the downloaded candidate after decompression. The original downloaded | ||
| candidate is then signed with SHA-512 after resolving the selected secret key to | ||
| the exact full fingerprint. Comparing the canonical payload avoids treating a | ||
| platform's gzip encoding as source identity. A workflow-produced digest alone | ||
| is not an independent trust check and is insufficient for signing. | ||
|
|
||
| Start from a reviewed copy of the current podling `KEYS` file, then append the | ||
| matching public key when needed: | ||
|
|
||
| ```sh | ||
| (gpg --list-sigs <full-pgp-fingerprint> && gpg --armor --export <full-pgp-fingerprint>) >> KEYS | ||
| gpg --show-keys --with-fingerprint KEYS | ||
| ``` | ||
|
|
||
| Review the combined file before publishing it. Retain every key that has been | ||
| used to sign an Apache Maka release. | ||
|
|
||
| Verify the complete signed candidate in a temporary keyring populated only | ||
| from the reviewed `KEYS` file: | ||
|
|
||
| ```sh | ||
| npm run release:asf:verify -- \ | ||
| --artifact <candidate-dir>/apache-maka-<version>-incubating-src.tar.gz \ | ||
| --keys <path-to-reviewed-KEYS> | ||
| ``` | ||
|
|
||
| Supplying `--keys` requires a detached signature. Verification rejects signing | ||
| keys or subkeys that are not RSA with at least 2048 bits, expired or revoked | ||
| keys and signatures, SHA-1 or any digest outside the accepted set, and bad or | ||
| missing signatures. SHA-256, SHA-384, and SHA-512 signatures are accepted. | ||
|
|
||
| ## Stage on Apache dist/dev | ||
|
|
||
| Release Managers need ASF commit access to the distribution repository. Check | ||
| out the podling development area, create a new immutable RC directory, and add | ||
| only the source archive, SHA-512 file, and detached signature: | ||
|
|
||
| ```sh | ||
| svn checkout https://dist.apache.org/repos/dist/dev/incubator/maka maka-dist-dev | ||
| mkdir maka-dist-dev/<version>-incubating-rc<rc> | ||
| cp apache-maka-<version>-incubating-src.tar.gz{,.sha512,.asc} \ | ||
| maka-dist-dev/<version>-incubating-rc<rc>/ | ||
| svn add maka-dist-dev/<version>-incubating-rc<rc> | ||
| svn commit maka-dist-dev -m "Stage Apache Maka <version> incubating RC<rc>" | ||
| ``` | ||
|
|
||
| Publish or update `KEYS` at the podling distribution root through the same | ||
| reviewed ASF distribution process. Confirm the staged HTTPS URLs before sending | ||
| the vote email. | ||
|
|
||
| ## Independent verification | ||
|
|
||
| Before casting a binding `+1`, every voter must download all signed source | ||
| packages and the published `KEYS` over HTTPS onto their own hardware, validate | ||
| ASF release-policy compliance and all cryptographic signatures, inspect the | ||
| archive, and compile/test the extracted source. Non-binding voters are strongly | ||
| encouraged to perform the same checks. Voters should record the commit, SHA-512, | ||
| signing-key fingerprint, platform, and commands used. | ||
|
|
||
| ## Vote requirements | ||
|
|
||
| Both the podling and Incubator PMC review periods should normally remain open | ||
| for at least 72 hours. A shortened vote is only for exceptional expedited | ||
| releases; the vote email must explain why it is expedited, and the deviation | ||
| must be reported through the ASF process. | ||
|
|
||
| The podling vote passes only with at least three PPMC `+1` votes and more `+1` | ||
| than `-1` votes. After that result is summarized to the Incubator general list, | ||
| the release requires at least three Incubator PMC `+1` votes and more binding | ||
| `+1` than binding `-1` votes. | ||
|
|
||
| ## Podling vote template | ||
|
|
||
| Send to `dev@maka.apache.org` and allow at least 72 hours. | ||
|
|
||
| ```text | ||
| Subject: [VOTE] Release Apache Maka <version> (incubating) RC<rc> | ||
|
|
||
| Hello Apache Maka community, | ||
|
|
||
| This is a vote to release Apache Maka <version> (incubating), release candidate <rc>. | ||
|
|
||
| The source candidate: | ||
| <dist-dev-candidate-url> | ||
|
|
||
| The source commit: | ||
| <commit-url-and-full-sha> | ||
|
|
||
| The KEYS file: | ||
| <published-keys-url> | ||
|
|
||
| Please review and vote: | ||
| [ ] +1 Release this package | ||
| [ ] 0 No opinion | ||
| [ ] -1 Do not release this package (please provide the reason) | ||
|
|
||
| The vote will remain open for at least 72 hours. | ||
| ``` | ||
|
|
||
| After the podling vote passes, send a vote to | ||
| `general@incubator.apache.org`, linking the podling result and presenting the | ||
| same immutable candidate bytes. Apply the requirements above independently to | ||
| this Incubator PMC vote. | ||
|
|
||
| ## Publish after approval | ||
|
|
||
| Only after both required votes pass, copy the exact approved files from the | ||
| development distribution area to the appropriate Apache release distribution | ||
| area, update the download page, and announce the release. Do not rebuild or | ||
| rename the approved archive during promotion. | ||
|
|
||
| Current policy references: | ||
|
|
||
| - https://incubator.apache.org/guides/releasemanagement.html | ||
| - https://incubator.apache.org/guides/distribution.html | ||
| - https://www.apache.org/legal/release-policy.html | ||
| - https://infra.apache.org/release-distribution.html | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| name: Prepare ASF source candidate | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
|
M4n5ter marked this conversation as resolved.
|
||
| inputs: | ||
| version: | ||
| description: Exact version from the root package.json | ||
| required: true | ||
| type: string | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: asf-source-${{ github.sha }} | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| candidate: | ||
| name: Build and test source archive | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 60 | ||
| env: | ||
| CANDIDATE_PATH: release/asf/apache-maka-${{ inputs.version }}-incubating-src.tar.gz | ||
| RELEASE_VERSION: ${{ inputs.version }} | ||
| steps: | ||
| - name: Require main | ||
| env: | ||
| RELEASE_REF: ${{ github.ref }} | ||
| run: | | ||
| if [[ "$RELEASE_REF" != "refs/heads/main" ]]; then | ||
| echo "ASF source candidates must be dispatched from main; found $RELEASE_REF" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Check out the candidate commit | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| ref: ${{ github.sha }} | ||
|
M4n5ter marked this conversation as resolved.
|
||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up Node.js | ||
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | ||
| with: | ||
| node-version: '24' | ||
| cache: npm | ||
|
|
||
| - name: Select the repository npm toolchain | ||
| run: npm install --global --no-audit --no-fund "$(node -p 'require("./package.json").packageManager')" | ||
|
|
||
| - name: Install Linux runtime dependencies | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y bubblewrap ripgrep | ||
| if [[ -e /proc/sys/kernel/apparmor_restrict_unprivileged_userns ]]; then | ||
| sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 | ||
| fi | ||
| if [[ -e /proc/sys/kernel/unprivileged_userns_clone ]]; then | ||
| sudo sysctl -w kernel.unprivileged_userns_clone=1 | ||
| fi | ||
|
|
||
| - name: Create the unsigned source candidate | ||
| run: | | ||
| npm run release:asf:source -- \ | ||
| --version "$RELEASE_VERSION" \ | ||
| --revision "$GITHUB_SHA" | ||
|
|
||
| - name: Extract the exact candidate | ||
| run: | | ||
| mkdir candidate-source | ||
| tar -xzf "$CANDIDATE_PATH" \ | ||
| --strip-components=1 \ | ||
| -C candidate-source | ||
|
|
||
| - name: Install from the committed lockfile | ||
| working-directory: candidate-source | ||
| run: npm ci | ||
|
|
||
| - name: Audit production dependencies | ||
| working-directory: candidate-source | ||
| run: npm audit --omit=dev --audit-level=moderate | ||
|
M4n5ter marked this conversation as resolved.
|
||
|
|
||
| - name: Verify source attribution inventories | ||
|
M4n5ter marked this conversation as resolved.
|
||
| working-directory: candidate-source | ||
| run: | | ||
| npm run check:third-party-notices | ||
| npm run check:cli-third-party-notices | ||
| npm run check:windows-cargo-notices | ||
|
|
||
| - name: Lint and format-check the extracted source | ||
| working-directory: candidate-source | ||
| run: | | ||
| npm run lint | ||
| npm run format:check | ||
|
|
||
| - name: Build and type-check the extracted source | ||
| working-directory: candidate-source | ||
| run: | | ||
| npm run build | ||
| npm run typecheck | ||
|
|
||
| - name: Run ASF source checks from the extracted source | ||
| working-directory: candidate-source | ||
| run: npm run check:asf-source | ||
|
M4n5ter marked this conversation as resolved.
|
||
|
|
||
| - name: Test the extracted source | ||
| working-directory: candidate-source | ||
| run: npm test | ||
|
|
||
| - name: Upload the verified unsigned candidate | ||
| id: candidate | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: apache-maka-${{ inputs.version }}-incubating-${{ github.sha }}-unsigned | ||
| path: | | ||
| ${{ env.CANDIDATE_PATH }} | ||
| ${{ env.CANDIDATE_PATH }}.sha512 | ||
| if-no-files-found: error | ||
| compression-level: 0 | ||
| retention-days: 30 | ||
|
|
||
| - name: Record Release Manager handoff | ||
| env: | ||
| ARTIFACT_URL: ${{ steps.candidate.outputs.artifact-url }} | ||
| RUNBOOK_URL: ${{ github.server_url }}/${{ github.repository }}/blob/${{ github.sha }}/.github/ASF_SOURCE_RELEASE.md | ||
| run: | | ||
| { | ||
| echo "## Unsigned ASF source candidate" | ||
| echo | ||
| echo "- Version: \`$RELEASE_VERSION\`" | ||
| echo "- Commit: \`$GITHUB_SHA\`" | ||
| echo "- Artifact: $ARTIFACT_URL" | ||
| echo "- Release Manager runbook: $RUNBOOK_URL" | ||
| echo | ||
| echo "This workflow does not create an ASF release candidate by itself." | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.