diff --git a/.agents/skills/release/SKILL.md b/.agents/skills/release/SKILL.md index 1e6643a3..0fa9ee3f 100644 --- a/.agents/skills/release/SKILL.md +++ b/.agents/skills/release/SKILL.md @@ -1,6 +1,6 @@ --- name: release -description: Prepare, resume, or verify Apache Asyncband releases when release-manager work is requested, including candidate artifacts, votes, publication, and recovery. +description: Run or resume an Apache Asyncband (Incubating) release, from its tracking issue and frozen source through RC composition, ATR voting, publication, and follow-up; also verify an existing candidate. --- -# Release Apache Asyncband +# Release Apache Asyncband (Incubating) -Help the release manager carry out the requested release work, explain the current state, and propose practical next steps. Keep release coordination in the main conversation. Delegate substantial candidate verification when it can run independently, and use the shared license-audit skill for the licensing review. The release manager and project community make release decisions. +Use the release tracking issue to coordinate the release. The normal path assumes that signing, GitHub environments, ATR, and crates.io Trusted Publishing are already configured: GitHub builds and stages the candidate; ATR runs the votes and publishes the approved source; the final Git tag publishes the convenience crate. -## Resume the requested work +Follow the steps below for a new release, or resume from the tracking issue and current GitHub/ATR state. For an independent candidate check, use [verification](references/verification.md). -Establish the requested scope and what has already happened from the conversation, current checkout, and relevant external records. Read the affected parts of `asyncband/Cargo.toml`, `Cargo.lock`, `CHANGELOG.md`, `.github/workflows/release.yml`, `.asf.yaml`, and `xtask/src/main.rs` when needed. Use live GitHub, ASF distribution, mailing-list archives, and registry records to resolve uncertain state. Load only the reference for the current phase; an existing candidate does not require repeating preparation or setup. +## 1. Choose the version and open the tracking issue -Carry forward the user's existing authorization. A status check, review, or plan stays read-only. For execution, complete authorized work and prepare any proposed external action before asking about authorization that is actually missing. Sending vote or announcement messages, publishing, merging, or changing tags needs authorization for that action; opening this skill does not provide it. Preserve the user's work when selecting a checkout or creating a release worktree. +Choose the stable `VERSION` (`X.Y.Z`) from the changes since the previous published release; the semver check below validates compatibility. Record a source cutoff commit and any work deferred to a later release. -Keep these values and supporting links in the conversation so work can resume across turns: +Find an existing `Tracking Issue to Release ${VERSION}` before creating one. Otherwise, create it immediately from the [tracking issue template](references/tracking-issue.md). Use it as the release record: link the release PR, checked revisions, workflow runs, candidate, votes, and publication results; update completed items with evidence and keep the next action current. -- `VERSION`: the final crate version, such as `0.7.2`; RCs do not change the package version. -- `RC`: the positive candidate number; `RC_TAG` is `v${VERSION}-rc.${RC}`. -- `RELEASE_COMMIT`: the merged release pull request commit bound to the candidate. -- `RELEASE_DIR`: an absolute working directory outside the repository for artifacts, verification, and SVN checkouts; reuse it while continuing the same candidate. -- Candidate tag and artifact location, checksum/signature results, relevant CI runs, PPMC/IPMC vote threads and results, and completed publication steps. +## 2. Audit first, prepare the version, and freeze the source -Report completed work with evidence, the next useful step, and any input still needed. Distinguish pending, failed, and unverified steps. Keep handoff notes in the conversation unless the user requests a file. +Audit the selected checkout with the `license-audit` skill. -## Choose the current phase +Prepare a release PR from the chosen scope: update `asyncband/Cargo.toml`, refresh `Cargo.lock` with Cargo, and move the final user-visible changelog entries from `Unreleased` into an undated `v${VERSION}` section, leaving `Unreleased` empty. Include necessary audit corrections; defer unrelated source changes. Respect required CI and merge authorization. -| Current work | Read | -| ------------------------------------------------- | ---------------------------------------------------- | -| Version/changelog PR, RC, artifacts, or staging | [Candidate preparation](references/candidate.md) | -| Checking an existing candidate's artifacts | [Candidate verification](references/verification.md) | -| Voting, approved publication, follow-up, or retry | [Publication](references/publication.md) | +Review the merged PR's tree, including any changes added since the source cutoff, and update affected audit results. Record its merge commit as `RELEASE_COMMIT` in the issue. Use a detached checkout of this frozen snapshot for all remaining checks and tags: -The phase guides are the maintained release procedure for both people and agents. Existing infrastructure is described in [Infrastructure](references/infrastructure.md); read it only for configuration changes, a new release manager's signing key, or infrastructure troubleshooting. +```shell +RELEASE_DIR="$(mktemp -d "${TMPDIR:-/tmp}/asyncband-release.XXXXXX")" +git fetch https://github.com/apache/asyncband.git main +git worktree add --detach "${RELEASE_DIR}/checkout" "${RELEASE_COMMIT}" +cd "${RELEASE_DIR}/checkout" +test -z "$(git status --porcelain)" +``` -Links within this skill resolve from the containing document and stay within this skill's files. Repository paths such as `.github/workflows/release.yml` resolve from the caller's Asyncband repository root, which may differ from the current working directory. Locate the `license-audit` skill and configured agents by name; if skill discovery is unavailable, read `.agents/skills/license-audit/SKILL.md` from that repository root. Do not infer repository locations by walking upward from this skill's installation directory. +Keep scratch artifacts outside the repository. If a check requires a correction, prepare a new reviewed commit and replace the recorded snapshot explicitly; recheck the affected results. After an RC tag exists, changed release contents require a new RC number. Never silently move an existing tag or change the files under a vote. -Read `cargo x --help` and the relevant subcommand help before running repository checks. +## 3. Check the frozen checkout -## Delegate candidate verification +Read `cargo x --help` and each relevant subcommand's help, then run: -Use the `release_verifier` Codex agent for a substantial check of an existing candidate when the main agent can continue independent work, such as preparing vote materials. Other coding agents can delegate the same candidate-verification guide to a worker or follow it directly. Keep a small status query in the main conversation. +```shell +cargo x lint +cargo x check +cargo x test --no-capture +RUSTUP_TOOLCHAIN=1.86.0 cargo x test --no-capture +cargo x semver --release-version "${VERSION}" +cargo publish --package asyncband --locked --dry-run +``` -Give the verifier the repository root, candidate commit and tag, version, absolute artifact paths, expected signing fingerprint and its provenance, requested checks, and a scratch directory outside the checkout. It returns the checked revision and artifacts, observed results, and remaining gaps. Preserve the original artifacts for a separate `license-audit` review; the verifier does not duplicate that audit. Collect the results before staging or publishing the candidate. +Record results against `RELEASE_COMMIT`, including its required CI result. Pass the generated `target/package/asyncband-${VERSION}.crate` to the license review. -## Candidate and publication continuity +For a semver-major release, including a pre-1.0 minor bump, document and review expected API breaks before rerunning `cargo x semver --release-version "${VERSION}" --acknowledge-breaking-changes`. An incompatible patch release needs a corrected version or source before the snapshot can pass. -The signed source archive approved by the Apache Incubator PMC and published through ASF distribution is the official Apache release. Its name is `apache-asyncband-${VERSION}-incubating-src.tar.gz`. The crates.io package is a convenience distribution from the same approved commit; keep its Cargo-generated name and layout. +## 4. Push the RC and verify the ATR candidate -Keep the RC tag, commit, artifacts, and vote tied together. A later `main` commit does not invalidate an existing candidate. Reuse an existing signed tag and staged bytes when retrying a transient failure. If candidate content changes or the community rejects it, agree on the replacement candidate and increment `RC`; preserve existing tags rather than rewriting them. +Use the release manager's personal OpenPGP code-signing key. It must be valid for signing, have a user ID containing their `@apache.org` address, and have its public key published in the [KEYS](https://downloads.apache.org/incubator/asyncband/KEYS) file. List the matching local keys and set `SIGNING_KEY_FINGERPRINT` to the selected key's primary fingerprint: -After both vote results record approval, promote the exact voted source artifacts. The signed final `v${VERSION}` tag uses the approved RC commit and starts the crates.io publication workflow, subject to the configured `release` environment review. Successful CI alone does not establish vote approval. Confirm each external action's result before reporting completion or retrying it. +```shell +gpg --list-secret-keys --with-fingerprint '@apache.org' +SIGNING_KEY_FINGERPRINT='' +``` -Use the shared `license-audit` skill to examine the relevant checkout or artifact contents. In Codex, the configured `license_auditor` can perform a delegated review; another agent can follow the same skill directly. Provide the candidate revision and actual artifact paths, then discuss the review's evidence and suggestions with the release manager. +Set `RC` to the next unused positive candidate number. The package and ATR version remain `${VERSION}`; only the Git tag carries the RC suffix: -Follow the current [ASF Release Policy](https://www.apache.org/legal/release-policy.html), [Release Distribution Policy](https://infra.apache.org/release-distribution), [Release Creation Process](https://infra.apache.org/release-publishing.html), and [Incubator release guidance](https://incubator.apache.org/guides/releasemanagement.html). Explain any relevant ambiguity with its source and practical options instead of treating incomplete evidence as a project defect. +```shell +RC_TAG="v${VERSION}-rc.${RC}" +test "$(git rev-parse HEAD)" = "${RELEASE_COMMIT}" +test -z "$(git status --porcelain)" +git tag --sign --local-user "${SIGNING_KEY_FINGERPRINT}" "${RC_TAG}" --message "Apache Asyncband (Incubating) ${VERSION} release candidate ${RC}" "${RELEASE_COMMIT}" +git verify-tag "${RC_TAG}" +git push https://github.com/apache/asyncband.git "${RC_TAG}" +``` + +Follow both workflows for this tag: + +- `Release` checks the `${VERSION}` Cargo package. +- `Compose source release` builds `apache-asyncband-${VERSION}-incubating-src.tar.gz` and its checksum. Approve its `release` environment job to sign with the automated project key and upload the archive, `.asc`, and `.sha512` to ATR project `asyncband`, version `${VERSION}`. + +Open the candidate in [ATR](https://releases.apache.org/projects/asyncband), inspect its checks, and record its URL, ATR revision, workflow run, and SHA-512 in the issue. Download that revision and complete [verification](references/verification.md) and `license-audit` on the actual distributions before voting. The `release_verifier` and `license_auditor` agents can perform these checks independently; reuse completed checks for the same candidate. + +## 5. Vote and publish through ATR + +Follow the [ATR operations guide](references/atr.md). In the configured email-vote mode, the normal sequence is: + +1. Review the candidate and vote email, then start the PPMC vote in ATR. +2. After at least 72 hours and sufficient PPMC votes, review the tally and resolve it as `Passed`. ATR sends the result and starts the IPMC vote automatically; check that thread and supply the PPMC result/tally link and any carried IPMC votes if missing. +3. After at least another 72 hours and sufficient binding IPMC votes, review and resolve that vote as `Passed`. ATR sends the result and moves the release to Finish. +4. In Finish, publish the exact approved revision to ASF distribution, or verify the completed automatic publication if it was enabled. Keep the candidate bytes unchanged. + +Record both rounds' `[VOTE]` threads and `[RESULT]` messages in the tracking issue using their `lists.apache.org` permalinks. + +## 6. Publish the crate and close the issue + +Once both votes have passed and the source publication is confirmed, create the signed final tag at the approved commit: + +```shell +git verify-tag "${RC_TAG}" +test "$(git rev-parse "${RC_TAG}^{commit}")" = "${RELEASE_COMMIT}" +git tag --sign --local-user "${SIGNING_KEY_FINGERPRINT}" "v${VERSION}" --message "Apache Asyncband (Incubating) ${VERSION}" "${RELEASE_COMMIT}" +git push https://github.com/apache/asyncband.git "v${VERSION}" +``` + +Approve the final tag's `release` environment deployment in `release.yml`, then verify crates.io, docs.rs, and the ASF downloads. Use ATR's Announce action after both distributions are available; review the message and recipients and record the announcement. Submit the changelog publication-date PR, confirm any superseded-release archival, and close the tracking issue after its required items are complete. Remove the detached worktree and scratch directory when no longer needed. + +## Resume or recover + +Use the issue's frozen commit and candidate revision rather than the latest `main`. Reuse an existing tag after checking its signature and target. For uncertain uploads, votes, or publications, inspect ATR and the destination before repeating an action; signing/upload retries can create a new signature and revision. See [ATR recovery](references/atr.md#recover-without-replacing-voted-files). For a failed crates.io run, check whether that version already exists before retrying; a published package is immutable. + +Consult [infrastructure](references/infrastructure.md) for setup or access failures. Resolve repository paths from the caller's supplied repository root. diff --git a/.agents/skills/release/references/atr.md b/.agents/skills/release/references/atr.md new file mode 100644 index 00000000..b980a2f1 --- /dev/null +++ b/.agents/skills/release/references/atr.md @@ -0,0 +1,59 @@ + + +# Operate the release in ATR + +Open the [Asyncband project](https://releases.apache.org/projects/asyncband) with the release manager's ASF login. Use email voting: voters reply on the mailing lists and the release manager reviews the tally in ATR. + +Fill the tracking issue's ATR vote page and communication links as each step completes. Use `lists.apache.org` permalinks for the PPMC and IPMC vote threads, their result messages, and the announcement. Each result link should open the message containing the outcome and tally. + +## Start the vote + +1. Open the uploaded `${VERSION}` draft in Compose. Confirm the revision and checksum recorded in the tracking issue, inspect ATR's checks, and complete the candidate verification and artifact license review. Resolve blockers and review any concerns before acknowledging them. If ATR's commit field is empty, enter the frozen `RELEASE_COMMIT` using its commit-hash form. +2. Open the voting form. Set the first-round recipient to `dev@asyncband.apache.org`, the second-round recipient to `general@incubator.apache.org`, and the minimum duration to at least 72 hours. The second round uses that duration too. +3. Review the generated subject and body. Identify Apache Asyncband (Incubating), `${VERSION}`, and the RC; include the ATR candidate/revision, RC tag and commit, source checksum, KEYS and signer information, changelog, and verification evidence. Link ATR as the location of the voted files. Submit **Send vote email** when sending the vote is authorized, then record its archive link and closing time. + +If publication is authorized, automatic publication can publish the source after IPMC approval; confirm the download suffix is `${VERSION}` when selecting it. Otherwise, publish in Finish after both votes pass. + +## Resolve both rounds + +Each round needs at least 72 hours, at least three eligible `+1` votes, and more eligible `+1` than `-1` votes. Eligibility is PPMC membership for round one and IPMC membership for round two. Allow at least six days for the two sequential votes. See the [Incubator vote rules](https://incubator.apache.org/cookbook/#two-phase-vote-on-podling-releases). + +1. After the PPMC period, open the vote resolution page. Compare ATR's email tally with the thread, including voters' roles and any carried IPMC votes, and review the result body. Select `Passed` only if the requirements are met, then resolve the vote. +2. ATR sends the PPMC result and automatically starts the IPMC vote on the selected second-round list. Confirm delivery and record both links. Check that the IPMC thread includes the PPMC result, its `lists.apache.org` tally link, and any IPMC votes carried from round one; supplement that thread with any missing evidence. +3. After the IPMC period, review its binding tally, including eligible votes carried from round one and counting only each voter's latest vote, and review the result body before resolving it as `Passed`. ATR sends the result, also reports the passing result to the first-round thread, and moves the release to Finish. Record both vote results before final publication. + +## Publish and announce + +In Finish, inspect **Publish to ASF Distribution Area**. If automatic publication already completed, record its result. Otherwise, confirm the approved revision and destination `dist/release/incubator/asyncband/${VERSION}/`, then use the Publish action. ATR commits the voted files to SVN; record the SVN revision and compare the published archive, signature, and checksum with the candidate. + +Return to the main runbook to push the final tag and verify crates.io publication. Once both the ASF downloads and the crate are available, use **Announce** in ATR, review the recipients and message, and submit it. ATR sends the announcement and updates its release catalog. Record the announcement link in the tracking issue. See [ATR publication](https://releases.apache.org/docs/promoting-to-release). + +Use ATR's archive action or the configured auto-archive option for superseded releases. Confirm archival and removal from active downloads, and resolve any cleanup warnings. + +## API and CLI alternative + +Use the website to review the tally, edit emails, and publish. For scripted operations, consult the [CLI](https://github.com/apache/tooling-releases-client) and [server API](https://github.com/apache/tooling-trusted-releases/blob/0d156e9a/atr/api/__init__.py). Check the client's help, current API schema, and generated vote/result messages against the steps above. + +## Recover without replacing voted files + +- Failed or uncertain upload: inspect ATR before retrying. If all three files arrived and verification succeeds, record that revision even if the GitHub run reported a late failure. Otherwise, retry before voting and verify the resulting revision; signing/upload can produce a new signature and revision. If the GitHub source bundle expired, rerun composition from the same RC and compare the recorded checksum. +- Source or workflow correction: use a new reviewed commit and refresh affected checks. If an RC already exists, use a new RC number. A workflow rerun uses the old tagged workflow, so changing `main` cannot repair it. +- Rejected or cancelled vote: resolve that outcome in ATR, which returns the release to Compose. Preserve the previous tag and vote history in the issue, then prepare and verify the replacement candidate. Do not modify an active vote's files. +- Vote or publication error: inspect the current round, mail-delivery status, Finish state, and destination files before retrying. Resume a completed transition; do not resend an IPMC vote or republish matching files merely to obtain a green status. diff --git a/.agents/skills/release/references/candidate.md b/.agents/skills/release/references/candidate.md deleted file mode 100644 index 7dbd2b7a..00000000 --- a/.agents/skills/release/references/candidate.md +++ /dev/null @@ -1,112 +0,0 @@ - - -# Prepare and stage a release candidate - -## Prepare the release pull request - -For a new release, start from current `main` and choose `VERSION` from the changes since the latest crates.io release. If the release pull request or candidate already exists, resume its recorded version and commit. - -1. Change `version` in `asyncband/Cargo.toml` and refresh `Cargo.lock` with Cargo. -2. Move the entries under `Unreleased` in `CHANGELOG.md` into an undated `v${VERSION}` section immediately below it, then restore an empty `Unreleased` section. Keep user-impacting sections ordered as breaking changes, new features, bug fixes, and improvements; add the actual release date only after publication. -3. Review `LICENSE`, `NOTICE`, `DISCLAIMER`, source headers, and bundled dependencies using the `license-audit` skill. Codex can delegate that review to `license_auditor`; provide the repository root and requested revision. Use its evidence and suggestions to decide what follow-up is needed during release preparation. -4. Run the release checks: - -```shell -cargo x lint -cargo x check -cargo x test --no-capture -RUSTUP_TOOLCHAIN=1.86.0 cargo x test --no-capture -cargo x semver --release-version "${VERSION}" -cargo publish --package asyncband --locked --dry-run -``` - -For a semver-major release, including a pre-1.0 minor release such as `0.7.0`, the semver command uses minor compatibility rules to report breaking API changes. When it reports expected changes, record and review them in `CHANGELOG.md`, then rerun: - -```shell -cargo x semver --release-version "${VERSION}" --acknowledge-breaking-changes -``` - -Merge the release pull request and record its merge commit as `RELEASE_COMMIT`. Every candidate artifact, the final tag, and the crates.io package use this exact commit. - -## Create and validate a release candidate - -For a new candidate, create a release directory outside the repository and a detached worktree at `RELEASE_COMMIT`. When resuming, reuse the recorded `RELEASE_DIR`, worktree, and RC tag. Run later Git and Cargo commands from that worktree root. - -```shell -RELEASE_DIR="$(mktemp -d "${TMPDIR:-/tmp}/asyncband-release.XXXXXX")" -RC_TAG="v${VERSION}-rc.${RC}" -git fetch https://github.com/apache/asyncband.git main -git worktree add --detach "${RELEASE_DIR}/checkout" "${RELEASE_COMMIT}" -cd "${RELEASE_DIR}/checkout" -git grep -F "version = \"${VERSION}\"" -- asyncband/Cargo.toml Cargo.lock -git grep -Fx "## v${VERSION}" -- CHANGELOG.md -test -z "$(git status --porcelain)" -git tag --sign "${RC_TAG}" \ - --message "Apache Asyncband ${VERSION} release candidate ${RC}" \ - "${RELEASE_COMMIT}" -git push https://github.com/apache/asyncband.git "${RC_TAG}" -``` - -Wait for the `Release` GitHub Actions workflow to pass. The workflow validates the RC tag and runs `cargo publish --dry-run` against the unchanged `${VERSION}` package; it skips the crates.io publish job for RC tags. A candidate that needs a code change gets a new release pull request, merge commit, RC number, and signed tag. - -## Build and verify the source archive - -For a new candidate, build the source archive from the verified RC tag. Reuse existing signed artifacts for a retry of the same candidate. The `incubating` marker is required in the filename, and `gzip -n` keeps the gzip header independent of the local build time. - -```shell -RC_TAG="v${VERSION}-rc.${RC}" -SOURCE_DIR="apache-asyncband-${VERSION}-incubating-src" -ARTIFACT_DIR="${RELEASE_DIR}/dist" -mkdir -p "${ARTIFACT_DIR}" -git verify-tag "${RC_TAG}" -git archive --format=tar --prefix="${SOURCE_DIR}/" "${RC_TAG}" \ - | gzip -n -9 > "${ARTIFACT_DIR}/${SOURCE_DIR}.tar.gz" -( - cd "${ARTIFACT_DIR}" - shasum -a 512 "${SOURCE_DIR}.tar.gz" > "${SOURCE_DIR}.tar.gz.sha512" - gpg --armor --detach-sign --local-user "${ASF_GPG_FINGERPRINT}" \ - "${SOURCE_DIR}.tar.gz" -) -``` - -Verify the existing artifacts with the [candidate-verification guide](verification.md), directly or through `release_verifier`. Supply the candidate identity, signing-key fingerprint, repository root, and absolute artifact paths. Keep the signed archive unchanged throughout verification. - -For the separate `license-audit` review, provide the original source archive, the extracted source, and the Cargo package produced by verification, together with `RELEASE_COMMIT`. Discuss its evidence and suggestions with the release manager. Keep the verification directory available until both reviews finish; then remove that disposable directory. - -## Stage the candidate on ASF infrastructure - -Check whether this candidate is already staged. For a new staging operation, check out a working copy under `RELEASE_DIR`, add the three candidate files, and commit them: - -```shell -svn checkout --depth=empty \ - https://dist.apache.org/repos/dist/dev/incubator/asyncband "${RELEASE_DIR}/svn-dev" -mkdir "${RELEASE_DIR}/svn-dev/${VERSION}-rc.${RC}" -cp \ - "${ARTIFACT_DIR}/${SOURCE_DIR}.tar.gz" \ - "${ARTIFACT_DIR}/${SOURCE_DIR}.tar.gz.asc" \ - "${ARTIFACT_DIR}/${SOURCE_DIR}.tar.gz.sha512" \ - "${RELEASE_DIR}/svn-dev/${VERSION}-rc.${RC}/" -svn add "${RELEASE_DIR}/svn-dev/${VERSION}-rc.${RC}" -svn status "${RELEASE_DIR}/svn-dev" -svn commit "${RELEASE_DIR}/svn-dev" \ - -m "Stage Apache Asyncband ${VERSION} release candidate ${RC}" -``` - -Confirm the candidate at `https://dist.apache.org/repos/dist/dev/incubator/asyncband/${VERSION}-rc.${RC}/` and verify every link prepared for the vote email. diff --git a/.agents/skills/release/references/infrastructure.md b/.agents/skills/release/references/infrastructure.md index 69a078a0..554ae880 100644 --- a/.agents/skills/release/references/infrastructure.md +++ b/.agents/skills/release/references/infrastructure.md @@ -17,18 +17,18 @@ specific language governing permissions and limitations under the License. --> -# Release infrastructure +# Troubleshoot release services -This is a reference for the existing design, not a recurring release checklist. Consult it when changing configuration, troubleshooting publication, or arranging a new release manager's signing key. +Start with the failed workflow step or ATR operation, then check the corresponding configuration below. -## crates.io Trusted Publishing +## Source composition -Trusted Publishing is already configured for `asyncband`: repository `apache/asyncband`, workflow `release.yml`, and environment `release`. Routine releases reuse this configuration. +For a blocked or failed compose run, check the `release` environment approval and the settings required by `.github/workflows/release-compose.yml`. For ATR authentication or key-recognition failures, compare the project's Trusted Publishing settings with `.asf.yaml` and confirm that the signing key is available in the project's [KEYS](https://downloads.apache.org/incubator/asyncband/KEYS). See [ATR Trusted Publishing](https://releases.apache.org/docs/trusted-publishing). -`.github/workflows/release.yml` obtains a short-lived crates.io token through GitHub OIDC. Only a final `v${VERSION}` tag can enter the publish job; RC tags run package checks. `.asf.yaml` configures version-tag deployments and required reviewers for the `release` environment. Consult those repository files and the live crate settings when diagnosing a mismatch. See the [crates.io Trusted Publishing documentation](https://crates.io/docs/trusted-publishing) for changes to this setup. +## ATR voting and publication -## Signing and ASF distribution +Check Asyncband's podling status and voting settings in ATR, and compare its synchronized settings with `.asf.yaml`. For publication failures, inspect Finish's result and the destination `https://dist.apache.org/repos/dist/release/incubator/asyncband/${VERSION}/`. Published files propagate to `https://downloads.apache.org/incubator/asyncband/${VERSION}/`. See [ATR publication](https://releases.apache.org/docs/promoting-to-release). -Candidates are staged under `https://dist.apache.org/repos/dist/dev/incubator/asyncband/`; approved releases are promoted to `https://dist.apache.org/repos/dist/release/incubator/asyncband/`. The public verification key list is `https://downloads.apache.org/incubator/asyncband/KEYS`. +## crates.io publication -The release manager uses an ASF-associated signing key published in the existing project `KEYS` file. For a new signing key, follow the [ASF release signing guide](https://infra.apache.org/release-signing.html), verify the fingerprint through an independent channel, and add the public key while preserving existing keys. Reuse established distribution areas and signing configuration; investigate a reported access or verification failure before proposing infrastructure changes. +Check the final tag's run of `.github/workflows/release.yml` and its `release` environment approval. For an authentication failure, confirm that the crate's Trusted Publisher matches repository `apache/asyncband`, workflow `release.yml`, and environment `release`. See [crates.io Trusted Publishing](https://crates.io/docs/trusted-publishing). diff --git a/.agents/skills/release/references/publication.md b/.agents/skills/release/references/publication.md deleted file mode 100644 index d850c6bb..00000000 --- a/.agents/skills/release/references/publication.md +++ /dev/null @@ -1,89 +0,0 @@ - - -# Vote, publish, and follow up - -Resume from the recorded candidate and vote results. Check which publication steps already succeeded before running commands again. - -## Hold the two-phase vote - -Incubating releases use the [Incubator two-phase vote](https://incubator.apache.org/cookbook/#two-phase-vote-on-podling-releases). Each vote remains open for at least 72 hours. - -When the staged candidate and supporting links have been verified, send `[VOTE] Release Apache Asyncband (Incubating) ${VERSION} RC${RC}` to `dev@asyncband.apache.org`. Include: - -- the staged source URL; -- the `KEYS` URL and signing-key fingerprint; -- the signed RC tag and commit hash; -- the changelog or comparison with the previous release; -- verification commands or a checklist for signatures, checksums, licensing, unexpected binaries, and the build; -- a closing time at least 72 hours after the vote starts. - -The PPMC vote passes with at least three PPMC `+1` votes and more PPMC `+1` votes than `-1` votes. Publish a result email that identifies the voters and links the archived vote thread. - -Then send the proposal to `general@incubator.apache.org` with the PPMC result and archive link. The IPMC vote passes with at least three binding IPMC `+1` votes and more binding `+1` votes than `-1` votes. Publish its result email and record the archive link. - -Begin publication after the IPMC result records a passing vote. - -## Promote and publish the approved release - -Promote the exact voted artifacts from the development distribution area: - -```shell -svn move \ - "https://dist.apache.org/repos/dist/dev/incubator/asyncband/${VERSION}-rc.${RC}" \ - "https://dist.apache.org/repos/dist/release/incubator/asyncband/${VERSION}" \ - -m "Release Apache Asyncband ${VERSION}" -``` - -If the final tag already exists, verify that it points to the approved RC commit and continue with its workflow state. Otherwise, create the signed final tag from the verified RC tag and push it: - -```shell -RC_TAG="v${VERSION}-rc.${RC}" -git verify-tag "${RC_TAG}" -git tag --sign "v${VERSION}" \ - --message "Apache Asyncband ${VERSION}" \ - "${RC_TAG}^{commit}" -git push https://github.com/apache/asyncband.git "v${VERSION}" -``` - -The final tag starts the crates.io publishing job. A configured reviewer compares the final tag with the approved RC, confirms the IPMC vote result, and approves the `release` environment deployment. The workflow verifies the package version and publishes with a short-lived crates.io token. - -After publication: - -1. Verify the version and metadata on crates.io and docs.rs. -2. After ASF distribution syncs, verify the source archive, checksum, and signature under `https://downloads.apache.org/incubator/asyncband/${VERSION}/` and the project `KEYS` file at `https://downloads.apache.org/incubator/asyncband/KEYS`. -3. Submit a post-release pull request that adds the actual publication date to the `v${VERSION}` changelog heading. -4. Announce the release on `dev@asyncband.apache.org` and other appropriate channels as Apache Asyncband (Incubating). -5. Remove superseded releases from `dist/release`; ASF retains them in the archive. - -## Recover from failures - -A transient CI, staging, or registry error can be retried against the same candidate after checking what already succeeded. - -If the community rejects a candidate or its content changes, coordinate a new candidate with an incremented `RC`. Remove the rejected candidate from the development distribution area: - -```shell -svn delete \ - "https://dist.apache.org/repos/dist/dev/incubator/asyncband/${VERSION}-rc.${RC}" \ - -m "Remove rejected Apache Asyncband ${VERSION} release candidate ${RC}" -``` - -For a failed final crates.io job, first check whether the version exists on crates.io. If it exists and its contents and metadata match the approved release, continue post-publication verification and follow-up; the upload may have succeeded before the job lost its response. If the version is absent, rerun the same final-tag workflow for a transient or publishing-infrastructure failure. A source or package change uses a new version and the full ASF vote process. A published version is immutable; if it has a confirmed problem, discuss any yank and follow-up release on the development list. - -Once release follow-up is complete and the release manager no longer needs the local artifacts, remove the detached worktree with `git worktree remove "${RELEASE_DIR}/checkout"` and clean up the recorded release directory. Retain it while there are unresolved verification or recovery questions. diff --git a/.agents/skills/release/references/tracking-issue.md b/.agents/skills/release/references/tracking-issue.md new file mode 100644 index 00000000..6f21e5c5 --- /dev/null +++ b/.agents/skills/release/references/tracking-issue.md @@ -0,0 +1,86 @@ + + +# Release tracking issue + +Use title `Tracking Issue to Release ${VERSION}` in `apache/asyncband`. Keep the release's current state, check results, and next action in this issue so another release manager can take over. + +Fill in known values and leave unfinished items unchecked. Record the checked revision and result for each completed item. With `gh`, pass the body through standard input using `--body-file -` and preserve other participants' edits. + +Set `RUNBOOK_COMMIT` to the full SHA of the upstream commit containing the runbook used when opening the issue. Keep this link pinned throughout the release. + +```markdown +- **Release manager:** @ +- **Target version:** +- **Previous release:** +- **Source cutoff:** +- **Release PR:** pending +- **Frozen release commit:** pending +- **Current phase / next action:** initial license audit +- **Runbook:** https://github.com/apache/asyncband/blob//.agents/skills/release/SKILL.md + +## Prepare and freeze + +- [ ] Audit the selected source with `license-audit`; resolve release-blocking findings. +- [ ] Merge the version, lockfile, and undated changelog PR; record the frozen release commit. +- [ ] Complete the final license review, including the actual Cargo package and any audit corrections. + +## Check the frozen release commit + +- [ ] `cargo x lint` +- [ ] `cargo x check` +- [ ] `cargo x test --no-capture` +- [ ] `RUSTUP_TOOLCHAIN=1.86.0 cargo x test --no-capture` +- [ ] `cargo x semver --release-version `; document any allowed major-release API breaks. +- [ ] `cargo publish --package asyncband --locked --dry-run` from the clean checkout. +- [ ] Required GitHub CI passes for the release commit. + +## Candidate + +- **RC / signed tag:** pending +- **Compose run and attempt / package-check run:** pending +- **ATR candidate URL / revision:** pending +- **Source SHA-512:** pending +- **Tag signer / source signer fingerprints and provenance:** pending + +- [ ] Push the signed RC tag at the frozen commit; confirm both release workflows and ATR upload. +- [ ] Download and verify the ATR revision: signatures, checksum, source contents against the RC commit, independent archive rebuild for automated signing, build, and packaging. +- [ ] Complete the license review of the actual source archive and Cargo distribution; address ATR findings. + +## Vote and publish + +- **ATR vote page:** pending +- **PPMC vote thread:** pending +- **PPMC result:** pending +- **IPMC vote thread:** pending +- **IPMC result:** pending +- **Announcement:** pending + +- [ ] Start the PPMC vote in ATR; record the vote link and closing time. +- [ ] After the required duration and votes, resolve PPMC as Passed in ATR; record its result and the automatically started IPMC vote, and ensure that thread includes the PPMC tally link and any carried IPMC votes. +- [ ] After the required duration and binding votes, resolve IPMC as Passed in ATR; record its result. +- [ ] Confirm ATR publishes the voted revision to ASF distribution; record the SVN revision and download URL. +- [ ] Push the signed final tag at the approved commit and approve/verify crates.io publication. +- [ ] Verify ASF downloads and signatures, crates.io, and docs.rs. +- [ ] Send the announcement through ATR and record its archive link. +- [ ] Merge the publication-date changelog PR and confirm superseded-release archival as applicable. +- [ ] Close this issue after the preceding required items are complete. +``` + +For a replacement candidate, retain the previous tag, commit, ATR revision, and vote outcome in the issue history. Update the active candidate fields, invalidate checks affected by changed contents, and make the next action explicit. diff --git a/.agents/skills/release/references/verification.md b/.agents/skills/release/references/verification.md index e915013d..9367873c 100644 --- a/.agents/skills/release/references/verification.md +++ b/.agents/skills/release/references/verification.md @@ -19,23 +19,25 @@ under the License. # Verify an existing release candidate -Perform the requested checks against the supplied candidate and return evidence to the caller. This is a bounded technical check: preserve the supplied repository and artifacts, and create extraction or build outputs only in the caller's scratch directory or a new temporary directory outside the checkout. Do not change tags, sign replacement artifacts, stage, publish, send messages, or perform a separate license audit. +Verify the supplied candidate and return results to the release manager. Preserve the original artifacts and use a scratch directory outside the checkout for extraction and builds. ## Inputs and scope -Use the caller's repository root, `VERSION`, `RC_TAG`, `RELEASE_COMMIT`, absolute archive/signature/checksum paths, expected signing fingerprint and its provenance, and requested verification scope. The caller may supply an existing Cargo package as well. Resolve repository files from that explicit root; this guide's location does not identify the audited checkout. +Use the supplied repository root, `VERSION`, `RC_TAG`, `RELEASE_COMMIT`, ATR URL and revision, compose run and attempt, archive/signature/checksum paths, expected signing fingerprints and their provenance, and requested verification scope. -Establish which checks are requested and which inputs are available. Continue independent checks when an input is missing and report the resulting gap. A key bundled with an archive is not independent evidence of signer identity. Use a temporary GPG home when importing supplied verification keys so the user's keyring stays unchanged. +Obtain signing fingerprints from trusted project key records and import verification keys into a temporary GPG home. Report any missing inputs or incomplete checks. ## Verify identity and source contents -1. Record the resolved commit and artifact paths. Check the supplied RC tag's commit and signature against `RELEASE_COMMIT` and the expected signing fingerprint. Keep an existing candidate tied to that commit even if `main` has advanced. -2. Verify the SHA-512 checksum and detached archive signature on the original bytes. Record signature validity separately from whether the signer matches the expected identity; a checksum or signature alone does not establish agreement with the candidate commit. +1. Record the resolved commit, ATR revision, and artifact paths. Check the RC tag's commit and signature against `RELEASE_COMMIT` and the release manager's personal signing-key fingerprint recorded in the issue. +2. Verify the downloaded archive's SHA-512 against its checksum file and the compose summary. Verify its detached signature against the expected source-signing fingerprint, recording the primary fingerprint when a signing subkey is used. The archive signer can differ from the Git tag signer; check both identities. 3. Inspect the archive member list before extracting. Check the expected `apache-asyncband-${VERSION}-incubating-src/` root, reject paths that escape the extraction directory, and inspect symlinks without following them outside the extracted tree. -4. Compare the archived source inventory, file contents, executable bits, and symlink targets with `git archive` of the resolved candidate commit, using a separate temporary extraction. Compare logical entries rather than compressed bytes or timestamps. Report missing, added, or changed entries with concrete paths. Check the package version in the extracted manifest against `VERSION`. +4. Compare the archived source inventory, file contents, executable bits, and symlink targets with `git archive` of the resolved candidate commit, using a separate temporary extraction. Report missing, added, or changed entries with concrete paths. Check the package version in the extracted manifest against `VERSION`. For example, use `git -C "${REPO_ROOT}" rev-parse "${RC_TAG}^{commit}"` to resolve a candidate and `git -C "${REPO_ROOT}" verify-tag "${RC_TAG}"` to inspect its signature. Run `shasum -a 512 --check` from the artifact directory and `gpg --verify` with the explicitly supplied signature and archive. Inspect checksum filenames before using the checksum file so it checks the intended artifact. +On trusted hardware outside GitHub Actions, run the candidate's compose recipe in a Linux environment and compare the rebuilt archive's SHA-512 with the ATR artifact. Record this once per candidate to satisfy [ASF automated signing](https://infra.apache.org/release-signing.html#automated-release-signing). When diagnosing a mismatch, compare source contents separately from archive headers and compression. + ## Verify the build and Cargo package Run these checks when the caller requests build and packaging verification. Read `cargo x --help` and the relevant subcommand help in the extracted source first. The archive test uses Cargo's `--locked` mode to check the shipped lockfile; `cargo x test` does not expose that option. @@ -46,10 +48,10 @@ cargo test --workspace --all-features --locked cargo publish --package asyncband --locked --dry-run ``` -Use the resulting `target/package/asyncband-${VERSION}.crate` for packaging verification and the caller's license review. If a package is supplied separately, inspect its actual contents and compare it with the package generated from the candidate when available. Check the package name, version, packaged source, and Cargo provenance metadata where present. Account for Cargo-generated manifests and repository metadata when comparing; an absent commit field in a package built outside a Git checkout is not itself a mismatch. +Use the resulting `target/package/asyncband-${VERSION}.crate` for packaging verification and license review. Check its name, version, source contents, and provenance metadata where present. When comparing a separately supplied package, account for Cargo-generated manifests and metadata that depend on whether a Git checkout is available. -Preserve the original archive inventory when builds add files to the extracted directory. Report build or dependency-fetch failures as observed failures; do not change the candidate to make verification pass. +Keep the original archive inventory for license review; build outputs belong only to the verification workspace. ## Return the result -Return a concise account of the checked candidate, signature/checksum and source-comparison results, requested build/package results, concrete discrepancies, and checks not completed. Include commands and exit results when they matter. Give the caller the extracted source and package paths for `license-audit`; retain these scratch outputs until the caller has finished with them. Do not create a report file unless requested. +Return the checked candidate and ATR revision, signer fingerprints, signature/checksum results, source differences, independent rebuild checksum, build/package results, and any incomplete checks. Provide the extracted source and package paths for `license-audit` and retain them until that review is complete. diff --git a/.github/workflows/release-compose.yml b/.github/workflows/release-compose.yml index 9c047663..63df3436 100644 --- a/.github/workflows/release-compose.yml +++ b/.github/workflows/release-compose.yml @@ -113,7 +113,7 @@ jobs: needs: compose runs-on: ubuntu-24.04 timeout-minutes: 10 - # Keep signing secrets in this environment so access requires release approval. + # Release approval gates signing with the ASF-provisioned secrets and ATR upload. environment: name: release url: https://releases.apache.org/projects/asyncband @@ -136,11 +136,11 @@ jobs: env: ARCHIVE: ${{ needs.compose.outputs.archive }} IMPORTED_FINGERPRINT: ${{ steps.gpg.outputs.fingerprint }} - SIGNING_FINGERPRINT: ${{ vars.SOURCE_SIGNING_FINGERPRINT }} + SIGNING_FINGERPRINT: ${{ secrets.SOURCE_SIGNING_FINGERPRINT }} run: | set -euo pipefail if [[ ! "${SIGNING_FINGERPRINT}" =~ ^([0-9A-F]{40}|[0-9A-F]{64})$ ]]; then - echo "::error::SOURCE_SIGNING_FINGERPRINT must be the full uppercase primary fingerprint." + echo "::error::The SOURCE_SIGNING_FINGERPRINT secret must be the full uppercase primary fingerprint." exit 1 fi if [[ "${IMPORTED_FINGERPRINT}" != "${SIGNING_FINGERPRINT}" ]]; then