Skip to content

Release v2.4.0

Release v2.4.0 #21

Workflow file for this run

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
# One GitHub Release per tag. A re-pushed tag queues behind the in-flight run
# for that same tag rather than racing it; cancel-in-progress is false so a
# release is never aborted mid-creation. Distinct tags carry distinct refs and
# still proceed in parallel.
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
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@v7
- name: Set up Temurin JDK 17
uses: actions/setup-java@v6
with:
distribution: temurin
java-version: '17'
cache: maven
- name: Verify (gate the release on a green build)
run: ./mvnw -B -ntp clean verify
# The pack describes the API of the commit being tagged, and `verify`
# above has already produced the classes it reads. --check first, so a tag
# cannot ship a pack that disagrees with the code it claims to describe;
# the write that follows is then a no-op for tracked files and exists only
# to produce target/knowledge/provenance.json, which the bundle carries.
- name: Set up Node for the knowledge bundle
uses: actions/setup-node@v7
with:
node-version: '20'
- name: Build the knowledge bundle
run: |
node knowledge/tools/api-surface/extract-api.mjs --from-reactor --check
node knowledge/tools/api-surface/extract-api.mjs --from-reactor
# The bundle carries claims/ and routing/ too, so a correct API surface
# is not enough to publish it: a hand-authored route or a stale claims
# index would ship as a release asset unchallenged. The tag re-checks
# them against the exact commit being tagged rather than trusting that
# a PR run covered it. (check-routes has no --check: routes have no
# generated counterpart, so validating them is the check.)
node knowledge/tools/claims/check-claims.mjs --check
node knowledge/tools/routing/check-routes.mjs
# And the tool fixtures, for the same reason. build-bundle --verify
# asserts exit codes only, which cannot see whether the version
# comparison inside the shipped bin/query.mjs is right — and that copy
# exists precisely because the bundle cannot import the shared module.
# Without this, the one thing pinning it never runs at a tag.
for suite in $(find knowledge/tools -type f -name '*.test.mjs' | sort); do
echo "--- $suite"
node "$suite"
done
node knowledge/tools/bundle/build-bundle.mjs --verify
- name: Extract CHANGELOG section for the tag
id: notes
run: |
TAG="${GITHUB_REF_NAME}"
# A pre-release (vX.Y.Z-rc.N / -alpha / -beta) has no CHANGELOG section of its
# own — it is cut against the upcoming FINAL version's notes. Strip the
# pre-release suffix so the lookup uses the final section (## vX.Y.Z, whether
# still "— Planned" or already dated). A final tag has no suffix, so NOTES_TAG
# == TAG.
NOTES_TAG="${TAG%%-*}"
NOTES_FILE="${RUNNER_TEMP}/release-notes.md"
# Print the "## <NOTES_TAG> — ..." 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="## ${NOTES_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 ${NOTES_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
# The archive and its checksum go up as two assets. The checksum sits
# BESIDE the archive, never inside it: a hash stored in the file it
# describes is rewritten by whatever rewrote the file.
- name: Attach the knowledge bundle to the release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "${GITHUB_REF_NAME}" target/knowledge/graph-compose-knowledge-*.zip target/knowledge/graph-compose-knowledge-*.zip.sha256 --clobber