From 0d9c313b6b8da9c50bf16318904c5e836e6a2de5 Mon Sep 17 00:00:00 2001 From: Asgeir Frimannsson Date: Tue, 15 Sep 2026 11:24:22 +0200 Subject: [PATCH 1/2] feat: default to kapi 1.2.0 and fetch history for diff-scoped gates kapi-version defaults to 1.2.0 in up.yml and gate.yml. The rc14 default reads no source-only recipe and has no --diff-range. gate.yml takes fetch-depth and passes it to actions/checkout. A diff-scoped check reads the commits it compares, and in the depth-1 checkout it had, kapi check --diff-range fails with "HEAD~1 names no commit" on any kapi version. The comments and README name the current gates and the committed unit state under .kapi/state/. test.yml calls both workflows from the checkout on a source-only fixture with kapi 1.2.0-rc32. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01VRid8i4qnuNfE7Lio73Uu4 --- .github/workflows/gate.yml | 24 +++++++---- .github/workflows/test.yml | 81 ++++++++++++++++++++++++++++++++++++ .github/workflows/up.yml | 4 +- README.md | 21 +++++++--- test/fixture/.gitignore | 1 + test/fixture/content/en.json | 4 ++ test/fixture/kapi.yaml | 11 +++++ 7 files changed, 130 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 test/fixture/.gitignore create mode 100644 test/fixture/content/en.json create mode 100644 test/fixture/kapi.yaml diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml index 1873e0a..0bbb9a0 100644 --- a/.github/workflows/gate.yml +++ b/.github/workflows/gate.yml @@ -1,8 +1,8 @@ name: kapi ship gate -# Reusable quality-gate workflow: fail a pull request when a brand, -# terminology, QA, or coverage gate is unmet (kapi check --ship, exit 3), -# with a sticky report comment on the PR: +# Reusable quality-gate workflow: fail a pull request when a voice, +# terminology, rule-based check or coverage gate is unmet (kapi check --ship, +# exit 3), with a sticky report comment on the PR: # # jobs: # ship-gate: @@ -11,10 +11,10 @@ name: kapi ship gate # contents: read # pull-requests: write # -# Ordinary builds never fail on target-language drift — the gate is the -# explicit, opt-in enforcement point. A `reviewed` threshold reads its -# decisions from the committed state store (.kapi-state.json); commit that -# file in the PR or the runner sees no approvals. +# Ordinary builds never fail on target-language drift; the gate is the +# explicit, opt-in enforcement point. A `reviewed` threshold reads the +# decisions `kapi commit` writes under .kapi/state/, so commit them in the PR +# or the runner sees no approvals. on: workflow_call: @@ -32,13 +32,17 @@ on: type: string default: "bowrain" kapi-version: - description: "kapi CLI version. `check --ship` ships in 1.2.0; until 1.2.0 is stable this pins the release candidate" + description: "kapi CLI version for setup-kapi: a release such as 1.2.0, or latest for the newest stable release" type: string - default: "1.2.0-rc14" + default: "1.2.0" pr-comment: description: "Sticky report comment on pull-request events" type: boolean default: true + fetch-depth: + description: "Commits to fetch, 0 for all. A diff-scoped check (--diff-range, --diff-against) reads the commits it compares, so set 0 for one" + type: number + default: 1 server: description: "Bowrain server URL (exported as BOWRAIN_SERVER_URL)" type: string @@ -63,6 +67,8 @@ jobs: gate: ${{ steps.kapi.outputs.gate }} steps: - uses: actions/checkout@v6 + with: + fetch-depth: ${{ inputs.fetch-depth }} - uses: neokapi/setup-kapi@v1 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..40ad8ff --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,81 @@ +name: Test + +# Calls both reusable workflows from this checkout against the source-only +# fixture in test/fixture, which needs no provider key and no server. Nothing +# is delivered: the fixture converges with no changes. The CLI is pinned to a +# release that reads the fixture's recipe and has --diff-range; move it to the +# stable release once one exists. + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +jobs: + fixture-range: + name: Find the commit that last changed the fixture content + runs-on: ubuntu-latest + outputs: + range: ${{ steps.range.outputs.range }} + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - id: range + run: | + set -euo pipefail + sha="$(git log -1 --format=%H -- test/fixture/content)" + echo "range=${sha}~1..${sha}" >> "$GITHUB_OUTPUT" + + gate-ship: + name: "gate.yml: check --ship" + uses: ./.github/workflows/gate.yml + with: + project: test/fixture/kapi.yaml + plugins: "" + kapi-version: "1.2.0-rc32" + pr-comment: false + + gate-diff-range: + name: "gate.yml: a diff-scoped check" + needs: fixture-range + uses: ./.github/workflows/gate.yml + with: + project: test/fixture/kapi.yaml + args: "--diff-range ${{ needs.fixture-range.outputs.range }}" + fetch-depth: 0 + plugins: "" + kapi-version: "1.2.0-rc32" + pr-comment: false + + up-source-only: + name: "up.yml: kapi up on the source-only fixture" + uses: ./.github/workflows/up.yml + with: + project: test/fixture/kapi.yaml + plugins: "" + kapi-version: "1.2.0-rc32" + + verify: + name: Verify the workflow outputs + needs: [gate-ship, gate-diff-range, up-source-only] + runs-on: ubuntu-latest + steps: + - name: Check each output + env: + SHIP_GATE: ${{ needs.gate-ship.outputs.gate }} + DIFF_GATE: ${{ needs.gate-diff-range.outputs.gate }} + OUTCOME: ${{ needs.up-source-only.outputs.outcome }} + HAS_CHANGES: ${{ needs.up-source-only.outputs.has-changes }} + run: | + set -euo pipefail + echo "gate-ship: ${SHIP_GATE}; gate-diff-range: ${DIFF_GATE}; up: ${OUTCOME}, has-changes ${HAS_CHANGES}" + test "${SHIP_GATE}" = pass + test "${DIFF_GATE}" = pass + test "${OUTCOME}" = converged + test "${HAS_CHANGES}" = false diff --git a/.github/workflows/up.yml b/.github/workflows/up.yml index e479526..337ff75 100644 --- a/.github/workflows/up.yml +++ b/.github/workflows/up.yml @@ -37,9 +37,9 @@ on: type: string default: "bowrain" kapi-version: - description: "kapi CLI version. `kapi up` ships in 1.2.0; until 1.2.0 is stable this pins the release candidate" + description: "kapi CLI version for setup-kapi: a release such as 1.2.0, or latest for the newest stable release" type: string - default: "1.2.0-rc14" + default: "1.2.0" server: description: "Bowrain server URL (exported as BOWRAIN_SERVER_URL)" type: string diff --git a/README.md b/README.md index 9d47fd3..d99411b 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,11 @@ flowchart LR U --> PASS CK -->|every gate met| CV["up to date
PR with translations"] CK -->|needs a person| PK["parked
the review queue"] - PK --> RV["review & approve
recorded in .kapi-state.json"] + PK --> RV["review & approve
committed under .kapi/state"] RV -.->|next run sees it| U ``` -Parked work is the review queue, not an error: a person reviews and approves it, the decision is recorded in the committed `.kapi-state.json` state store (or on the connected server), and the `reviewed` coverage the ship gate measures goes up — the next run and the next gate see it. +Parked work is the review queue, not an error: a person reviews and approves it, `kapi commit` records the decision under `.kapi/state/` (or the connected server records it), and the `reviewed` coverage the ship gate measures goes up. The next run and the next gate see it. ## `gate.yml` — fail PRs on unmet content quality gates @@ -61,13 +61,24 @@ jobs: pull-requests: write ``` -Runs `kapi check --ship` — the project's bound quality gates (brand, terminology, QA) plus its ship/source coverage gates. An unmet gate exits `3`, fails the job with a distinct "gate unmet" annotation, and posts one sticky report comment on the PR. Ordinary builds never fail on target-language drift; the gate is the explicit, opt-in enforcement point. +Runs `kapi check --ship`: the project's bound gates (voice, terminology, rule-based checks) plus its ship and source coverage gates. An unmet gate exits `3`, fails the job with a distinct "gate unmet" annotation, and posts one sticky report comment on the PR. Ordinary builds never fail on target-language drift; the gate is the explicit, opt-in enforcement point. -Inputs: `project`, `args` (default `--ship`), `plugins`, `kapi-version`, `pr-comment` (default `true`), `server`, `runs-on`. Output: `gate` (`pass`/`fail`). +Inputs: `project`, `args` (default `--ship`), `plugins`, `kapi-version`, `pr-comment` (default `true`), `fetch-depth` (default `1`), `server`, `runs-on`. Output: `gate` (`pass`/`fail`). + +A diff-scoped check reads the commits it compares, so pass `fetch-depth: 0` with `--diff-range` or `--diff-against`: + +```yaml +jobs: + ship-gate: + uses: neokapi/kapi-workflows/.github/workflows/gate.yml@v1 + with: + args: "--diff-range ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}" + fetch-depth: 0 +``` ## Versions -`@v1` is a floating major tag. `kapi up` and `check --ship` ship in kapi 1.2.0; until 1.2.0 is stable the workflows pin the release candidate CLI (`kapi-version: 1.2.0-rc14`) — override the input to choose your own. +`@v1` is a floating major tag. The workflows install kapi 1.2.0 by default. Set `kapi-version` to another release to pin it, or to `latest` for the newest stable release. ## License diff --git a/test/fixture/.gitignore b/test/fixture/.gitignore new file mode 100644 index 0000000..e361024 --- /dev/null +++ b/test/fixture/.gitignore @@ -0,0 +1 @@ +.kapi/work/ diff --git a/test/fixture/content/en.json b/test/fixture/content/en.json new file mode 100644 index 0000000..58cf524 --- /dev/null +++ b/test/fixture/content/en.json @@ -0,0 +1,4 @@ +{ + "greeting": "Welcome back.", + "farewell": "See you soon." +} diff --git a/test/fixture/kapi.yaml b/test/fixture/kapi.yaml new file mode 100644 index 0000000..2d982d5 --- /dev/null +++ b/test/fixture/kapi.yaml @@ -0,0 +1,11 @@ +version: v1 +name: fixture + +# A source-only project: no target language, so kapi up converges the source +# and kapi check --ship passes with no provider key and no server. +defaults: + source_language: en +collections: + - name: app + content: + - path: "content/en.json" From 3157e829d47d5b61410437cc7544fb80938c1e90 Mon Sep 17 00:00:00 2001 From: Asgeir Frimannsson Date: Sat, 19 Sep 2026 12:12:12 +0200 Subject: [PATCH 2/2] Drop the release-candidate pins from the test workflow The reusable workflows now default to kapi 1.2.0, which is a stable release, so the tests no longer need a pin to reach it. Removing the three overrides means the suite exercises the default every consumer gets. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01CnTBkxVmyc7RCgQMQN7z9s --- .github/workflows/test.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 40ad8ff..e0de163 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,7 +38,6 @@ jobs: with: project: test/fixture/kapi.yaml plugins: "" - kapi-version: "1.2.0-rc32" pr-comment: false gate-diff-range: @@ -50,7 +49,6 @@ jobs: args: "--diff-range ${{ needs.fixture-range.outputs.range }}" fetch-depth: 0 plugins: "" - kapi-version: "1.2.0-rc32" pr-comment: false up-source-only: @@ -59,7 +57,6 @@ jobs: with: project: test/fixture/kapi.yaml plugins: "" - kapi-version: "1.2.0-rc32" verify: name: Verify the workflow outputs