This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a commit to branch main
in repository
https://gitbox.apache.org/repos/asf/incubator-grails-gradle-publish.git
The following commit(s) were added to refs/heads/main by this push:
new f1106b4 chore: add release helper scripts for post voting
f1106b4 is described below
commit f1106b406efc2fe4a1a12c3cce23bde68adbd486
Author: James Daugherty <[email protected]>
AuthorDate: Thu Sep 11 14:33:21 2025 -0400
chore: add release helper scripts for post voting
---
.asf.yaml | 7 ++
.github/scripts/releaseDistributions.sh | 83 +++++++++++++++++++
.github/scripts/releaseJarFiles.sh | 141 ++++++++++++++++++++++++++++++++
.github/workflows/release.yaml | 89 +++++++++++++++-----
4 files changed, 299 insertions(+), 21 deletions(-)
diff --git a/.asf.yaml b/.asf.yaml
index fc165e8..6a1d253 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -27,6 +27,13 @@ github:
- id: jdaugherty
type: User
wait_timer: 0
+ close:
+ required_reviewers:
+ - id: grails-committers
+ type: Team
+ - id: jdaugherty
+ type: User
+ wait_timer: 0
notifications:
# GitHub related
commits: [email protected]
diff --git a/.github/scripts/releaseDistributions.sh
b/.github/scripts/releaseDistributions.sh
new file mode 100755
index 0000000..08da0e9
--- /dev/null
+++ b/.github/scripts/releaseDistributions.sh
@@ -0,0 +1,83 @@
+#!/bin/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.
+#
+
+# ./releaseDistributions.sh <tag> <username> <password>
+
+set -euo pipefail
+
+if [[ $# -ne 3 ]]; then
+ echo "Usage: $0 <tag> <username> <password>" >&2
+ exit 1
+fi
+
+RELEASE_TAG="$1"
+RELEASE_VERSION="${RELEASE_TAG#v}"
+SVN_USER="$2"
+SVN_PASS="$3"
+RELEASE_ROOT="https://dist.apache.org/repos/dist/release/incubator/grails/grails-publish"
+DEV_ROOT="https://dist.apache.org/repos/dist/dev/incubator/grails/grails-publish"
+
+if [[ -z "${RELEASE_TAG}" ]]; then
+ echo "â ERROR: Release Tag must not be empty." >&2
+ exit 1
+fi
+if [[ -z "${SVN_USER}" ]]; then
+ echo "â ERROR: Username must not be empty." >&2
+ exit 1
+fi
+if [[ -z "${SVN_PASS}" ]]; then
+ echo "â ERROR: Password must not be empty." >&2
+ exit 1
+fi
+
+svn_flags=(--non-interactive --trust-server-cert --username "${SVN_USER}"
--password "${SVN_PASS}")
+
+svn_exists() {
+ local url="$1"
+ svn ls "${svn_flags[@]}" --depth=empty "${url}" >/dev/null 2>&1
+}
+
+old_release_folder="$(svn ls "${svn_flags[@]}" "${RELEASE_ROOT}" | awk -F/
'NF{print $1; exit}')"
+if [[ -n "${old_release_folder}" ]]; then
+ PRIOR_RELEASE_URL="${RELEASE_ROOT}/${old_release_folder}"
+ echo "đī¸ Deleting old release folder: ${PRIOR_RELEASE_URL}"
+ svn rm "${svn_flags[@]}" -m "Remove previous release ${old_release_folder}"
"${PRIOR_RELEASE_URL}"
+ echo "â
Deleted old release folder"
+else
+ echo "âšī¸ No existing release subfolder found under ${RELEASE_ROOT}"
+fi
+
+DEV_VERSION_URL="$DEV_ROOT/${RELEASE_VERSION}"
+RELEASE_VERSION_URL="$RELEASE_ROOT/${RELEASE_VERSION}"
+
+if ! svn_exists "${DEV_VERSION_URL}"; then
+ echo "â ERROR: dev folder for ${RELEASE_VERSION} does not exist at:
${DEV_VERSION_URL}" >&2
+ exit 2
+fi
+
+if svn_exists "${RELEASE_VERSION_URL}"; then
+ echo "â ERROR: release folder for ${RELEASE_VERSION} already exists at:
${RELEASE_VERSION_URL}" >&2
+ exit 3
+fi
+
+echo "đ Promoting ${DEV_VERSION_URL} -> ${RELEASE_VERSION_URL}"
+svn mv "${svn_flags[@]}" -m "Promote Apache Grails (incubating)
${RELEASE_VERSION} from dev to release" "${DEV_VERSION_URL}"
"${RELEASE_VERSION_URL}"
+echo "â
Promoted"
diff --git a/.github/scripts/releaseJarFiles.sh
b/.github/scripts/releaseJarFiles.sh
new file mode 100755
index 0000000..8421782
--- /dev/null
+++ b/.github/scripts/releaseJarFiles.sh
@@ -0,0 +1,141 @@
+#!/bin/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.
+#
+
+# ./releaseJarFiles.sh <staging repo description> <username> <password>
+
+set -euo pipefail
+
+if [[ $# -ne 3 ]]; then
+ echo "Usage: $0 <staging repo description> <username> <password>" >&2
+ exit 1
+fi
+
+NEXUS_URL="https://repository.apache.org"
+STAGING_DESCRIPTION="$1"
+NEXUS_USER="$2"
+NEXUS_PASS="$3"
+
+if [[ -z "${STAGING_DESCRIPTION}" ]]; then
+ echo "ERROR: Staging Description must not be empty." >&2
+ exit 1
+fi
+if [[ -z "${NEXUS_USER}" ]]; then
+ echo "ERROR: Username must not be empty." >&2
+ exit 1
+fi
+if [[ -z "${NEXUS_PASS}" ]]; then
+ echo "ERROR: Password must not be empty." >&2
+ exit 1
+fi
+
+nexusApi() {
+ local request_method="$1"; shift
+ local path="$1"; shift
+ curl -fsS -u "${NEXUS_USER}:${NEXUS_PASS}" \
+ -H 'Accept: application/json' \
+ -H 'Content-Type: application/json' \
+ -X "${request_method}" "${NEXUS_URL}/service/local/${path}" "$@"
+}
+
+wait_for_promotion() {
+ local repoId="$1"
+ local timeout_s="${2:-600}" # default 10 minutes
+ local interval_s="${3:-3}"
+ local started
+ started="$(date +%s)"
+
+ echo "Waiting for release promotion to complete (timeout ${timeout_s}s)âĻ"
+
+ while :; do
+ # 1) If any ERROR appears in activity, fail fast
+ act="$(nexusApi GET "/staging/repository/${repoId}/activity" || true)"
+ if [[ -n "$act" ]]; then
+ err_count="$(jq -r '[.. | objects? | select(has("severity")) |
select(.severity=="ERROR")] | length' <<<"$act" 2>/dev/null || echo 0)"
+ if [[ "$err_count" != "0" ]]; then
+ echo "ERROR: Staging activity contains failure(s). Aborting." >&2
+ # Optionally dump recent relevant lines:
+ jq -r '.. | objects? | select(has("severity")) | "\(.severity):
\(.name // "event") - \(.message // "")"' <<<"$act" || true
+ return 1
+ fi
+ fi
+
+ # 2) Check transitioning flag â when false after promote, action is done
+ trans="$(nexusApi GET '/staging/profile_repositories' \
+ | jq -r --arg r "$repoId" '.data[]? | select(.repositoryId==$r) |
.transitioning' 2>/dev/null || echo "true")"
+
+ if [[ "$trans" == "false" ]]; then
+ # sanity: make sure we actually saw some "release/promote" activity;
otherwise keep waiting a bit
+ if [[ -n "$act" ]]; then
+ # did we see any promote/release-ish step?
+ saw_promote="$(jq -r '
+ [ .. | objects? | .name? // empty | ascii_downcase
+ | select(test("release|promote|finish")) ] | length' <<<"$act"
2>/dev/null || echo 0)"
+ if [[ "$saw_promote" -gt 0 ]]; then
+ echo "Promotion appears complete."
+ return 0
+ fi
+ fi
+ fi
+
+ # timeout?
+ now="$(date +%s)"
+ if (( now - started >= timeout_s )); then
+ echo "ERROR: Timed out waiting for promotion to complete." >&2
+ # Show a short summary to aid debugging
+ if [[ -n "$act" ]]; then
+ echo "--- Recent activity snapshot ---"
+ jq -r '.. | objects? | select(has("severity") or has("name")) |
"\(.severity // "")\t\(.name // "")\t\(.message // "")"' <<<"$act" | tail -n 20
|| true
+ fi
+ return 1
+ fi
+
+ sleep "$interval_s"
+ done
+}
+
+repos_json="$(nexusApi GET '/staging/profile_repositories')"
+repoId="$(jq -r --arg d "${STAGING_DESCRIPTION}" '.data[] |
select(.description==$d) | .repositoryId' <<<"${repos_json}")"
+profileId="$(jq -r --arg d "${STAGING_DESCRIPTION}" '.data[] |
select(.description==$d) | .profileId' <<<"${repos_json}")"
+state="$(jq -r --arg d "${STAGING_DESCRIPTION}" '.data[] |
select(.description==$d) | .type' <<<"${repos_json}")"
+
+if [[ -z "${repoId}" || -z "${profileId}" ]]; then
+ echo "ERROR: No staged repository found with description:
${STAGING_DESCRIPTION}" >&2
+ exit 2
+fi
+echo "Found staged repo: ${repoId} (profile: ${profileId}, state: ${state})"
+if [[ "${state}" == "open" ]]; then
+ echo "ERROR: Staged Repo is not closed: ${STAGING_DESCRIPTION}" >&2
+ exit 3
+fi
+
+if [[ "${state}" == "closed" ]]; then
+ echo "Promoting (release) ${repoId}âĻ"
+ nexusApi POST "/staging/profiles/${profileId}/promote" \
+ --data "$(jq -n --arg r "${repoId}" --arg d "${STAGING_DESCRIPTION}"
'{data:{stagedRepositoryId:$r,description:$d}}')"
+fi
+
+wait_for_promotion "$repoId" 600 3
+
+echo "Dropping staging repository ${repoId}âĻ"
+nexusApi POST "/staging/profiles/${profileId}/drop" \
+ --data "$(jq -n --arg r "$repoId" --arg d "${STAGING_DESCRIPTION}"
'{data:{stagedRepositoryId:$r,description:$d}}')"
+
+echo "Done. Released ${repoId}."
\ No newline at end of file
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index c5b8052..72673ae 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -22,7 +22,7 @@ env:
GRAILS_PUBLISH_RELEASE: 'true'
jobs:
publish:
- name: "Stage Artifacts for Release"
+ name: "Stage Jar Files"
permissions:
contents: write # to stage distributions to the GitHub release page
issues: write # to modify milestones
@@ -346,7 +346,7 @@ jobs:
VERSION_COMMIT_ID: ${{ needs.publish.outputs.commit_hash }}
run: |
export DIST_SVN_REVISION=$(awk '/Last Changed Rev:/ {print $4}'
dev-repo/DIST_SVN_REVISION.txt)
-
+ echo "::group::Grails PPMC Vote Email"
echo "*************************************************"
echo "Subject: [VOTE] Release Apache Grails (incubating) - Gradle
Plugin - Grails Publish ${VERSION}"
echo "*************************************************"
@@ -354,6 +354,7 @@ jobs:
echo "*************************************************"
cat grails-publish/.github/vote_templates/staged.txt | envsubst
echo "*************************************************"
+ echo "::endgroup::"
- name: 'đ§ Print Groovy Vote Email'
env:
VERSION: ${{ needs.publish.outputs.release_version }}
@@ -361,6 +362,7 @@ jobs:
run: |
export DIST_SVN_REVISION=$(awk '/Last Changed Rev:/ {print $4}'
dev-repo/DIST_SVN_REVISION.txt)
+ echo "::group::Groovy PMC Vote Email"
echo "*************************************************"
echo "Subject: [VOTE] Approval of Apache Grails (incubating) -
Gradle Plugin - Grails Publish ${VERSION} release by Groovy PMC"
echo "*************************************************"
@@ -368,25 +370,48 @@ jobs:
echo "*************************************************"
cat grails-publish/.github/vote_templates/groovy_pmc.txt | envsubst
echo "*************************************************"
- - name: 'Announcement Email'
- env:
- VERSION: ${{ needs.publish.outputs.release_version }}
- VERSION_COMMIT_ID: ${{ needs.publish.outputs.commit_hash }}
- PREVIOUS_VERSION: 'TODO_PREVIOUS_VERSION'
+ echo "::endgroup::"
+ release:
+ environment: release
+ name: 'VOTE SUCCEEDED - Release Artifacts'
+ needs: [ publish, source, upload ]
+ runs-on: ubuntu-24.04
+ steps:
+ - name: "Output Agent IP" # in the event RAO blocks this agent, this can
be used to debug it
+ run: curl -s https://api.ipify.org
+ - name: "Setup SVN and Tools"
+ run: sudo apt-get install -y subversion subversion-tools tree
gettext-base
+ - name: "đŗ Grails PPMC Vote Confirmation - MANUAL"
run: |
- export DIST_SVN_REVISION=$(awk '/Last Changed Rev:/ {print $4}'
dev-repo/DIST_SVN_REVISION.txt)
-
- echo "*************************************************"
- echo "Subject: [ANNOUNCE] Apache Grails (incubating) - Gradle Plugin
- Grails Publish ${VERSION}"
- echo "*************************************************"
- echo "Body:"
- echo "*************************************************"
- cat grails-publish/.github/vote_templates/announce.txt | envsubst
- echo "*************************************************"
+ echo "::group::Manual Confirmation"
+ echo "đ This step is a placeholder that the vote confirmation on
[email protected] completed successfully."
+ echo "::endgroup::"
+ - name: "đŗ Groovy PMC Vote Confirmation - MANUAL"
+ run: |
+ echo "::group::Manual Confirmation"
+ echo "đ This step is a placeholder that the vote confirmation on
[email protected] completed successfully."
+ echo "::endgroup::"
+ - name: "đ Release JAR files - MANUAL"
+ run: |
+ echo "::group::Manual Jar Promotion"
+ echo "Run .github/scripts/releaseJarFiles.sh 'v${{
needs.publish.outputs.release_version }}' '${{
needs.publish.outputs.extract_repository_name }}:${{
needs.publish.outputs.release_version }}' ASF_USER ASF_PASS"
+ echo "::endgroup::"
+ - name: "đ Release distribution artifacts - MANUAL"
+ run: |
+ echo "::group::Manual ASF Artifact Promotion"
+ echo "Run github/scripts/releaseDistributions.sh 'v${{
needs.publish.outputs.release_version }}' ASF_USER ASF_PASS"
+ echo "::endgroup::"
+ - name: "â
Update ASF Reporter - MANUAL"
+ run: |
+ echo "::group::Manual ASF Reporter Update"
+ TODAY=$(date +"%Y-%m-%d")
+ echo "Check email & update https://reporter.apache.org to mark the
release ${{ needs.publish.outputs.release_version }} as complete as of ${TODAY}"
+ echo "Note: this is a place holder; currently this is not possible
since Groovy sponsors us instead of the incubator PMC"
+ echo "::endgroup::"
docs:
environment: docs
- name: "Publish Documentation"
- needs: [ publish, source, upload ]
+ name: "VOTE SUCCEEDED - Publish Documentation"
+ needs: [ publish, source, upload ] # TODO Once we have confirmed release
won't fail, add it as a dependency here
runs-on: ubuntu-24.04
steps:
- name: "đĨ Checkout repository"
@@ -417,10 +442,10 @@ jobs:
GRADLE_PUBLISH_RELEASE: 'true'
SOURCE_FOLDER: plugin/build/docs/api
VERSION: ${{ needs.publish.outputs.release_version }}
- release:
- name: "Close Release"
+ close:
+ name: "VOTE SUCCEEDED - Close Release"
environment: release
- needs: [ publish, source, upload, docs ]
+ needs: [ publish, source, upload, docs ] # TODO Once we have confirmed
release won't fail, add `release` as a dependency here
runs-on: ubuntu-24.04
permissions:
contents: write # required for gradle.properties revert
@@ -445,3 +470,25 @@ jobs:
develocity-access-key: ${{ secrets.GRAILS_DEVELOCITY_ACCESS_KEY }}
- name: "âī¸ Run post-release"
uses: apache/grails-github-actions/post-release@asf
+ - name: 'đ Create Blog Post - MANUAL'
+ run: |
+ echo "::group::Blog Post Creation - MANUAL"
+ echo "Publish a blog post on https://grails.apache.org/blog/ about
the new release ${{ needs.publish.outputs.release_version }} using the repo
https://github.com/apache/grails-static-website"
+ echo "::endgroup::"
+ - name: 'Announcement Email'
+ env:
+ VERSION: ${{ needs.publish.outputs.release_version }}
+ VERSION_COMMIT_ID: ${{ needs.publish.outputs.commit_hash }}
+ PREVIOUS_VERSION: 'TODO_PREVIOUS_VERSION'
+ run: |
+ export DIST_SVN_REVISION=$(awk '/Last Changed Rev:/ {print $4}'
dev-repo/DIST_SVN_REVISION.txt)
+
+ echo "::group::Announcement Email"
+ echo "*************************************************"
+ echo "Subject: [ANNOUNCE] Apache Grails (incubating) - Gradle Plugin
- Grails Publish ${VERSION}"
+ echo "*************************************************"
+ echo "Body:"
+ echo "*************************************************"
+ cat grails-publish/.github/vote_templates/announce.txt | envsubst
+ echo "*************************************************"
+ echo "::endgroup::"