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..e0de163
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,78 @@
+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: ""
+ 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: ""
+ 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: ""
+
+ 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"