From a148e3957a638090b060f0d4c3ad53f9292afd24 Mon Sep 17 00:00:00 2001 From: Charlotte Wilson <251223230+cswilson252@users.noreply.github.com> Date: Sun, 22 Feb 2026 14:21:33 -0500 Subject: [PATCH 1/3] feat: automate deletion of stale snapshots --- build.gradle | 5 +++- scripts/snapshot_management.sh | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 scripts/snapshot_management.sh diff --git a/build.gradle b/build.gradle index ab4b16a146..2f86927075 100644 --- a/build.gradle +++ b/build.gradle @@ -200,7 +200,10 @@ subprojects { // But since the dependsOn is within the conditional, the dependency is not created for release versions // so the publish in that case is never executed. if (version.toString().endsWith('SNAPSHOT')) { - dependsOn tasks.publish + exec { + workingDir project.projectDir + commandLine 'bash', "${project.rootDir}/scripts/snapshot_management.sh", project.name, project.version + } } doFirst { if (!version.toString().endsWith('SNAPSHOT')) { diff --git a/scripts/snapshot_management.sh b/scripts/snapshot_management.sh new file mode 100644 index 0000000000..c329f2f830 --- /dev/null +++ b/scripts/snapshot_management.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# This script manages the uploading of snapshots to the Maven Central snapshot repository, and deletion of the associated snapshots after a set retention time. +set -e + +subproject=$1 +version=$2 + +echo "Running $(basename $0) for subproject \"$subproject\" and version \"$version\"" +echo "Executing in directory = $(pwd)" + +zipfile=${subproject}.${version}.zip + +pushd build/local-repo +zip -qr ${zipfile} * + +bearer_token=$(echo "$MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME:$MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD" | base64) + +# We delete older snapshots before uploading the new one to Maven Central's snapshot repository. +#TODO: this does not iterate properly into the for. fix +for deployments in $subproject; do +getId=$(curl -s --request POST \ + --header "Authorization: Bearer ${bearer_token}" \ + 'https://central.sonatype.com/api/v1/publisher/deployments/files') +staleId=$(getId | jq -r '.deploymentIds') +echo "id from old snapshot: $staleId" + +dropStale=$(curl --request DELETE \ + --verbose \ + --header "Authorization: Bearer ${bearer_token}" \ + "https://central.sonatype.com/api/v1/publisher/deployments/{$staleId}") +done + +# We publish to the snapshot repository automatically whenever this script is called, without manual intervention. +answer=$(curl --request POST \ + --verbose \ + --header "Authorization: Bearer ${bearer_token}" \ + --form bundle="@${zipfile}" \ + 'https://central.sonatype.com/api/v1/publisher/upload?publishingType=AUTOMATIC') + +echo "curl request answer: $answer" + +popd \ No newline at end of file From ea37681b25b2289cbdb134dd4f9f04a11423dfb5 Mon Sep 17 00:00:00 2001 From: Charlotte Wilson <251223230+cswilson252@users.noreply.github.com> Date: Tue, 3 Mar 2026 18:06:29 -0500 Subject: [PATCH 2/3] Clarify snapshot deletion comment in script Updated comment to clarify snapshot deletion process. --- scripts/snapshot_management.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/snapshot_management.sh b/scripts/snapshot_management.sh index c329f2f830..ea3ce95c75 100644 --- a/scripts/snapshot_management.sh +++ b/scripts/snapshot_management.sh @@ -15,8 +15,7 @@ zip -qr ${zipfile} * bearer_token=$(echo "$MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME:$MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD" | base64) -# We delete older snapshots before uploading the new one to Maven Central's snapshot repository. -#TODO: this does not iterate properly into the for. fix +# We delete older snapshots before uploading the newest one to the project's snapshot repository on Maven Central. for deployments in $subproject; do getId=$(curl -s --request POST \ --header "Authorization: Bearer ${bearer_token}" \ @@ -39,4 +38,4 @@ answer=$(curl --request POST \ echo "curl request answer: $answer" -popd \ No newline at end of file +popd From 58d7c218ed15f5887231aa81bd1c88ab9901a58e Mon Sep 17 00:00:00 2001 From: Charlotte Wilson <251223230+cswilson252@users.noreply.github.com> Date: Tue, 16 Jun 2026 18:57:47 -0400 Subject: [PATCH 3/3] copilot changes --- scripts/publish_to_maven_central.sh | 32 +++++++++----- scripts/snapshot_management.sh | 67 ++++++++++++++++++----------- 2 files changed, 62 insertions(+), 37 deletions(-) diff --git a/scripts/publish_to_maven_central.sh b/scripts/publish_to_maven_central.sh index 76157c5e13..ac7ba9cc87 100644 --- a/scripts/publish_to_maven_central.sh +++ b/scripts/publish_to_maven_central.sh @@ -12,11 +12,25 @@ echo "Executing in directory = $(pwd)" zipfile=${subproject}.${version}.zip -pushd build/local-repo -zip -qr ${zipfile} * - -bearer_token=$(echo "$MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME:$MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD" | base64) - +# Ensure jq is available if later used by callers; not strictly required here but helpful to surface missing deps early. +if ! command -v jq >/dev/null 2>&1; then + echo "Warning: jq not found. Some scripts expect jq for JSON parsing. Continue if you're sure it's not needed." +fi + +pushd build/local-repo >/dev/null +zip -qr "${zipfile}" * + +# Build Authorization header: prefer an explicit bearer token, otherwise fall back to Basic auth built from username/password +if [ -n "${MAVEN_CENTRAL_PORTAL_BEARER_TOKEN:-}" ]; then + auth_header="Authorization: Bearer ${MAVEN_CENTRAL_PORTAL_BEARER_TOKEN}" +elif [ -n "${MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME:-}" ] && [ -n "${MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD:-}" ]; then + basic_token=$(echo -n "${MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME}:${MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD}" | base64 | tr -d '\n') + auth_header="Authorization: Basic ${basic_token}" +else + echo "Error: No credentials found. Set MAVEN_CENTRAL_PORTAL_BEARER_TOKEN or MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME and MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD." + popd >/dev/null || true + exit 1 +fi # Upload to maven central. # publishingType=USER_MANAGED means that the artefacts will be uploaded and verified, but not yet published. @@ -24,12 +38,8 @@ bearer_token=$(echo "$MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME:$MAVEN_CENTRAL_PORTAL_ # From this page you can examine the list of artefacts, and either drop them or release them. # If you want to remove that step, use publishingType=AUTOMATIC below, that will publish directly. # Note that once published you cannot remove or alter the artifacts. -answer=$(curl --request POST \ - --verbose \ - --header "Authorization: Bearer ${bearer_token}" \ - --form bundle="@${zipfile}" \ - 'https://central.sonatype.com/api/v1/publisher/upload?publishingType=USER_MANAGED') +answer=$(curl --request POST --silent --show-error --header "${auth_header}" --form bundle="@${zipfile}" 'https://central.sonatype.com/api/v1/publisher/upload?publishingType=USER_MANAGED') echo "curl request answer: $answer" -popd \ No newline at end of file +popd >/dev/null \ No newline at end of file diff --git a/scripts/snapshot_management.sh b/scripts/snapshot_management.sh index ea3ce95c75..237dedd334 100644 --- a/scripts/snapshot_management.sh +++ b/scripts/snapshot_management.sh @@ -10,32 +10,47 @@ echo "Executing in directory = $(pwd)" zipfile=${subproject}.${version}.zip -pushd build/local-repo -zip -qr ${zipfile} * - -bearer_token=$(echo "$MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME:$MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD" | base64) - -# We delete older snapshots before uploading the newest one to the project's snapshot repository on Maven Central. -for deployments in $subproject; do -getId=$(curl -s --request POST \ - --header "Authorization: Bearer ${bearer_token}" \ - 'https://central.sonatype.com/api/v1/publisher/deployments/files') -staleId=$(getId | jq -r '.deploymentIds') -echo "id from old snapshot: $staleId" - -dropStale=$(curl --request DELETE \ - --verbose \ - --header "Authorization: Bearer ${bearer_token}" \ - "https://central.sonatype.com/api/v1/publisher/deployments/{$staleId}") -done - -# We publish to the snapshot repository automatically whenever this script is called, without manual intervention. -answer=$(curl --request POST \ - --verbose \ - --header "Authorization: Bearer ${bearer_token}" \ - --form bundle="@${zipfile}" \ - 'https://central.sonatype.com/api/v1/publisher/upload?publishingType=AUTOMATIC') +# Ensure jq is available for JSON parsing +if ! command -v jq >/dev/null 2>&1; then + echo "Error: jq is required but not installed. Please install jq and retry." + exit 1 +fi + +pushd build/local-repo >/dev/null +zip -qr "${zipfile}" * + +# Build Authorization header: prefer an explicit bearer token, otherwise fall back to Basic auth built from username/password +if [ -n "${MAVEN_CENTRAL_PORTAL_BEARER_TOKEN:-}" ]; then + auth_header="Authorization: Bearer ${MAVEN_CENTRAL_PORTAL_BEARER_TOKEN}" +elif [ -n "${MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME:-}" ] && [ -n "${MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD:-}" ]; then + basic_token=$(echo -n "${MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME}:${MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD}" | base64 | tr -d '\n') + auth_header="Authorization: Basic ${basic_token}" +else + echo "Error: No credentials found. Set MAVEN_CENTRAL_PORTAL_BEARER_TOKEN or MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME and MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD." + popd >/dev/null || true + exit 1 +fi + +# Delete older snapshots before uploading the newest one to the project's snapshot repository on Maven Central. +# Query the deployments/files endpoint and delete each returned deployment id. +response=$(curl -s --request POST --header "${auth_header}" 'https://central.sonatype.com/api/v1/publisher/deployments/files') +if [ -z "${response}" ]; then + echo "No response from Sonatype when listing deployments." +else + staleIds=$(echo "${response}" | jq -r '.deploymentIds[]?') + if [ -z "${staleIds}" ]; then + echo "No stale deployment ids found." + else + for id in ${staleIds}; do + echo "Deleting stale deployment id: ${id}" + curl -s --request DELETE --header "${auth_header}" "https://central.sonatype.com/api/v1/publisher/deployments/${id}" || echo "Warning: failed to delete deployment ${id}" + done + fi +fi + +# Publish to the snapshot repository automatically (no manual intervention). +answer=$(curl --request POST --silent --show-error --header "${auth_header}" --form bundle="@${zipfile}" 'https://central.sonatype.com/api/v1/publisher/upload?publishingType=AUTOMATIC') echo "curl request answer: $answer" -popd +popd >/dev/null