diff --git a/.github/scripts/release/version-bump-test.sh b/.github/scripts/release/version-bump-test.sh index 424d084..9a696c5 100644 --- a/.github/scripts/release/version-bump-test.sh +++ b/.github/scripts/release/version-bump-test.sh @@ -118,3 +118,42 @@ if [ "$actual_next_snapshot" != "0.9.0-SNAPSHOT" ]; then exit 1 fi printf 'PASS next snapshot: %s\n' "$actual_next_snapshot" + +NOTES_FIXTURE="$TEST_ROOT/release-notes" +mkdir -p "$NOTES_FIXTURE" +cat > "$NOTES_FIXTURE/CHANGELOG.md" <<'CHANGELOG' +# Changelog + +Preamble. + +## [0.10.0] - 2026-10-19 + +### 📦 Other +- docs: keep version text true (#109) (cb71d83) + +## [0.9.0] - 2026-10-18 + +### 🐛 Fixes +- fix: older entry (529dedd) +CHANGELOG +expected_notes="### 📦 Other +- docs: keep version text true (#109) (cb71d83)" +for version in 0.10.0 0.9.0; do + actual_notes="$( + cd "$NOTES_FIXTURE" + "$REPOSITORY_ROOT/.github/scripts/release/version-bump.sh" release-notes "$version" + )" + if [ "$version" = "0.9.0" ]; then + expected_notes="### 🐛 Fixes +- fix: older entry (529dedd)" + fi + if [ "$actual_notes" != "$expected_notes" ]; then + printf 'FAIL release notes %s: got\n%s\n' "$version" "$actual_notes" >&2 + exit 1 + fi +done +if (cd "$NOTES_FIXTURE" && "$REPOSITORY_ROOT/.github/scripts/release/version-bump.sh" release-notes 0.1.0 >/dev/null 2>&1); then + printf 'FAIL release notes: missing version must fail\n' >&2 + exit 1 +fi +printf 'PASS release notes\n' diff --git a/.github/scripts/release/version-bump.sh b/.github/scripts/release/version-bump.sh index a2c043e..feb0f02 100755 --- a/.github/scripts/release/version-bump.sh +++ b/.github/scripts/release/version-bump.sh @@ -13,6 +13,7 @@ # version-bump.sh bump-release # -SNAPSHOT -> root candidate release in build.gradle.kts # version-bump.sh bump-next-snap # release -> next -SNAPSHOT in build.gradle.kts # version-bump.sh changelog # generate/update CHANGELOG.md for release +# version-bump.sh release-notes VER # print the CHANGELOG.md section body for VER set -euo pipefail BUILD_FILE="build.gradle.kts" @@ -136,6 +137,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/). echo "Generated CHANGELOG.md for version ${version}" } +# Print one CHANGELOG.md section without its heading or surrounding blank lines. +release_notes() { + local version="${1:?release-notes needs a version}" + local notes + notes="$(awk -v target="## [${version}]" ' + index($0, target) == 1 { found = 1; next } + found && /^## / { exit } + found { print } + ' CHANGELOG.md | sed -e '/./,$!d' | sed -e ':a' -e '/^\n*$/{$d;N;ba' -e '}')" + if [ -z "$notes" ]; then + echo "CHANGELOG.md has no section for ${version}." >&2 + return 1 + fi + printf '%s\n' "$notes" +} + bump_release() { local release release="$(extract_release)" @@ -159,8 +176,9 @@ case "${1:-}" in bump-release) bump_release ;; bump-next-snap) bump_next_snap ;; changelog) generate_changelog ;; + release-notes) release_notes "${2:-}" ;; *) - echo "Usage: $0 {extract-version|extract-release|extract-next-snap|bump-release|bump-next-snap|changelog}" >&2 + echo "Usage: $0 {extract-version|extract-release|extract-next-snap|bump-release|bump-next-snap|changelog|release-notes VERSION}" >&2 exit 1 ;; esac diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 9a67885..688d827 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -245,6 +245,31 @@ jobs: :kompact-gradle-plugin:centralPortalPublish \ --no-daemon --console=plain + github-release: + needs: [prepare, deploy] + if: needs.deploy.result == 'success' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + ref: "v${{ needs.prepare.outputs.release_version }}" + persist-credentials: false + + - name: Create or update GitHub release from the changelog + env: + GH_TOKEN: ${{ github.token }} + RELEASE_VERSION: ${{ needs.prepare.outputs.release_version }} + run: | + TAG="v${RELEASE_VERSION}" + .github/scripts/release/version-bump.sh release-notes "$RELEASE_VERSION" > "$RUNNER_TEMP/notes.md" + if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + gh release edit "$TAG" --repo "$GITHUB_REPOSITORY" --title "$TAG" --notes-file "$RUNNER_TEMP/notes.md" + echo "Updated GitHub release ${TAG}" + else + gh release create "$TAG" --repo "$GITHUB_REPOSITORY" --verify-tag --title "$TAG" --notes-file "$RUNNER_TEMP/notes.md" + echo "Created GitHub release ${TAG}" + fi + advance-snapshot: needs: [prepare, deploy] if: needs.deploy.result == 'success' diff --git a/docs/adr/0004-release-pr-automation.md b/docs/adr/0004-release-pr-automation.md index 53c3f8b..3ada533 100644 --- a/docs/adr/0004-release-pr-automation.md +++ b/docs/adr/0004-release-pr-automation.md @@ -62,7 +62,9 @@ managed topic branches and release tags. It never pushes a commit to protected - The workflow builds, signs, and deploys all published artifacts to Central Portal. Publication stays behind the `release` GitHub Environment, which a maintainer approves by hand. -- After a successful publication, the workflow creates or updates a separate +- After a successful publication, the workflow creates or updates the GitHub + release for the tag. Its notes are the version's `CHANGELOG.md` section. +- After a successful publication, the workflow also creates or updates a separate PR from a managed branch. That PR moves the root version and the development documentation to the next `-SNAPSHOT`. The workflow does not push this commit directly to `main`. @@ -72,7 +74,8 @@ managed topic branches and release tags. It never pushes a commit to protected - `version-bump.sh` reads the canonical root version. It removes `-SNAPSHOT` for the release candidate, advances the minor version for the next development cycle, and generates changelog sections from the commits since - the latest tag. + the latest tag. Its `release-notes` command prints one version's section for + the GitHub release. - `sync-version-references.py` updates the maintained consumer docs for either a stable release candidate or a development snapshot. - The docs-version checker compares development references with the root @@ -114,8 +117,8 @@ managed topic branches and release tags. It never pushes a commit to protected the tag and the Central Portal state before you retry. Reusing the tag is safe only when its commit matches the reviewed merge. - If the release fails after publication but before the next-SNAPSHOT PR - opens, the published release stays valid. Rerunning the follow-up job must - create or update the PR without publishing again. + opens, the published release stays valid. Rerunning the follow-up jobs must + create or update the GitHub release and the PR without publishing again. - Behavior tests and allocation evidence on physical iOS Arm64 and Android Native Arm64 devices stay independent release gates, documented in [`docs/ci.md`](../ci.md). Simulator results do not count as proof on a diff --git a/docs/ci.md b/docs/ci.md index 74440aa..676918b 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -445,7 +445,7 @@ release version. The `Release Publish` workflow ([`release-publish.yml`](../.github/workflows/release-publish.yml)) runs when -a PR with the `release` label merges into `main`. It has three jobs: +a PR with the `release` label merges into `main`. It has four jobs: 1. `prepare` checks that the workflow runs on the merge commit and that the merged version is stable. It reruns the release script tests and the @@ -457,7 +457,11 @@ a PR with the `release` label merges into `main`. It has three jobs: builds and signs the bundles from the tag with the real signing key, uploads them to the Central Portal, waits for validation, and publishes them to Maven Central. -3. `advance-snapshot` runs only after a successful publication. It opens or +3. `github-release` runs only after a successful publication. It creates the + GitHub release for the tag, or updates it on a rerun. The release notes + are the `CHANGELOG.md` section for the version, printed by + `version-bump.sh release-notes `. +4. `advance-snapshot` runs only after a successful publication. It opens or updates a PR on `release/next-snapshot-` that sets the next `-SNAPSHOT` version and updates the development-version references.