You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by jd...@apache.org on 2025/05/30 19:29:20 UTC

(grails-core) 05/06: [skip ci] scripts to automate jar file verification

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

commit 2e61ff2a599151f5e2dfa2ed06554199f2c6bb6a
Author: James Daugherty <jd...@jdresources.net>
AuthorDate: Fri May 30 15:19:25 2025 -0400

    [skip ci] scripts to automate jar file verification
---
 RELEASE.md                                         |  77 ++++++++--------
 etc/bin/verify-jar-artifacts.sh                    | 102 +++++++++++++++++++++
 ...rify-distribution.sh => verify-reproducible.sh} |   0
 3 files changed, 143 insertions(+), 36 deletions(-)

diff --git a/RELEASE.md b/RELEASE.md
index 77e94e8942..f203600f23 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -62,61 +62,44 @@ During the staging step, we must create a source distribution & stage any binary
 
 Prior to releasing a vote, we need to verify the staged artifacts. Follow the below steps to verify each staged artifact.
 
+### Download the Staged Artifacts
+
+Use `etc/bin/download-release-artifacts.sh` to download the staged artifacts. This script will download the source distribution, wrapper binary distribution, and sdkman binary distribution. The distribution should come from [https://dist.apache.org/repos/dist/dev/grails/version](https://dist.apache.org/repos/dist/dev/grails).
+
 ### Source Distribution Verification
-Download the zipped source distribution artifacts:
+
+The following are the source distribution artifacts:
    * `apache-grails-<version>-incubating-src.zip` - the source distribution
    * `apache-grails-<version>-incubating-src.zip.asc` - the public key to verify the source distribution
    * `apache-grails-<version>-incubating-src.zip.sha512` - the checksum to verify the source distribution
 
-from [https://dist.apache.org/repos/dist/dev/grails/version](https://dist.apache.org/repos/dist/dev/grails)
+Use `etc/bin/verify-source-distribution.sh` to verify the source distribution. This script performs the following:
 
-Verify the source distribution checksum via the command:
+Verifies the source distribution checksum via the command:
    ```bash
    shasum -a 512 -c apache-grails-<version>-incubating-src.zip.sha512
    ```
 
-Verify the source distribution signature via the command:
+Verifies the source distribution signature via the command:
    ```bash
     gpg --verify apache-grails-<version>-incubating-src.zip.asc apache-grails-<version>-incubating-src.zip
    ```
 
-Extract the zip file and verify the contents:
+Extracts the zip file and verifies the contents:
    * Ensure the `LICENSE` & `NOTICE` files are present to ensure license compliance.
    * Ensure `README.md` & `CONTRIBUTING.md` are present to ensure project build & usage instructions are present.
-   * Ensure the `PUBLISHED` file is present so we know how to pull the various jar files.
+   * Ensure the `PUBLISHED_ARTIFACTS` file is present so we know how to pull the various jar files.
+   * Ensure the `CHECKSUMS` file is present so we can ensure those checksums match the staged artifacts.
 
 ### Jar file Signature Verification (Nexus Staging Repositories)
-As part of uploading to repository.apache.org the signatures are verified to match a KEY that has been distributed.  It does not verify that the jar files are built with a key trusted by the Grails project.
 
-Download the latest KEYS file and make sure it's imported into gpg:
-```bash
-    wget https://github.com/apache/grails-core/blob/7.0.x/KEYS
-    gpg --import KEYS
-```
+As part of uploading to repository.apache.org the signatures are verified to match a KEY that has been distributed, but RAO does not verify that the jar files are built with a key trusted by the Grails project.
+
+To ensure checksums match the server & signatures match, run the script `etc/bin/verify-jar-artifacts.sh` in the `grails-core` repository. This script will download the jar files from the staging repository, verify their signatures, and ensure they match the checksums provided in the source distribution.
 
-The jar files will need downloaded and verified they were signed by a valid Grails key.  Run the script (substitute the staging repo name):  
+Example: 
 ```bash
-    wget --recursive --no-parent --accept jar,asc https://repository.apache.org/content/repositories/orgapachegrails-1020/org/apache/grails
-    for jar_file in *.jar; do
-      asc_file="${jar_file}.asc"
-    
-      if [[ -f "$asc_file" ]]; then
-        echo "🔍 Verifying $jar_file..."
-    
-        gpg --verify "$asc_file" "$jar_file"
-        verify_status=$?
-    
-        if [ $verify_status -eq 0 ]; then
-          echo "✅ $jar_file is correctly signed."
-        else
-          echo "❌ $jar_file FAILED signature verification!"
-        fi
-    
-        echo ""
-      else
-        echo "⚠️ No .asc file found for $jar_file. Skipping."
-      fi
-    done
+    ./etc/bin/verify-jar-artifacts.sh orgapachegrails-1026 v7.0.0-M4 <grailsdownloadlocation>
 ```
 
 ### Reproducible Jar File
@@ -127,7 +110,7 @@ Bootstrap the source distribution so that it can be built:
     gradle wrapper
     ```
 
-Run the `verify-distribution.sh` shell script to compare the published jar files to a locally built version of them. For any differences, extract the jar files, use IntelliJ to compare each differing file. Assuming differences are ordering related, we can continue with the verification.
+Run the `verify-reproducible.sh` shell script to compare the published jar files to a locally built version of them. For any differences, extract the jar files, use IntelliJ to compare each differing file. Assuming differences are ordering related, we can continue with the verification.
 
 ### Binary Distribution Verification
 Download the binary distribution & expand it to test the various CLI's: `grailsw` (wrapper), `grails` (delegating), `grails-forge-cli`, and `grails-shell-cli`.  For each CLI, verify the published signature in the `PUBLISHED` file:
@@ -182,4 +165,26 @@ To remove a Nexus staging repo, run the workflow `Release - Drop Nexus Staging`
 
 ## Rollback Distribution
 
-To remove the staged distribution, use your SVN credentials to remove the version directory at [https://dist.apache.org/repos/dist/dev/grails](https://dist.apache.org/repos/dist/dev/grails)
\ No newline at end of file
+To remove the staged distribution, use your SVN credentials to remove the version directory at [https://dist.apache.org/repos/dist/dev/grails](https://dist.apache.org/repos/dist/dev/grails)
+
+## Appendix: GPG Configuration
+If you wish to verify any artifact manually, you must trust the key used to build Grails. To do so:
+
+Download the latest KEYS file and make sure it's imported into gpg:
+```bash
+    wget https://github.com/apache/grails-core/blob/7.0.x/KEYS
+    gpg --import KEYS
+```
+
+Setup the key for trust:
+```bash
+   gpg --edit-key <key id>
+   gpg> trust
+   gpg> 4
+   gpg> quit
+```
+
+Setup the key for validity:
+```bash
+   gpg --lsign-key 08E2CEC47E38FE415F080AB62ADECADC11775306
+```
\ No newline at end of file
diff --git a/etc/bin/verify-jar-artifacts.sh b/etc/bin/verify-jar-artifacts.sh
new file mode 100755
index 0000000000..0fe52d14f4
--- /dev/null
+++ b/etc/bin/verify-jar-artifacts.sh
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+#
+
+set -euo pipefail
+
+STAGING_REPO_ID=$1
+RELEASE_TAG=$2
+DOWNLOAD_LOCATION="${3:-downloads}"
+DOWNLOAD_LOCATION=$(realpath "${DOWNLOAD_LOCATION}")
+
+if [ -z "${STAGING_REPO_ID}" ] || [ -z "${RELEASE_TAG}" ]; then
+  echo "Usage: $0 [staging-repo-id] [release-tag] <optional download location>"
+  exit 1
+fi
+
+VERSION=${RELEASE_TAG#v}
+
+ARTIFACTS_FILE="${DOWNLOAD_LOCATION}/PUBLISHED_ARTIFACTS"
+CHECKSUMS_FILE="${DOWNLOAD_LOCATION}/CHECKSUMS"
+
+if [ ! -f "${ARTIFACTS_FILE}" ] || [ ! -f "${CHECKSUMS_FILE}" ]; then
+  echo "Required files ${ARTIFACTS_FILE} and/or ${CHECKSUMS_FILE} not found."
+  exit 1
+fi
+
+
+REPO_BASE_URL="https://repository.apache.org/content/repositories/${STAGING_REPO_ID}"
+
+# Create a temporary directory to work in
+WORK_DIR='build'
+mkdir -p $WORK_DIR
+echo "Using temp dir: $WORK_DIR"
+cd "$WORK_DIR"
+
+# Read each line from PUBLISHED_ARTIFACTS
+while IFS= read -r line; do
+  JAR_FILE=$(echo "${line}" | awk '{print $1}')
+  [[ "${JAR_FILE}" != *.jar ]] && continue
+  
+  COORDINATES=$(echo "${line}" | awk '{print $2}')
+
+  GROUP_ID=$(echo "${COORDINATES}" | cut -d: -f1 | tr '.' '/')
+  ARTIFACT_ID=$(echo "${COORDINATES}" | cut -d: -f2)
+  VERSION=$(echo "${COORDINATES}" | cut -d: -f3)
+  CLASSIFIER=$(echo "${COORDINATES}" | cut -d: -f4-)
+
+  if [[ -n "${CLASSIFIER}" ]]; then
+    FILE_NAME="${ARTIFACT_ID}-${VERSION}-${CLASSIFIER}.jar"
+  else
+    FILE_NAME="${ARTIFACT_ID}-${VERSION}.jar"
+  fi
+
+  JAR_URL="${REPO_BASE_URL}/${GROUP_ID}/${ARTIFACT_ID}/${VERSION}/${FILE_NAME}"
+  ASC_URL="${JAR_URL}.asc"
+
+  echo "🔎 Checking artifact: ${FILE_NAME}"
+  echo "... Downloading: ${JAR_URL}"
+  curl -sSfLO "${JAR_URL}"
+
+  echo "... Downloading signature: ${ASC_URL}"
+  curl -sSfLO "${ASC_URL}"
+
+  echo "... Verifying GPG signature..."
+  gpg --verify "${FILE_NAME}.asc" "${FILE_NAME}"
+
+  EXPECTED_CHECKSUM=$(grep "^${FILE_NAME} " "${CHECKSUMS_FILE}" | awk '{print $2}')
+  if [ -z "${EXPECTED_CHECKSUM}" ]; then
+    echo "❌ Checksum not found for ${FILE_NAME}"
+    exit 1
+  fi
+
+  echo "... Verifying checksum..."
+  ACTUAL_CHECKSUM=$(shasum -a 512 "${FILE_NAME}" | awk '{print $1}')
+
+  if [ "${ACTUAL_CHECKSUM}" != "${EXPECTED_CHECKSUM}" ]; then
+    echo "❌ Checksum mismatch for ${FILE_NAME}"
+    echo "Expected: ${EXPECTED_CHECKSUM}"
+    echo "Actual:   ${ACTUAL_CHECKSUM}"
+    exit 1
+  fi
+
+  echo "✅ Verified: ${FILE_NAME}"
+done < "${ARTIFACTS_FILE}"
+
+echo "✅✅✅ All artifacts verified successfully. ✅✅✅"
diff --git a/etc/bin/verify-distribution.sh b/etc/bin/verify-reproducible.sh
similarity index 100%
rename from etc/bin/verify-distribution.sh
rename to etc/bin/verify-reproducible.sh