Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions .github/workflows/gate.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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:
Expand Down
78 changes: 78 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions .github/workflows/up.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ flowchart LR
U --> PASS
CK -->|every gate met| CV["up to date<br/>PR with translations"]
CK -->|needs a person| PK["parked<br/>the review queue"]
PK --> RV["review & approve<br/>recorded in .kapi-state.json"]
PK --> RV["review & approve<br/>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

Expand All @@ -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

Expand Down
1 change: 1 addition & 0 deletions test/fixture/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.kapi/work/
4 changes: 4 additions & 0 deletions test/fixture/content/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"greeting": "Welcome back.",
"farewell": "See you soon."
}
11 changes: 11 additions & 0 deletions test/fixture/kapi.yaml
Original file line number Diff line number Diff line change
@@ -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"
Loading