From 2a92d25ccb1318a8c1d7ddc5569cdcaef851967a Mon Sep 17 00:00:00 2001 From: DemchaAV Date: Fri, 29 May 2026 09:24:19 +0100 Subject: [PATCH] ci(release): add tag-triggered release workflow release.yml fires on vX.Y.Z tag pushes: it re-runs `mvnw clean verify -pl .` against the tagged commit as a green-build gate, then creates (or updates) a GitHub Release whose body is that version's CHANGELOG section. Pre-release tags (e.g. v1.7.0-rc.1) ship as GitHub pre-releases. Removes the manual "create a GitHub Release" step from the release checklist. The version-consistency guard passes by construction because cut-release.ps1 bumps every version site in the release commit (#71). --- .github/workflows/release.yml | 87 +++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..6e339af0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,87 @@ +name: Release + +# Cut a GitHub Release whenever a vX.Y.Z tag is pushed. The tag is the +# trigger and the contract: the workflow re-runs the verify gate against +# the exact tagged commit, then publishes a Release whose body is that +# version's CHANGELOG section. A tag only ships if the build is green. +# +# Tags are pushed by scripts/cut-release.ps1 after it bumps every version +# site (library pom, aggregator, examples/benchmarks parents, README +# install snippets) in the release commit, so the version guard in the +# verify step below passes by construction. + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + release: + name: Verify and publish GitHub Release + runs-on: ubuntu-latest + env: + JAVA_TOOL_OPTIONS: -Djava.awt.headless=true + + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Set up Temurin JDK 17 + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: '17' + cache: maven + + - name: Verify (gate the release on a green build) + run: ./mvnw -B -ntp clean verify -pl . + + - name: Extract CHANGELOG section for the tag + id: notes + run: | + TAG="${GITHUB_REF_NAME}" + NOTES_FILE="${RUNNER_TEMP}/release-notes.md" + # Print the "## — ..." heading and every line up to the + # next "## v" heading. index()==1 is a literal prefix match, so + # the dots in the version are not treated as regex wildcards and + # the trailing space stops "## v1.6.5 " from matching "v1.6.50". + awk -v hdr="## ${TAG} " ' + index($0, hdr) == 1 { flag = 1; print; next } + /^## v/ && flag { flag = 0 } + flag { print } + ' CHANGELOG.md > "${NOTES_FILE}" + if [ ! -s "${NOTES_FILE}" ]; then + echo "::warning::No CHANGELOG section found for ${TAG}; using a generic note." + printf '%s\n' "Release ${TAG}. See [CHANGELOG.md](CHANGELOG.md) for details." > "${NOTES_FILE}" + fi + echo "notes_file=${NOTES_FILE}" >> "${GITHUB_OUTPUT}" + # Tags carrying a pre-release suffix (e.g. v1.7.0-rc.1) ship as + # GitHub pre-releases so they never become "Latest". + if printf '%s' "${TAG}" | grep -q '-'; then + echo "prerelease=true" >> "${GITHUB_OUTPUT}" + else + echo "prerelease=false" >> "${GITHUB_OUTPUT}" + fi + + - name: Create or update GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="${GITHUB_REF_NAME}" + PRERELEASE_FLAG="" + if [ "${{ steps.notes.outputs.prerelease }}" = "true" ]; then + PRERELEASE_FLAG="--prerelease" + fi + if gh release view "${TAG}" >/dev/null 2>&1; then + echo "Release ${TAG} already exists — updating its notes." + gh release edit "${TAG}" --notes-file "${{ steps.notes.outputs.notes_file }}" + else + gh release create "${TAG}" \ + --title "GraphCompose ${TAG}" \ + --notes-file "${{ steps.notes.outputs.notes_file }}" \ + --verify-tag \ + ${PRERELEASE_FLAG} + fi