This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a commit to branch 7.0.x
in repository https://gitbox.apache.org/repos/asf/grails-core.git
The following commit(s) were added to refs/heads/7.0.x by this push:
new 3c6ab0b7ea chore: initial release setup history documentation
3c6ab0b7ea is described below
commit 3c6ab0b7ea7d44882cf45f5abd2dd45a437b56d0
Author: James Daugherty <[email protected]>
AuthorDate: Fri Sep 19 14:55:25 2025 -0400
chore: initial release setup history documentation
---
RELEASE.md | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 149 insertions(+), 2 deletions(-)
diff --git a/RELEASE.md b/RELEASE.md
index 6edfd20a18..3946c809f9 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -16,7 +16,10 @@ limitations under the License.
# Apache Grails Release Process
-This document outlines the steps to release a new version of Apache Grails. It
is important to follow these steps carefully to ensure a smooth release
process. The release process can be divided into 4 stages across the
`grails-core` repository.
+This document outlines the steps to release a new version of Apache Grails. It
is important to follow these steps
+carefully to ensure a smooth release process. The release process can be
divided into 4 stages across the `grails-core`
+repository. The history & requirements of our release setup can be found
+in [Appendix: Release Setup Requirements &
History](#appendix-release-setup-requirements--history).
## Prerequisites
@@ -435,4 +438,148 @@ After an RC release has been vetted by the community,
then a final release will
If a Nexus Repo needs to drop the staging jars, there exists a workflow
`Release - Drop Nexus Staging` in `grails-core`.
The input of this workflow can be obtained by logging
into
[repository.apache.org](https://repository.apache.org/index.html#stagingRepositories)
and finding the staging
-repository name to drop.
\ No newline at end of file
+repository name to drop.
+
+# Appendix: Release Setup Requirements & History
+
+Due to GitHub Actions being
+considered [untrusted
hardware](https://www.apache.org/legal/release-policy.html#owned-controlled-hardware),
there are
+several requirements the ASF Security Team places on ASF projects for projects
to release via GitHub Actions. To
+summarize the main requirement: a build must be verifiable so that code built
in GitHub Actions can be compared to code
+built elsewhere and proven to be equivalent. The most common way to ensure a
build is verifiable is to ensure the build
+is reproducible. This section documents the Grails Development Team's efforts
to meet these requirements & the process
+we followed to ensure we are compliant.
+
+## Step 1: Ensuring we are reproducible
+
+For Grails, significant work has been done to ensure the framework is
reproducible. This work ensures it's documentation
+artifacts, source artifacts, & binary artifacts are all verifiable. End Grails
applications can also be made to be
+reproducible as a result of this work. From our experience, reproducibility is
first best addressed building on the same
+machine and then secondarily building in GitHub actions and comparing the
results.
+
+The Grails Development Team created several shell scripts that test
reproducibility. Each script is enumerated:
+
+1. etc/bin/test-reproducible-build.sh - a script that targets making a single
Gradle project (in a multi-project build)
+ reproducible. Built results are stored in /etc/bin/results and ignored via
`.gitignore`.
+2. etc/bin/test-reproducible-builds.sh - a script that targets making the
entire Grails multi-project build reproducible
+ by building all projects twice, and comparing the results. Built results
are stored in /etc/bin/results and ignored
+ via `.gitignore`.
+3. etc/bin/generate-build-artifact-hashes.groovy - a Groovy script that
generates hashes for all built artifacts. This
+ allows quicker comparison for which files differ.
+4. etc/bin/extract-build-artifact.sh - a script that extracts a built jar file
to allow for easier comparison of
+ differences. Executing this script will create a `firstArtifact` &
`secondArtifact` directory in `/etc/bin/results`
+ that can be compared via IntelliJ (right click the 2 selected directories
to compare). Since IntelliJ uses the Fern
+ Decompiler, this allows for isolating why the files are different - if the
decompiled code is the same, it means a
+ timestamp or file ordering issue exists.
+
+To test reproducibility locally, running etc/bin/test-reproducible-builds.sh
will execute the necessary gradle commands
+to build the three gradle projects Grails uses. The artifacts are then saved
off, and built again. Finally, the hashes
+are generated to ensure the artifacts are the same.
+
+Some common gotchas with Java build reproducibility problems:
+
+1. Most tools support a `SOURCE_DATE_EPOCH` environment variable that can be
set to a fixed time to ensure timestamps
+ are reproducible. Grails sets this based on the last Git Commit and it's
release process preserves the date chosen.
+2. The date set by `SOURCE_DATE_EPOCH` will need to be passed to various
Gradle configuration to ensure the same date is
+ used. Grails performs this configuration in it's root `build.gradle` file.
+3. Properties files are not reproducible because they write out the current
time. Grails publishes a utility to mutate
+ property files so they're reproducible. As part of our build process, we
mutate any property file we generate and do
+ not ship property files with dynamic timestamps.
+4. Gradle builds are not reproducible by default because Gradle does not
guarantee file ordering. Grails configures all
+ tasks that create archive files to ensure file ordering is reproducible.
This configuration can be found in
+ `gradle/java-config.gradle`
+5. Grails makes heavy use of Groovy AST transforms. These transforms often
lookup methods on a class, and the methods
+ returned by Java will vary by operating system. Any code using reflection
to lookup methods or fields must sort the
+ results to ensure reproducibility.
+6. Javadoc / Groovydoc will by default write out dates in it's documentation
headers. Grails configures these settings
+ in `gradle/java-config.gradle`.
+
+After a build is made reproducible on the same machine, the next step is to
ensure it's reproducible in GitHub actions.
+OS differences will exist and even exist between different Docker Runtimes. To
help test reproducible builds, we found
+it best to create a docker container that closely resembles the GitHub actions
environment. This docker file is checked
+in under bin. The docker image will mount the local project directory into the
container so that builds can be performed
+in the container, but compared on the host machine. To run our docker file,
the process is documented in the
+section [Appendix: Verification from a
Container](#appendix-verification-from-a-container).
+
+## Step 2: Obtaining Secrets for GitHub Actions
+
+After the build has been shown to be reproducible & a document similar to this
file covering the expected release steps
+has been created, the next step is to obtain the necessary secrets to sign &
stage artifacts. The Grails Development
+Team worked with the ASF Security Team to obtain the necessary secrets by
creating an infrastructure issue.
+
+Secrets we use for our builds include:
+
+1. GPG Signing Key - secret name: `GPG_PRIVATE_KEY`
+ ASF Infra needs to generate a GPG Key that will be used to sign artifacts.
As part of that key setup, infra should
+ ask for another key so a revocation cert can be established in the event
the key needs to be revoked too. A PMC
+ member used their key for the Grails key, but multiple can be used if
asked. The public key will also need added to
+ the project's KEYS file so external users can verify artifacts.
+
+2. GPG Signing Key ID - secret name: `GPG_KEY_ID`
+ The ID of the GPG key that was created. This is used to tell GPG which key
to use for signing. This can be extracted
+ from the GPG key with the following command: `gpg --list-keys
--keyid-format short`. We use Gradle to sign our
+ artifacts, and
https://docs.gradle.org/current/userguide/signing_plugin.html says it's the
last 8 digits of the key,
+ but I've found the previously mentioned GPG command more reliable.
+
+3. Nexus Credentials to Publish Snapshot Artifacts
+ Credentials to publish to https://repository.apache.org for snapshots need
provided. We had these credentials placed
+ in the following secrets:
+ * `NEXUS_USERNAME` - the username
+ * `NEXUS_PASSWORD` - the password
+
+4. Nexus Credentials to Stage Release Artifacts
+ Credentials to publish to https://repository.apache.org for staging need
provided. We had these credentials placed in
+ the following secrets:
+ * `NEXUS_STAGE_DEPLOYER_USER` - the username
+ * `NEXUS_STAGE_DEPLOYER_PW` - the password
+
+5. The Nexus Staging Profile ID
+ Grails uses the nexus-publish plugin in it's own publishing plugin
(grails-publish). This plugin requires the staging
+ profile ID. We added this ID as a secret named `NEXUS_STAGING_PROFILE_ID`.
This ID can be found by logging
+ into https://repository.apache.org, clicking on "Staging Profiles" and then
clicking on the profile used to stage
+ artifacts. The ID is in the URL at the end.
+
+6. SVN Credentials
+ The built artifacts will need uploaded to the https://dist.apache.org SVN
repository. Infrastructure will provide
+ credentials that can publish to `dev` only. The credentials to do so were
placed in the following secrets:
+ * `SVC_DIST_GRAILS_USERNAME` - the SVN username
+ * `SVC_DIST_GRAILS_PASSWORD` - the SVN password
+
+7. Develocity Access Key - secret name: `GRAILS_DEVELOCITY_ACCESS_KEY `
+ Grails uses Gradle's Develocity with it's Gradle builds. This secret
contains the access key to publish / use the
+ cache in Develocity.
+
+Note: A caution about secrets, GitHub action secrets will often need entered
with a new line for secrets like the GPG
+key. If errors are encountered in the build after Infrastructure setups
secrets, it's likely because the new line after
+the secret value was not added.
+
+## Step 3: Workflow Setup
+
+Once the secrets have been added to the GitHub repository, the next step is to
create the GitHub workflows. Grails uses
+the following workflows:
+
+1. `codeql.yml` - Configures Code Quality Scans
+2. `codestyle.yml` - Runs checkstyle on our build to ensure code style
requirements are met against any submitted code.
+3. `forge-*.yml` - Workflows to build & publish our public App Generation
website.
+4. `gradle.yml` - Our main CI workflow & snapshot publishing.
+5. `groovy-joint-workflow.yml` - A workflow that runs with the latest snapshot
of Groovy to ensure we are forward
+ compatible and give the Groovy team early feedback.
+6. `rat.yml` - A workflow that runs the Apache RAT license audit to ensure
license compliance. We use the Gradle plugin
+ org.nosphere.apache.rat` to perform the audit.
+7. `release.yml` - The main release workflow that performs the release steps
documented earlier in this document.
+8. `release-abort.yml` - A workflow that can be used to abort a release prior
to voting. See the
+ section [Rollback](#rollback) for more details.
+9. `release-drop-staging.yml` - A workflow that can be used to drop a staging
repository. We rarely use this now that we
+ have the `release-abort.yml` workflow.
+10. `release-notes.yml` - A workflow that configures Release Drafter so that
release notes are automatically generated
+ as pull requests are merged.
+
+The key workflows above are: `gradle.yml`, `release.yml`, and
`release-abort.yml`.
+
+The workflows have been gradually made to be more generic, but they still
contain specific folder names / project names.
+If you wish to fork these workflows, please ensure you update project specific
information.
+
+## Step 4 - Next Steps
+
+After the workflows are setup, they should be tested per this Release
document. GitHub releases can be deleted and
+recreated as needed to test the release process up to voting. Use the `Release
Abort` workflow to rollback as needed.
\ No newline at end of file