Skip to main content

Use Case: kapi in GitHub Actions

A GitHub Actions workflow can keep every language caught up on the server, commit the results back, and block a pull request that misses the ship gate. The workflows below run kapi with the bowrain plugin for drafting, quality checks and server sync.

Overview​

The setup-kapi GitHub Action installs kapi on any runner, with the bowrain plugin included by default. It handles platform detection, checksum verification, binary caching and optional server authentication.

For the other CI and delivery surfaces (GitLab CI, container runners, and the no-pipeline Bowrain GitHub App), start at The loop in CI.

Setup​

Add neokapi/setup-kapi@v1 to your workflow:

steps:
- uses: actions/checkout@v7

- uses: neokapi/setup-kapi@v1
with:
version: "1.2.0"
plugins: bowrain@1.2.0

The action downloads the correct binary for the runner platform (Linux, macOS, or Windows), verifies its SHA-256 checksum, and adds it to PATH. The built-in workflow token covers public release downloads, so no token input is required. On subsequent runs, the binary is restored from cache.

Action Inputs​

InputDescriptionDefault
versionCLI version to install, or latestlatest
pluginsComma or newline-separated plugin refs to install, as the registry names them ('' to install nothing)bowrain
auth-tokenBowrain server JWT (exported as BOWRAIN_AUTH_TOKEN)""
serverBowrain server URL (exported as BOWRAIN_SERVER_URL); self-hosted only, the hosted service is the default""

Action Outputs​

OutputDescription
versionInstalled version (for example 1.1.0)
cache-hitWhether the plugin cache was hit

The simplest CI pattern uses two actions together:

  • neokapi/setup-kapi: installs kapi (the bowrain plugin is included by default)
  • neokapi/kapi-action: runs a kapi command (here, kapi up) and reports what changed, for a step of your own to deliver
name: Catch up

on:
workflow_dispatch:
push:
branches: [main]
paths:
- "src/locales/en/**"

permissions:
contents: write

jobs:
up:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: neokapi/setup-kapi@v1
with:
version: "1.2.0"
plugins: bowrain@1.2.0
auth-token: ${{ secrets.BOWRAIN_AUTH_TOKEN }}
server: https://dev.bowrain.cloud

- uses: neokapi/kapi-action@v1
id: up
with:
command: up

- name: Commit the results
if: steps.up.outputs.has-changes == 'true'
env:
OUTCOME: ${{ steps.up.outputs.outcome }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "chore: update translations via kapi ($OUTCOME)"
git push

With command: up (the default), the action runs kapi up (the kapi loop on the server: push → catch up → pull) and reports what the run left in the working tree. The action commits nothing; the last step above commits and pushes.

  • A run that caught up (outcome converged: every gated locale cleared its ship gate) leaves the produced results.
  • A run that leaves work waiting for review (outcome parked) leaves what did catch up and annotates the languages waiting for review.
  • A failed run exits non-zero, so the steps after it are skipped.

It sets outputs you can use in later steps:

OutputDescription
statussuccess, no-changes, or failed
outcomeWith command: up: converged, or parked when work waits for review
passesWith command: up: how many reconciliation passes the run took
parked-localesWith command: up: comma-separated languages waiting for review, short of their gate
gateWith command: check: pass or fail; empty when the check did not run or errored
resultWith command: check: passed, failed, did_not_run, or error
did-not-run-causeWith command: check, when result is did_not_run: the cause kapi reported
has-changestrue when the run left changes in the working tree
changed-filesNewline-separated paths the run changed

With plan: true, the plan-* outputs carry the pending translations, the ones content memory recovers, the ones left for AI and the token estimate.

kapi-action Inputs​

InputDefaultDescription
commandupThe kapi command to run
args""Additional arguments
project""Path to the kapi.yaml recipe (-p flag)
planfalseWith command: up, dry-run instead (kapi up --plan): report pending work, memory reuse, and a token estimate; no writes, no provider calls. Pairs with pr-comment to post the cost of a change on its PR
fail-on-parkedfalseWith command: up, fail the workflow when work waits for review instead of reporting partial progress
pr-commentfalseOn pull-request events, post one sticky comment with the report (plan, kapi up outcome, or check result) that re-runs update in place
tokengithub.tokenGitHub token for the sticky PR comment
paths"" (the whole working tree)Space-separated paths to scan for changes
note

The workflow needs permissions: contents: write for your delivery step to push commits, plus pull-requests: write when pr-comment is used or the delivery step opens a pull request.

Reusable workflows​

For one-line adoption, neokapi/kapi-workflows packages the checkout → setup-kapi → kapi-action sequence as reusable workflows. up.yml@v1 catches the project up and delivers a pull request:

name: Catch up

on:
schedule:
- cron: "0 6 * * 1-5"
workflow_dispatch:

jobs:
up:
uses: neokapi/kapi-workflows/.github/workflows/up.yml@v1
permissions:
contents: write
pull-requests: write
with:
server: https://dev.bowrain.cloud
secrets:
bowrain-auth-token: ${{ secrets.BOWRAIN_AUTH_TOKEN }}

On a server-connected project the bowrain-auth-token secret and server input are all the job needs; the loop runs on the Bowrain server. A local-venue run passes the anthropic-api-key secret instead. The workflow exposes outcome, passes, parked-locales, has-changes, and pull-request-url as job outputs.

gate.yml@v1 is the merge gate: it runs kapi check --ship, fails the job on exit 3 (a gate is unmet) or 4 (a gate did not run), and posts one sticky report comment on the pull request:

name: Ship gate

on:
pull_request:
paths:
- "src/locales/**"
- "kapi.yaml"
- ".kapi/**"

jobs:
ship-gate:
uses: neokapi/kapi-workflows/.github/workflows/gate.yml@v1
permissions:
contents: read
pull-requests: write

Use the actions directly (as in the examples below) when the job needs a custom shape.

Example: Ship Gate on Pull Request​

Gate pull requests on the project's release bar whenever content files change. kapi check --ship runs the project's bound quality gates (voice, terms, rule-based checks) plus its ship/source coverage gates, and fails the job with exit 3 when any gate is unmet or exit 4 when a gate did not run:

name: Ship gate

on:
pull_request:
paths:
- "src/locales/**"
- "kapi.yaml"
- ".kapi/**"

jobs:
ship-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: neokapi/setup-kapi@v1
with:
version: "1.2.0"
plugins: bowrain@1.2.0

- name: Enforce the ship gates
run: kapi check --ship

An ordinary build passes when a locale is behind. check --ship is the step you add to enforce the release bar.

Example: Push source on Push to Main​

Send source changes to Bowrain Cloud when they land on main. Push only moves content. With bowrain.converge: on-push, the server then catches the project up on its own schedule. Use kapi up instead of kapi push if you want CI to watch the run and commit the results back:

name: Push source

on:
push:
branches: [main]
paths:
- "src/locales/**"

jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: neokapi/setup-kapi@v1
with:
version: "1.2.0"
plugins: bowrain@1.2.0
auth-token: ${{ secrets.BOWRAIN_AUTH_TOKEN }}
server: https://dev.bowrain.cloud

- name: Push to Bowrain Cloud
run: kapi push

The auth-token and server inputs export BOWRAIN_AUTH_TOKEN and BOWRAIN_SERVER_URL as environment variables, which the CLI picks up automatically.

Example: Scheduled catch-up​

Catch up on a schedule (for example nightly) to keep target locales up to date. kapi-action runs kapi up, and the last step commits what the run changed:

name: Nightly catch-up

on:
schedule:
- cron: "0 2 * * *" # 2 AM UTC

permissions:
contents: write

jobs:
up:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: neokapi/setup-kapi@v1
with:
version: "1.2.0"
plugins: bowrain@1.2.0
auth-token: ${{ secrets.BOWRAIN_AUTH_TOKEN }}
server: https://dev.bowrain.cloud

- uses: neokapi/kapi-action@v1
id: up

- name: Commit the results
if: steps.up.outputs.has-changes == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "chore: update translations via kapi"
git push

To draft specific files ad hoc instead of catching a project up, kapi translate takes explicit inputs: kapi translate src/locales/en/app.json --target-lang fr (an AI provider key such as ANTHROPIC_API_KEY must be set for a CI run that drafts).

Example: Pull and Merge Server Changes​

Pull results from Bowrain Cloud and open a PR:

name: Pull results

on:
workflow_dispatch:
schedule:
- cron: "0 8 * * 1" # Monday 8 AM UTC

jobs:
pull:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: neokapi/setup-kapi@v1
with:
version: "1.2.0"
plugins: bowrain@1.2.0
auth-token: ${{ secrets.BOWRAIN_AUTH_TOKEN }}
server: https://dev.bowrain.cloud

- name: Pull from Bowrain Cloud
run: kapi pull

- name: Create PR if changed
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git diff --quiet && exit 0
BRANCH="bowrain/pull-results-$(date +%Y%m%d)"
git checkout -b "${BRANCH}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "chore: pull results from Bowrain Cloud"
git push -u origin "${BRANCH}"
gh pr create \
--title "Pull results from Bowrain Cloud" \
--body "Automated pull of the latest results from Bowrain Cloud."

Authentication​

kapi supports two authentication methods in CI:

MethodHowBest For
Environment variableSet BOWRAIN_AUTH_TOKENGitHub Actions (via auth-token input)
Device flowRun kapi auth login interactivelyLocal development

The auth-token input on the setup action is the simplest approach: it exports the token as BOWRAIN_AUTH_TOKEN, which the CLI checks before looking for stored credentials.

Generating a CI Token​

Create an API token using kapi:

kapi auth login # authenticate with Bowrain Cloud
kapi auth token create --name "CI" --expire-days 90

The token (bwt_...) is shown once, so store it right away as a GitHub Actions secret:

gh secret set BOWRAIN_AUTH_TOKEN --repo your-org/your-repo

You can list and revoke tokens with kapi auth token list and kapi auth token delete.

Plugins​

The plugins input defaults to bowrain, the plugin that provides sync, push, and pull. List refs (as the registry names them) to add others alongside it, or pass '' to install nothing:

- uses: neokapi/setup-kapi@v1
with:
version: "1.2.0"
plugins: |
bowrain@1.2.0
okapi-bridge

Plugins are cached between runs. The cache key includes a hash of the plugin list, so changes to the list trigger a fresh install.

Pinning Versions​

Pin the CLI version to avoid surprises from new releases:

- uses: neokapi/setup-kapi@v1
with:
version: "1.2.0"

latest (the default) installs the newest stable release, and kapi up, kapi check --ship and the other commands these examples run need 1.2.0 or later.