From 2d6aa16f86794aa216942a7b439ee85b857b1bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguye=CC=82=CC=83n=20Tua=CC=82=CC=81n=20Vie=CC=A3=CC=82t?= Date: Thu, 1 Oct 2026 08:42:15 +0700 Subject: [PATCH] build: single Maven bundle script and release workflow using it Per-module publishes left 12 artifacts out of the 2.3.0 Central deployment. --- .github/workflows/release.yml | 59 ++++--------------- CHANGELOG.md | 3 + docs/RELEASING.md | 22 +++++++ scripts/build-maven-bundle.sh | 107 ++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 47 deletions(-) create mode 100644 docs/RELEASING.md create mode 100755 scripts/build-maven-bundle.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1b10d2..ed0a429 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -156,57 +156,22 @@ jobs: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - name: Publish krelay - run: ./gradlew :krelay:publishAllPublicationsToOSSRHRepository --no-daemon + # One signed bundle for ALL modules -> one Central Portal deployment, validated and published + # together. (Per-module Gradle publishes left some artifacts out of the 2.3.0 deployment.) + - name: Build bundle and upload to Central Portal + run: scripts/build-maven-bundle.sh --upload automatic env: - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + CENTRAL_USERNAME: ${{ secrets.OSSRH_USERNAME }} + CENTRAL_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - name: Publish krelay-flow - run: ./gradlew :krelay-flow:publishAllPublicationsToOSSRHRepository --no-daemon - env: - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - - name: Publish krelay-compose - run: ./gradlew :krelay-compose:publishAllPublicationsToOSSRHRepository --no-daemon - env: - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - - name: Publish krelay-testing - run: ./gradlew :krelay-testing:publishAllPublicationsToOSSRHRepository --no-daemon - env: - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - - name: Publish krelay-bom - run: ./gradlew :krelay-bom:publishAllPublicationsToOSSRHRepository --no-daemon - env: - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - # Legacy OSSRH (s01) was retired; uploads now land in the Central Portal - # staging API and must be handed over to the Portal to be published. - - name: Hand staged repository over to Central Portal - run: | - AUTH=$(printf '%s:%s' "$OSSRH_USERNAME" "$OSSRH_PASSWORD" | base64) - curl --fail-with-body -X POST \ - -H "Authorization: Bearer $AUTH" \ - "https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/dev.brewkits?publishing_type=automatic" - env: - OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + - name: Upload bundle as workflow artifact + if: always() + uses: actions/upload-artifact@v7 + with: + name: maven-bundle + path: krelay-v*-maven-bundle.zip # ────────────────────────────────────────────────────────────────────────── github-release: diff --git a/CHANGELOG.md b/CHANGELOG.md index e6b0132..5eaaf8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- **Release publishing**: `scripts/build-maven-bundle.sh` builds every module into a single signed Maven bundle (one Central Portal deployment) and `release.yml` uses it. Per-module Gradle publishes left 12 artifacts (`krelay-testing*`, `krelay-bom`, `krelay-flow-android/iosarm64/iossimulatorarm64`, `krelay-compose-wasm-js`) out of the 2.3.0 deployment. See `docs/RELEASING.md`. + --- ## [2.3.0] - 2026-10-01 diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000..ce8f234 --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,22 @@ +# Releasing KRelay + +1. Bump `version` in all five modules (`krelay`, `krelay-flow`, `krelay-compose`, `krelay-testing`, `krelay-bom`), update `CHANGELOG.md`, merge to `main`. +2. Tag: `git tag -a vX.Y.Z -m "KRelay X.Y.Z" && git push origin vX.Y.Z`. The **Release** workflow tests, builds and publishes. + +## Maven bundle script + +`scripts/build-maven-bundle.sh` signs and gathers **all** modules into one zip, so Maven Central gets +a single deployment containing every artifact. + +```bash +scripts/build-maven-bundle.sh # build + verify + zip -> krelay-vX.Y.Z-maven-bundle.zip +scripts/build-maven-bundle.sh --skip-build # reuse build/maven-central-staging +scripts/build-maven-bundle.sh --only-missing # zip only what is not on Maven Central yet +CENTRAL_USERNAME=... CENTRAL_PASSWORD=... \ + scripts/build-maven-bundle.sh --upload manual # upload via the Central Portal API (manual = you click Publish) +``` + +- Signing key: `signing.key` / `signing.password` in `~/.gradle/gradle.properties`, or `SIGNING_KEY` / `SIGNING_PASSWORD`. +- `CENTRAL_*` are a Central Portal **user token**. In CI they come from the `OSSRH_USERNAME` / `OSSRH_PASSWORD` secrets of the `maven-central` environment. +- The script refuses to run if module versions differ and fails if any artifact lacks a signature or checksum. +- After publishing, confirm all artifacts resolve: `krelay`, `krelay-flow`, `krelay-compose`, `krelay-testing`, `krelay-bom` and their `-jvm`, `-android`, `-ios*`, `-wasm-js` variants. diff --git a/scripts/build-maven-bundle.sh b/scripts/build-maven-bundle.sh new file mode 100755 index 0000000..e1d275e --- /dev/null +++ b/scripts/build-maven-bundle.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +# +# Builds ONE Maven Central bundle (zip) containing every KRelay module, signed and checksummed. +# +# scripts/build-maven-bundle.sh # build + verify + zip everything +# scripts/build-maven-bundle.sh --skip-build # reuse build/maven-central-staging +# scripts/build-maven-bundle.sh --only-missing # zip only artifacts not yet on Maven Central +# scripts/build-maven-bundle.sh --upload manual|automatic +# # also upload via the Central Portal API +# # (needs CENTRAL_USERNAME / CENTRAL_PASSWORD = Portal user token) +# +# Signing: ~/.gradle/gradle.properties (signing.key / signing.password) or SIGNING_KEY / SIGNING_PASSWORD. +# Output: krelay-v-maven-bundle.zip in the repository root (git-ignored). +# +# Why one bundle: publishing modules in separate Gradle runs left some artifacts out of the +# Central deployment for 2.3.0. A single zip is one deployment, validated and published together. + +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" + +SKIP_BUILD=0 +ONLY_MISSING=0 +UPLOAD="" +while [ $# -gt 0 ]; do + case "$1" in + --skip-build) SKIP_BUILD=1 ;; + --only-missing) ONLY_MISSING=1 ;; + --upload) UPLOAD="${2:?--upload needs 'manual' or 'automatic'}"; shift ;; + -h|--help) sed -n '2,17p' "$0"; exit 0 ;; + *) echo "Unknown option: $1" >&2; exit 2 ;; + esac + shift +done + +GROUP_PATH="dev/brewkits" +MODULES=(krelay krelay-flow krelay-compose krelay-testing krelay-bom) +STAGING="$ROOT/build/maven-central-staging" + +VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' krelay/build.gradle.kts | head -1)" +[ -n "$VERSION" ] || { echo "Cannot read version from krelay/build.gradle.kts" >&2; exit 1; } +for m in "${MODULES[@]}"; do + v="$(sed -n 's/^version = "\(.*\)"/\1/p' "$m/build.gradle.kts" | head -1)" + [ "$v" = "$VERSION" ] || { echo "Version mismatch: $m is $v, krelay is $VERSION" >&2; exit 1; } +done +echo "==> KRelay $VERSION" + +# 1. Build + sign every module into the local staging repository +if [ "$SKIP_BUILD" -eq 0 ]; then + rm -rf "$STAGING" + TASKS=() + for m in "${MODULES[@]}"; do TASKS+=(":$m:publishAllPublicationsToMavenCentralLocalRepository"); done + ./gradlew "${TASKS[@]}" --no-daemon -q +fi +[ -d "$STAGING/$GROUP_PATH" ] || { echo "No staged artifacts in $STAGING" >&2; exit 1; } + +# 2. Verify: every artifact file is signed and has checksums; filter by version +bad=0 +while IFS= read -r f; do + [ -f "$f.asc" ] && [ -f "$f.md5" ] && [ -f "$f.sha1" ] || { echo "Missing signature/checksum: $f" >&2; bad=1; } +done < <(find "$STAGING/$GROUP_PATH" -type f ! -name '*.asc' ! -name '*.md5' ! -name '*.sha*' ! -name 'maven-metadata*') +[ "$bad" -eq 0 ] || exit 1 + +# 3. Select artifacts for the bundle +WORK="$ROOT/build/maven-bundle-work" +rm -rf "$WORK"; mkdir -p "$WORK/$GROUP_PATH" +count=0 +for dir in "$STAGING/$GROUP_PATH"/*/; do + artifact="$(basename "$dir")" + [ -d "$dir$VERSION" ] || continue + if [ "$ONLY_MISSING" -eq 1 ]; then + code="$(curl -s -o /dev/null -w '%{http_code}' "https://repo1.maven.org/maven2/$GROUP_PATH/$artifact/$VERSION/$artifact-$VERSION.pom")" + if [ "$code" = "200" ]; then echo " skip (already on Central): $artifact"; continue; fi + fi + mkdir -p "$WORK/$GROUP_PATH/$artifact" + cp -R "$dir$VERSION" "$WORK/$GROUP_PATH/$artifact/" + count=$((count + 1)) +done +[ "$count" -gt 0 ] || { echo "Nothing to bundle." >&2; exit 1; } + +# 4. Zip with the Maven layout at the archive root +SUFFIX=""; [ "$ONLY_MISSING" -eq 1 ] && SUFFIX="-missing" +ZIP="$ROOT/krelay-v$VERSION$SUFFIX-maven-bundle.zip" +rm -f "$ZIP" +( cd "$WORK" && zip -qr "$ZIP" dev ) +rm -rf "$WORK" +echo "==> $count artifact(s) -> $ZIP ($(du -h "$ZIP" | cut -f1))" + +# 5. Optional upload to the Central Portal (one deployment) +if [ -n "$UPLOAD" ]; then + case "$UPLOAD" in + manual) TYPE=USER_MANAGED ;; + automatic) TYPE=AUTOMATIC ;; + *) echo "--upload must be 'manual' or 'automatic'" >&2; exit 2 ;; + esac + : "${CENTRAL_USERNAME:?set CENTRAL_USERNAME (Portal user token name)}" + : "${CENTRAL_PASSWORD:?set CENTRAL_PASSWORD (Portal user token password)}" + TOKEN="$(printf '%s:%s' "$CENTRAL_USERNAME" "$CENTRAL_PASSWORD" | base64)" + NAME="$(basename "$ZIP" .zip)" + ID="$(curl -sS --fail-with-body -X POST \ + -H "Authorization: Bearer $TOKEN" \ + -F "bundle=@$ZIP" \ + "https://central.sonatype.com/api/v1/publisher/upload?name=$NAME&publishingType=$TYPE")" + echo "==> Uploaded. Deployment id: $ID" + echo " Status: curl -X POST -H 'Authorization: Bearer ' 'https://central.sonatype.com/api/v1/publisher/status?id=$ID'" +fi