From 7615cede6d4c9e673db424f9dcc420719e07ed90 Mon Sep 17 00:00:00 2001 From: Joe Wicentowski Date: Mon, 15 Jun 2026 22:37:30 -0400 Subject: [PATCH 1/2] build(api): guard that api.json changes require an info.version bump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per review on #63 (line-o, duncdrum): the OpenAPI contract version (modules/api.json info.version) should track the API contract, not the package version — contract changes are rare while the package changes often, so tying them in lockstep would churn the contract version on every Java/internal fix. Replace the lockstep templating approach with: - info.version pinned to a fixed 0.9.0 baseline (no longer ${project.version}); - a CI guard (.github/workflows/api-version-guard.yml) that fails a PR if modules/api.json changed without info.version changing. The guard enforces "contract changed => version bumped"; it does not police the bump size. Automating an independent API semver track is a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/api-version-guard.yml | 39 +++++++++++++++++++++++++ modules/api.json | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/api-version-guard.yml diff --git a/.github/workflows/api-version-guard.yml b/.github/workflows/api-version-guard.yml new file mode 100644 index 0000000..57d0bfc --- /dev/null +++ b/.github/workflows/api-version-guard.yml @@ -0,0 +1,39 @@ +name: API version guard + +# The OpenAPI contract version (modules/api.json -> info.version) is tracked +# independently of the package version (pom.xml): the contract changes far less +# often than the package. This guard enforces the one invariant that keeps the +# contract version honest — if api.json changes in a PR, info.version must change +# too. It does not police the size of the bump (patch/minor/major); that is left +# to the author's semver judgment (and to a future automation, see the follow-up). + +on: + pull_request: + branches: [develop] + +permissions: + contents: read + +jobs: + api-version-bump: + name: Require an info.version bump when api.json changes + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Check modules/api.json info.version + run: | + base='${{ github.event.pull_request.base.sha }}' + if git diff --quiet "$base" HEAD -- modules/api.json; then + echo "modules/api.json unchanged in this PR — no API version bump required." + exit 0 + fi + old=$(git show "$base:modules/api.json" | jq -r '.info.version') + new=$(jq -r '.info.version' modules/api.json) + echo "modules/api.json changed. info.version: '$old' -> '$new'" + if [ "$old" = "$new" ]; then + echo "::error file=modules/api.json::modules/api.json changed but info.version did not (still '$new'). The OpenAPI contract version is tracked independently of the package version — bump info.version whenever the API contract changes." + exit 1 + fi + echo "info.version was bumped — OK" diff --git a/modules/api.json b/modules/api.json index f9c25cd..55b4182 100644 --- a/modules/api.json +++ b/modules/api.json @@ -2,7 +2,7 @@ "openapi": "3.0.3", "info": { "title": "eXist-db Platform API", - "version": "0.9.0-SNAPSHOT", + "version": "0.9.0", "description": "Unified REST API for eXist-db: query execution, language services, database management, user management, package management, search, and cross-app linking.", "license": { "name": "LGPL-2.1", From 188a015f2dceb595739d355df87a7ee3f83f0b29 Mon Sep 17 00:00:00 2001 From: Joe Wicentowski Date: Tue, 16 Jun 2026 07:40:26 -0400 Subject: [PATCH 2/2] build(api): scope the version guard to api.json changes (PRs + pushes) Per review (duncdrum): add a paths filter so the workflow only fires when modules/api.json is touched, and a push trigger for pre-PR feedback. The version-bump check resolves its base per event (PR base vs. merge-base with develop). Kept advisory, not required (a required paths-filtered check would block PRs that don't touch api.json). Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/api-version-guard.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/api-version-guard.yml b/.github/workflows/api-version-guard.yml index 57d0bfc..f10ef97 100644 --- a/.github/workflows/api-version-guard.yml +++ b/.github/workflows/api-version-guard.yml @@ -6,10 +6,18 @@ name: API version guard # contract version honest — if api.json changes in a PR, info.version must change # too. It does not police the size of the bump (patch/minor/major); that is left # to the author's semver judgment (and to a future automation, see the follow-up). +# +# Triggered only when modules/api.json is touched (PRs and branch pushes — the +# latter gives contributors feedback before they open a PR). NOTE: keep this an +# advisory check, not a required one — a paths-filtered job that is required would +# block PRs that don't touch api.json (the check would never report). on: pull_request: branches: [develop] + paths: ['modules/api.json'] + push: + paths: ['modules/api.json'] permissions: contents: read @@ -24,9 +32,14 @@ jobs: fetch-depth: 0 - name: Check modules/api.json info.version run: | - base='${{ github.event.pull_request.base.sha }}' + if [ "${{ github.event_name }}" = "pull_request" ]; then + base='${{ github.event.pull_request.base.sha }}' + else + git fetch --no-tags --quiet origin develop + base="$(git merge-base FETCH_HEAD HEAD)" + fi if git diff --quiet "$base" HEAD -- modules/api.json; then - echo "modules/api.json unchanged in this PR — no API version bump required." + echo "modules/api.json unchanged relative to base — no API version bump required." exit 0 fi old=$(git show "$base:modules/api.json" | jq -r '.info.version')