Skip to content

[VERSION] Snapshot v2.2.1-SNAPSHOT (#132) #74

[VERSION] Snapshot v2.2.1-SNAPSHOT (#132)

[VERSION] Snapshot v2.2.1-SNAPSHOT (#132) #74

name: Publish Artifacts
on:
push:
branches:
- main
tags:
- 'v*'
pull_request:
types: [opened, synchronize, reopened, labeled]
env:
GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false"
jobs:
publish:
runs-on: macos-latest
if: github.repository == 'episode6/mockspresso2' && (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'publish-snapshot'))
steps:
- uses: actions/checkout@v6
- name: Set up JDK 23
uses: actions/setup-java@v5
with:
java-version: '23'
distribution: 'zulu'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Clean Project
run: ./gradlew clean
- name: Check and Publish to Maven Local
run: ./gradlew check publishToMavenLocal
env:
ORG_GRADLE_PROJECT_nexusUsername: ${{ secrets.NEXUS_USERNAME }}
ORG_GRADLE_PROJECT_nexusPassword: ${{ secrets.NEXUS_PASSWORD }}
- name: Prepare Sonatype Bundle
run: |
mkdir -p bundle/com/episode6
cp -r ~/.m2/repository/com/episode6/mockspresso2 bundle/com/episode6/
# Drop Maven local-repo metadata; remote repos manage their own
# maven-metadata.xml and reject the -local variant (HTTP 400).
find bundle -name "maven-metadata*.xml" -delete
# Sign all artifacts with the dedicated signing subkey. Fail loudly if the
# secret holds anything else — an unpinned gpg would happily fall back to
# whatever signing-capable key it found.
echo "${{ secrets.GPG_MAVEN_KEY }}" | gpg --batch --import
gpg --list-secret-keys 3EBBA2410EE1077E > /dev/null 2>&1 || {
echo "::error::GPG_MAVEN_KEY does not contain signing subkey 3EBBA2410EE1077E"
exit 1
}
find bundle -type f ! -name "*.asc" ! -name "*.md5" ! -name "*.sha1" ! -name "*.sha256" ! -name "*.sha512" | while read -r file; do
gpg --batch --yes --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASS }}" --local-user '3EBBA2410EE1077E!' --digest-algo SHA512 --detach-sign --armor "$file" || exit 1
done
# Generate checksums
find bundle -type f ! -name "*.md5" ! -name "*.sha1" ! -name "*.sha256" ! -name "*.sha512" | while read -r file; do
for alg in md5 sha1 sha256 sha512; do
if [ "$alg" == "md5" ]; then md5sum "$file" | cut -d ' ' -f 1 > "$file.md5"
elif [ "$alg" == "sha1" ]; then sha1sum "$file" | cut -d ' ' -f 1 > "$file.sha1"
elif [ "$alg" == "sha256" ]; then sha256sum "$file" | cut -d ' ' -f 1 > "$file.sha256"
elif [ "$alg" == "sha512" ]; then sha512sum "$file" | cut -d ' ' -f 1 > "$file.sha512"
fi
done
done
- name: Create Sonatype Bundle
run: |
cd bundle
zip -r ../sonatype-bundle.zip .
- name: Upload Bundle to GitHub
uses: actions/upload-artifact@v7
with:
name: sonatype-bundle
path: sonatype-bundle.zip
compression-level: 0
- name: Publish to Sonatype Central
env:
NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }}
NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
run: |
set -e
# PR-triggered runs (via the publish-snapshot label) may only publish snapshots
if [ "${{ github.event_name }}" == "pull_request" ] && ! find bundle -type f | grep -q -- '-SNAPSHOT'; then
echo "Refusing to publish a non-snapshot version from a pull request"
exit 1
fi
# If any file path contains -SNAPSHOT treat as a snapshot and upload via the
# timestamped unique-snapshot protocol. Plain PUTs of non-unique snapshot
# filenames only register on a version's first publish — central keeps serving
# the first build forever after that (see scripts/upload-snapshots.py).
if find bundle -type f | grep -q -- '-SNAPSHOT'; then
echo "Snapshot artifacts detected — uploading timestamped snapshot builds to maven-snapshots"
python3 scripts/upload-snapshots.py --bundle bundle \
--repo-url https://central.sonatype.com/repository/maven-snapshots
else
mv sonatype-bundle.zip ${{ github.event.repository.name }}.zip
SONATYPE_TOKEN=$(printf "%s:%s" "$NEXUS_USERNAME" "$NEXUS_PASSWORD" | base64 | tr -d '\n')
# publishingType=AUTOMATIC publishes to Maven Central as soon as validation
# passes, with no manual step in the portal. Central is append-only — once a
# version publishes it can never be replaced or withdrawn.
DEPLOYMENT_ID=$(curl --fail-with-body --silent --show-error --request POST \
--header "Authorization: Bearer ${SONATYPE_TOKEN}" \
--form bundle=@${{ github.event.repository.name }}.zip \
'https://central.sonatype.com/api/v1/publisher/upload?publishingType=AUTOMATIC')
echo "Uploaded deployment ${DEPLOYMENT_ID}"
# Upload returns as soon as the bundle is accepted; validation and publication
# run asynchronously. Poll to completion, so a rejected deployment fails the
# release instead of passing silently with nothing on central.
PUBLISHED=false
for _ in $(seq 1 60); do
STATE=$(curl --fail-with-body --silent --show-error --request POST \
--header "Authorization: Bearer ${SONATYPE_TOKEN}" \
"https://central.sonatype.com/api/v1/publisher/status?id=${DEPLOYMENT_ID}" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["deploymentState"])')
echo " deploymentState=${STATE}"
if [ "$STATE" == "PUBLISHED" ]; then PUBLISHED=true; break; fi
if [ "$STATE" == "FAILED" ]; then
echo "::error::Deployment ${DEPLOYMENT_ID} failed validation"
curl --silent --request POST \
--header "Authorization: Bearer ${SONATYPE_TOKEN}" \
"https://central.sonatype.com/api/v1/publisher/status?id=${DEPLOYMENT_ID}"
exit 1
fi
sleep 30
done
if [ "$PUBLISHED" != true ]; then
echo "::error::Deployment ${DEPLOYMENT_ID} never reached PUBLISHED (last state: ${STATE})"
exit 1
fi
echo "Published ${DEPLOYMENT_ID} to Maven Central"
fi
- name: Generate API Docs
if: github.event_name != 'pull_request'
run: ./gradlew dokkaGenerateHtml
- name: Prepare Site Docs
if: github.event_name != 'pull_request'
run: ./gradlew configSite
- name: Deploy root docs to website
if: github.ref_type == 'tag'
uses: JamesIves/github-pages-deploy-action@132898c54c57c7cc6b80eb3a89968de8fc283505
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: site
FOLDER: build/site
TARGET_FOLDER: /
CLEAN: false
- name: Deploy dokka to website
if: github.event_name != 'pull_request'
uses: JamesIves/github-pages-deploy-action@132898c54c57c7cc6b80eb3a89968de8fc283505
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: site
FOLDER: build/dokka/html
TARGET_FOLDER: /docs/${{ github.ref_name }}/
CLEAN: true