Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .github/ASF_NPM_RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Apache Maka npm convenience candidate runbook

This runbook prepares the `maka-agent` npm convenience artifact for the first
Apache Maka (Incubating) release. The official ASF release is the source
release. The npm package is an additional distribution form and must be built
from the exact source release commit, reviewed independently, and published
only after the source release is approved.

## Candidate contract

For version `<version>` and source release candidate `<rc>`, the workflow
produces one unsigned handoff artifact containing:

- `maka-agent-<version>.tgz`;
- the existing SHA-256 sidecar used by the npm publication verifier;
- `maka-agent-<version>.tgz.sha512` for ASF release review;
- the npm pack file inventory; and
- `maka-agent-<version>.tgz.asf-candidate.json`, which binds those exact bytes
to the source reference `v<version>-incubating-rc<rc>`, its full commit, and
the producing workflow run attempt.

The workflow builds the tarball once and passes the same bytes through the
Linux, macOS, Windows, and Eval validation matrix. It does not call npm
staging, publish a package, modify a dist-tag, sign the tarball, or establish
that the source candidate has passed either required vote.

## Prerequisites

1. The intended source candidate tag
`v<version>-incubating-rc<rc>` exists as a signed annotated tag at a commit
on `main`. The Release Manager has independently verified the tag signature
with the trusted ASF `KEYS` material as required by the source-release
runbook.
2. The root product version and `packages/cli/package.json` version both equal
`<version>` at that commit.
3. The source candidate was prepared and reviewed under
[ASF_SOURCE_RELEASE.md](./ASF_SOURCE_RELEASE.md).
4. The reusable CLI package validation workflow is green for the exact commit.

## Prepare the unsigned candidate

Dispatch **Prepare ASF npm candidate** from the exact source candidate tag:

```sh
version=0.1.11
rc=1
source_reference_tag="v${version}-incubating-rc${rc}"
gh workflow run asf-npm-candidate.yml \
--ref "$source_reference_tag"
```

The workflow rejects a fork repository, a lightweight tag, a tag/version/RC
mismatch, a tag that does not resolve to the dispatched commit, and a commit
outside current `main`. These checks pin a source reference; they do not
authenticate its signature or establish source-release approval. Its reusable
validation job builds one clean-source npm tarball and tests that exact artifact
across the supported platform matrix. The final job adds only the source
reference/run record and uploads a new handoff artifact; it never rebuilds the
tarball. The handoff requires validation from the current workflow attempt. If
validation or handoff must be retried, re-run the validation job and its
dependent jobs; the handoff independently revalidates the live source tag, so
the successful resolve job does not need to be repeated.

## Verify the handoff

After downloading and extracting the workflow artifact, verify the record and
both checksum sidecars from a trusted checkout of the same source commit:

```sh
npm run release:asf:npm:verify -- \
<candidate-dir>/maka-agent-<version>.tgz.asf-candidate.json
```

Review the JSON record directly and confirm the full source commit, source
reference tag, workflow run ID, and run attempt against GitHub. Treat the
tarball as immutable after it enters release review. Any byte change requires a
new npm package version and, when the source commit changes, a new source RC.

## Publication boundary

This preparation workflow is deliberately credential-free. Before any npm
approval or public publication, G8 still requires:

- artifact-specific legal and dependency review;
- the reviewed detached-signature path and independent signature verification;
- the mentor/IPMC decision on the npm package name;
- a recorded successful source-release vote result;
- PPMC-controlled npm ownership, OIDC, 2FA recovery, and approval; and
- verification that npm staging and the public registry preserve these exact
candidate bytes and their provenance.

Do not infer source-release approval from a successful candidate workflow or
from the existence of an RC tag.

## References

- https://incubator.apache.org/guides/distribution.html#npm
- https://incubator.apache.org/guides/releasemanagement.html
- https://www.apache.org/legal/release-policy.html
- https://www.apache.org/legal/resolved.html
146 changes: 146 additions & 0 deletions .github/workflows/asf-npm-candidate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
name: Prepare ASF npm candidate

on:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: asf-npm-${{ github.ref }}
cancel-in-progress: false

jobs:
resolve:
name: Resolve source reference
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
version: ${{ steps.source.outputs.version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
fetch-depth: 0
persist-credentials: false
- name: Resolve the requested source reference
id: source
env:
RELEASE_REF: ${{ github.ref }}
RELEASE_REPOSITORY: ${{ github.repository }}
SOURCE_REFERENCE_TAG: ${{ github.ref_name }}
run: |
if [[ "$RELEASE_REPOSITORY" != "apache/maka" ]]; then
echo "ASF npm candidates must run in apache/maka; found $RELEASE_REPOSITORY" >&2
exit 1
fi
release_version="$(node scripts/asf-npm-candidate.mjs source-version "$SOURCE_REFERENCE_TAG")"
if [[ "$RELEASE_REF" != "refs/tags/$SOURCE_REFERENCE_TAG" ]]; then
echo "ASF npm candidates must be dispatched from $SOURCE_REFERENCE_TAG; found $RELEASE_REF" >&2
exit 1
fi
git fetch --force --no-tags origin \
"refs/tags/$SOURCE_REFERENCE_TAG:refs/tags/$SOURCE_REFERENCE_TAG"
test "$(git cat-file -t "refs/tags/$SOURCE_REFERENCE_TAG")" = tag
tag_commit="$(git rev-parse "refs/tags/$SOURCE_REFERENCE_TAG^{commit}")"
test "$tag_commit" = "$GITHUB_SHA"
git fetch --no-tags origin main:refs/remotes/origin/main
git merge-base --is-ancestor "$GITHUB_SHA" origin/main
echo "version=$release_version" >> "$GITHUB_OUTPUT"

validate:
name: Validate immutable npm candidate
needs: resolve
permissions:
contents: read
uses: ./.github/workflows/cli-package-validation.yml
with:
source_commit: ${{ github.sha }}

handoff:
name: Record unsigned ASF npm candidate
needs: [resolve, validate]
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Require a current validated artifact
env:
CURRENT_RUN_ATTEMPT: ${{ github.run_attempt }}
VALIDATE_RUN_ATTEMPT: ${{ needs.validate.outputs.release_candidate_run_attempt }}
run: |
if [[ "$VALIDATE_RUN_ATTEMPT" != "$CURRENT_RUN_ATTEMPT" ]]; then
echo "The validated candidate must come from this workflow attempt; re-run validation and its dependent jobs." >&2
exit 1
fi
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22.19.0'
package-manager-cache: false
- name: Download the exact validated npm candidate
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
artifact-ids: ${{ needs.validate.outputs.release_candidate_artifact_id }}
path: packages/cli/release
- name: Bind the npm candidate to the source reference
env:
RELEASE_VERSION: ${{ needs.resolve.outputs.version }}
SOURCE_REFERENCE_TAG: ${{ github.ref_name }}
SOURCE_COMMIT: ${{ github.sha }}
RELEASE_REPOSITORY: ${{ github.repository }}
RELEASE_RUN_ID: ${{ github.run_id }}
RELEASE_RUN_ATTEMPT: ${{ github.run_attempt }}
run: |
npm run release:asf:npm:record -- \
packages/cli/release \
"$SOURCE_REFERENCE_TAG" \
"$SOURCE_COMMIT" \
"$RELEASE_REPOSITORY" \
"$RELEASE_RUN_ID" \
"$RELEASE_RUN_ATTEMPT"
npm run release:asf:npm:verify -- \
"packages/cli/release/maka-agent-${RELEASE_VERSION}.tgz.asf-candidate.json"
- name: Revalidate the live source reference
env:
SOURCE_REFERENCE_TAG: ${{ github.ref_name }}
SOURCE_COMMIT: ${{ github.sha }}
run: |
git fetch --force --no-tags origin \
"refs/tags/$SOURCE_REFERENCE_TAG:refs/tags/$SOURCE_REFERENCE_TAG"
test "$(git cat-file -t "refs/tags/$SOURCE_REFERENCE_TAG")" = tag
test "$(git rev-parse "refs/tags/$SOURCE_REFERENCE_TAG^{commit}")" = "$SOURCE_COMMIT"
git fetch --no-tags origin main:refs/remotes/origin/main
git merge-base --is-ancestor "$SOURCE_COMMIT" origin/main
- name: Upload the unsigned ASF npm candidate
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: apache-maka-npm-${{ github.ref_name }}-${{ github.sha }}-unsigned
path: |
packages/cli/release/*.tgz
packages/cli/release/*.tgz.sha256
packages/cli/release/*.tgz.sha512
packages/cli/release/*.tgz.files.json
packages/cli/release/*.tgz.asf-candidate.json
if-no-files-found: error
retention-days: 14
- name: Record Release Manager handoff
env:
RELEASE_VERSION: ${{ needs.resolve.outputs.version }}
SOURCE_REFERENCE_TAG: ${{ github.ref_name }}
SOURCE_COMMIT: ${{ github.sha }}
RUNBOOK_URL: https://github.com/${{ github.repository }}/blob/${{ github.sha }}/.github/ASF_NPM_RELEASE.md
run: |
{
echo "## Unsigned ASF npm convenience candidate"
echo
echo "- Version: \`$RELEASE_VERSION\`"
echo "- Source reference: \`$SOURCE_REFERENCE_TAG\`"
echo "- Source commit: \`$SOURCE_COMMIT\`"
echo "- Runbook: $RUNBOOK_URL"
echo
echo "This workflow does not publish to npm and does not establish source-release approval."
} >> "$GITHUB_STEP_SUMMARY"
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ jobs:
- name: Test AX tree audit contract
run: node --test scripts/ax-tree-audit.test.mjs

- name: Verify ASF npm candidate mechanics
run: npm run check:asf-npm

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
if: steps.plan.outputs.code == 'true' || steps.plan.outputs.astryx_surface == 'true' || steps.plan.outputs.asf_source == 'true' || steps.plan.outputs.cli_package == 'true' || steps.plan.outputs.release_contract == 'true'
with:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/cli-package-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ on:
release_candidate_artifact_id:
description: Immutable artifact produced by the build job
value: ${{ jobs.build.outputs.release_candidate_artifact_id }}
release_candidate_run_attempt:
description: Workflow attempt that built the immutable artifact
value: ${{ jobs.build.outputs.release_candidate_run_attempt }}
workflow_dispatch:

permissions:
Expand All @@ -27,6 +30,7 @@ jobs:
timeout-minutes: 60
outputs:
release_candidate_artifact_id: ${{ steps.release-candidate.outputs.artifact-id }}
release_candidate_run_attempt: ${{ github.run_attempt }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,16 @@
"release:asf:verify": "node scripts/asf-source-release.mjs verify",
"release:asf:sign": "node scripts/asf-source-release.mjs sign",
"check:asf-source": "node --test scripts/asf-source-release.test.mjs scripts/asf-source-workflow-policy.test.mjs",
"release:asf:npm:record": "node scripts/asf-npm-candidate.mjs record",
"release:asf:npm:verify": "node scripts/asf-npm-candidate.mjs verify",
"check:asf-npm": "node --test scripts/asf-npm-candidate.test.mjs scripts/asf-npm-workflow-policy.test.mjs",
"check:product-release-identity": "node scripts/product-release-identity.mjs",
"package:cli:macos-arm64": "node scripts/package-macos-arm64-cli.mjs",
"verify:cli:macos-arm64": "node scripts/verify-macos-arm64-cli.mjs",
"test:product-release": "node --test scripts/product-release.test.mjs scripts/product-release-artifacts.test.mjs scripts/product-release-authority.test.mjs",
"generate:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs",
"check:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs --check",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && npm run check:product-release-identity && node --test scripts/product-release.test.mjs scripts/product-release-artifacts.test.mjs scripts/product-release-authority.test.mjs scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-runtime-host-diagnostics.test.mjs scripts/release-cli-workflow-policy.test.mjs scripts/verify-packaged-app.test.mjs scripts/third-party-closure.test.mjs scripts/generate-third-party-notices.test.mjs",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && npm run check:product-release-identity && npm run check:asf-npm && node --test scripts/product-release.test.mjs scripts/product-release-artifacts.test.mjs scripts/product-release-authority.test.mjs scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-runtime-host-diagnostics.test.mjs scripts/release-cli-workflow-policy.test.mjs scripts/verify-packaged-app.test.mjs scripts/third-party-closure.test.mjs scripts/generate-third-party-notices.test.mjs",
"package:macos-arm64": "node scripts/package-macos-arm64.mjs",
"verify:macos-arm64": "node scripts/verify-macos-arm64-dmg.mjs",
"package:windows-x64": "node scripts/package-windows-x64.mjs",
Expand Down
Loading