jdaugherty commented on code in PR #16229: URL: https://github.com/apache/grails-core/pull/16229#discussion_r4416679619
########## RELEASE.md: ########## @@ -433,6 +440,97 @@ Announcements should come from your apache email address (see https://infra.apac expected format. The announcement should be sent to `[email protected]`, `[email protected]`, `[email protected]`, & `[email protected]`. See the `close` job for a generated email. +### What GitHub Actions may automate + +The following boundaries preserve PMC authority and use only opt-in, protected workflows. Apache's release policy permits +automated release infrastructure to sign artifacts, while the vote remains a community email process. + +| Activity | GitHub Actions boundary and evidence | +| --- | --- | +| ATR compose, resolve, one announce, and distribution record | Opt-in workflows call [ASF Tooling actions](https://github.com/apache/tooling-actions) against [ATR Beta](https://releases.apache.org/docs/). Official examples: [compose](https://github.com/apache/tooling-asf-example/blob/main/.github/workflows/build-and-rsync-to-atr.yaml), [resolve](https://github.com/apache/tooling-asf-example/blob/main/.github/workflows/resolve-vote-on-atr.yaml), [announce](https://github.com/apache/tooling-asf-example/blob/main/.github/workflows/announce-release-on-atr.yaml), and [record](https://github.com/apache/tooling-asf-example/blob/main/.github/workflows/record-distribution-on-atr.yaml). | +| Signing in GitHub Actions | [Apache release policy](https://www.apache.org/legal/release-policy.html) requires release artifacts to be signed by the Release Manager or automated release infrastructure. ATR [Trusted Publishing](https://releases.apache.org/docs/trusted-publishing) accepts those signatures when the committee is eligible for CI builds. | +| Publish the voted distributions to `dist/release` | Beta ATR commits the approved artifacts to `svn:dist:release` itself. The workflow calls ATR. It does not write SVN. See [Promoting to release](https://releases.apache.org/docs/promoting-to-release). | +| Documentation and website content | [GitHub Actions policy](https://infra.apache.org/github-actions-policy.html) permits work on website content and other non-released data. Grails already deploys documentation from GitHub Actions. | +| Promote closed Nexus staging after a passed vote | `Release - Promote Nexus Staging (experimental)` is `workflow_dispatch` plus the protected `release` environment. It uses the existing `NEXUS_STAGE_DEPLOYER_USER` / `NEXUS_STAGE_DEPLOYER_PW` secrets used to drop staging, but that account's promotion permission is unverified. Keep using the Nexus UI or local script. | +| Skip `dist/dev` only when using ATR | ATR candidates may skip the traditional staging location as described in [Staging and voting](https://releases.apache.org/docs/staging-and-voting). The `dist/dev` plus Nexus path remains Grails' official default until the PMC opts in to ATR. | + +The following must remain manual: + +| Activity | Why it remains manual | +| --- | --- | +| Start the binding PMC vote and hold it for at least 72 hours | [Apache release policy](https://www.apache.org/legal/release-policy.html) says release votes SHOULD remain open for at least 72 hours. ATR can send and tally the vote email, but `apache/tooling-actions` has no action that starts a vote. | +| Move `dist/dev` distributions when ATR is not used | [GitHub Actions policy](https://infra.apache.org/github-actions-policy.html) prohibits automated services from pushing official release data without prior Infrastructure authorization. The default Grails path still uses `releaseDistributions.sh` as the release manager. | +| Rebuild and compare artifacts on trusted hardware | Trusted Publishing requires an independent rebuild outside GitHub Actions before publication. Grails already documents that comparison in this file. | +| Report the release to ASF Reporter | [ASF Reporter addrelease](https://reporter.apache.org/addrelease.html) has no documented public API. | +| Send lists beyond the one ATR announcement | ATR rejects a second announcement for the same release. Send `[email protected]`, `[email protected]`, and `[email protected]` manually after the single ATR announce. | +| Forge, SDKMAN!, the static website, and the blog | ATR records external distributions. It does not publish those Grails channels. | + +ATR Beta on `releases.apache.org` publishes finished releases directly to `svn:dist:release`. The PMC email that announces Beta to `pmcs@` is still tracked by [tooling-trusted-releases#1520](https://github.com/apache/tooling-trusted-releases/issues/1520). That open issue does not keep the platform on Alpha. + +## Apache Trusted Releases (ATR) + +[ASF Tooling](https://tooling.apache.org/) runs [Apache Trusted Releases](https://releases.apache.org/) to automate compose, vote, and finish. ATR is **Beta**. The existing `dist/dev` to `dist/release` and Nexus staging path remains the official Grails process until the PMC opts in. See the [ATR documentation](https://releases.apache.org/docs/) and [apache/tooling-actions](https://github.com/apache/tooling-actions). + +The workflows pin `apache/tooling-actions` to `dcba9f4ed2956f7a30113c3067e766eeda37d891` (`main` on 2026-10-08). [Trusted Publishing](https://releases.apache.org/docs/trusted-publishing) says these actions are not tagged and must be referenced by a full commit hash, not `@main`. Other `apache/*` actions stay on a branch. `validateRepositoryConventions` requires that commit hash for `apache/tooling-actions/*` and still rejects a commit hash for every other `apache/*` action. + +| Manual step today | Tooling action | Grails workflow | +| --- | --- | --- | +| Upload source, binary, wrapper, and docs zips | `upload-to-atr` | `Release - ATR Upload (experimental)`, or `atr_upload` in `release.yml` when `ATR_ENABLED=true` | +| Send the `[VOTE]` email | none (start the vote in the [ATR UI](https://releases.apache.org/) or with `atr vote start`) | — | +| Send the `[RESULT][VOTE]` email | `release-on-atr` with `resolve: true` | `Release - ATR Resolve Vote (experimental)` | +| Publish the voted zips to `dist/release` | ATR, when automatic publication was selected at vote start, or the Publish button | not a GitHub Actions SVN write | +| Promote Nexus staging to Maven Central | none | Nexus UI or `.github/scripts/releaseJarFiles.sh`, then `Release - ATR Record Distribution (experimental)` with `platform=MAVEN` | +| Send the `[ANNOUNCE]` email | `release-on-atr` with `announce: true` | `Release - ATR Announce (experimental)` once, to `[email protected]` | +| ASF Reporter, Forge, SDKMAN!, website, blog | none | unchanged | + +### Opt-in setup + +1. Log in at https://releases.apache.org/ with ASF credentials and confirm the Grails project. Discuss on [[email protected]](https://lists.apache.org/[email protected]) / [#apache-trusted-releases](https://the-asf.slack.com/archives/C049WADAAQG). +2. Set the project download path suffix to `core/{{VERSION}}`. Grails publishes under `dist/release/grails/core/<version>/`, not in the committee root next to `KEYS`. +3. Configure [Trusted Publishing](https://releases.apache.org/docs/trusted-publishing) for repository `grails-core`: + + * Compose: `.github/workflows/release.yml` and `.github/workflows/release-atr-upload.yml` + * Vote: `.github/workflows/release-atr-resolve.yml` + * Finish: `.github/workflows/release-atr-announce.yml` and `.github/workflows/release-atr-record.yml` + +4. The committee page must show CI builds enabled. That requires a signing key whose primary UID contains `Automated Release Signing` and whose email is `[email protected]`. The Infra-issued RSA 4096 key in `KEYS`, `Apache Grails Automated Release Signing <[email protected]>`, fingerprint `08E2 CEC4 7E38 FE41 5F08 0AB6 2ADE CADC 1177 5306`, already satisfies this requirement. The release workflow imports this key from `GRAILS_GPG_KEY` and signs with it using `GPG_KEY_ID`. Voters still compare an independent rebuild on trusted hardware with the uploaded artifacts. +5. Optional repository variables, after that setup: + + * `ATR_ENABLED=true` + * `ATR_PROJECT=grails` (the ATR project slug) + * `ATR_HOST=releases.apache.org` (optional; this is the default) + * `ATR_SSH_PORT=2222` (optional; this is the default) + +Do not enable ATR auto-archive of the prior release. On the ATR path, the release manager still removes the previous `dist/release` folder by hand so other current release lines are preserved. + +After publishing the new release, list the folders with `svn ls https://dist.apache.org/repos/dist/release/grails/core/` and select only the superseded release to remove. Keep the new version and the current releases on other lines. Use the same removal command as `.github/scripts/releaseDistributions.sh` with `SVN_FOLDER=core`, replacing `<folder>` with the selected folder name (without the trailing slash), as your ASF user: + +```bash +svn rm -m "Remove previous release <folder>" https://dist.apache.org/repos/dist/release/grails/core/<folder> +``` + +### Using ATR alongside a Grails release + +Keep the current `Release` workflow as the source of signed artifacts. After the `source` job has uploaded the zips to the GitHub Release: + +1. **Compose.** Run `Release - ATR Upload (experimental)`, or rely on `atr_upload` when `ATR_ENABLED=true`. This rsyncs `sources/apache-grails-<version>-src.zip` and, under `distribution/`, the binary zip, the wrapper zip, and the documentation zip, each with `.asc` and `.sha512`. The upload declares the release tag commit. ATR then checks that the source zip is a subset of that commit: omitted files are allowed, and files that are not in the git tree are not, except `PKG-INFO` when `pyproject.toml` is present. This zip adds `CHECKSUMS`, `PUBLISHED_ARTIFACTS`, `BUILD_DATE`, and `GIT_TAGS` under `grails/`. That records a concern from `atr.tasks.checks.compare.source_trees`. Starting the vote requires acknowledging that concern. A project ignore can mark the same checker as an exception, but a broad ignore also hides unexpected extra files, so acknowledge the concern until ATR can allow those specific added paths. Do not treat `dist/dev` as the files under vote when ATR is the staging location. See [Staging and voting](https://releases.apache.org/docs/staging-and-voting). +2. **Vote.** Start the vote on ATR. There is no Tooling GitHub Action for this. Select automatic publication to the SVN distribution area. The download path comes from the project suffix `core/{{VERSION}}`, which the current `release-on-atr` action does not override. Discard any `dist/dev` `[VOTE]` email if the `upload` job printed one; it skips that email when `ATR_ENABLED=true`. Keep the 72-hour PMC vote. The GitHub `release` environment approval is still the gate for later jobs. +3. **Resolve.** After the vote passes, run `Release - ATR Resolve Vote (experimental)` with `passed`. That replaces sending the `[RESULT][VOTE]` email by hand. `cancelled` is also a valid resolution. +4. **Publish.** If automatic publication was selected, ATR has committed the voted artifacts to `dist/release/grails/core/<version>/`. Otherwise press Publish on the finish page. Do not run `releaseDistributions.sh` and do not move `dist/atr`. +5. **Maven Central.** Use the Nexus UI or `.github/scripts/releaseJarFiles.sh` as your ASF user. The promotion workflow remains experimental until the stage-deployer account's promotion permission is verified. Once Maven Central is live, run `Release - ATR Record Distribution (experimental)` with `platform=MAVEN`, `distribution_owner_namespace=org.apache.grails`, and `distribution_package=grails-bom`. If the project tagging spec names Maven, ATR will not announce until that record exists. +6. **Public destinations named by the announcement.** Publish the voted documentation zip, release the version to SDKMAN!, and update plus publish the static website so [https://grails.apache.org/download.html](https://grails.apache.org/download.html) lists this version. The generated announcement links to that page. Confirm the page shows the new version before announcing. +7. **Announce.** Run `Release - ATR Announce (experimental)` once; it always sends to `[email protected]`. Leave `announce_body` empty to render `.github/vote_templates/announce.txt` (milestone wording is included only for `M` / `RC` versions). One successful announce must not be repeated for another list. If ATR rejects the call because the artifacts are not on the download mirrors yet, check the ATR finish page and retry that same announce. Do not retry an ambiguous failure. Then send the same body from your `@apache.org` address to `[email protected]`, `[email protected]`, and `[email protected]`. + +To find the verification base URL for an ATR candidate, open that candidate's page on the configured ATR host. The page provides a `curl` download one-liner and a list of file URLs at `/download/urls/<project>/<version>`. Read the actual source and binary file URLs from that list, and take the common directory prefix immediately before `sources/` and `distribution/`. Do not use the candidate page, the URL-list endpoint itself, or a GitHub release URL as the base. + +Confirm from the listed URLs that both subfolders are exposed under that common prefix exactly as `atr_upload` uploads them. `etc/bin/download-release-artifacts.sh` appends `/sources/<filename>` for the source artifacts and `/distribution/<filename>` for the binary, wrapper, and documentation artifacts, including their signatures and checksums. Do not assume ATR preserves that layout: if the listed URLs do not expose both subfolders under one base, this override cannot consume them as-is; do not guess a download URL pattern. Review Comment: Optional: this hedge can be replaced with the concrete base. In ATR's `atr/get/download.py`, `_generate_file_url_list` builds every line of `/download/urls/<project>/<version>` from the recursive relative path of each uploaded file, and the `path` route serves `/download/path/<project>/<version>/<path:file_path>` as an attachment. So while a candidate is under vote the base is `https://<atr-host>/download/path/grails/<version>`, and `sources/` and `distribution/` survive exactly as `atr_upload` rsyncs them. Naming that base here, with the URL list as the confirmation step, saves the release manager deriving it. The same endpoint switches to the published mirror URLs once the version reaches the RELEASE phase, so the override only applies during the vote. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
