Skip to content
Open
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
49 changes: 49 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,55 @@ jobs:
name: state-cache-restore-${{ matrix.os }}
path: ${{ runner.temp }}/results

# parse-cache and its deprecated name cache-tm. The save job left a parse
# cache to restore, and no kapi command runs here, so whether the cache
# directory exists after setup shows which value the action acted on. When
# cache-tm is set, it decides.
test-parse-cache-input:
name: "Parse cache input (${{ matrix.case }})"
needs: test-state-cache-save
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- case: "parse-cache: false"
parse_cache: "false"
cache_tm: ""
restored: "false"
- case: "cache-tm: false"
parse_cache: "true"
cache_tm: "false"
restored: "false"
- case: "cache-tm: true over parse-cache: false"
parse_cache: "false"
cache_tm: "true"
restored: "true"
steps:
- uses: actions/checkout@v6

- name: Setup kapi
uses: ./
with:
token: ${{ secrets.NEOKAPI_GITHUB_TOKEN }}
version: ${{ env.STATE_CACHE_KAPI_VERSION }}
plugins: ""
project-dir: test/fixture
parse-cache: ${{ matrix.parse_cache }}
cache-tm: ${{ matrix.cache_tm }}

- name: Check whether the parse cache was restored
shell: bash
env:
WANT_RESTORED: ${{ matrix.restored }}
run: |
docs=test/fixture/.kapi/work/cache/docs
if [ "${WANT_RESTORED}" = "true" ]; then
test -s "${docs}/index.db"
else
test ! -e "${docs}"
fi

test-cache:
name: "Cache hit (${{ matrix.os }})"
runs-on: ${{ matrix.os }}
Expand Down
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ The newest stable release, which `latest` installs, has no `kapi up`, so this ex
| `plugins` | Newline- or comma-separated plugin refs to install, as the registry names them (`bowrain`, `okapi-bridge`; a `kapi-` prefix is stripped). Pass `''` to install nothing | `bowrain` | No |
| `auth-token` | Bowrain server JWT, exported as `BOWRAIN_AUTH_TOKEN` | — | No |
| `server` | Bowrain server URL, exported as `BOWRAIN_SERVER_URL` | — | No |
| `cache-tm` | Carry kapi's parse cache (`.kapi/work/cache/docs`) between runs with the job cache; see [Project parse cache](#project-parse-cache). Runs only when a `kapi.yaml` recipe (or legacy `*.kapi`) is present. Set `false` to disable | `true` | No |
| `parse-cache` | Carry kapi's parse cache (`.kapi/work/cache/docs`) between runs with the job cache; see [Project parse cache](#project-parse-cache). Runs only when a `kapi.yaml` recipe (or legacy `*.kapi`) is present. Set `false` to disable | `true` | No |
| `cache-tm` | Deprecated name of `parse-cache`. When set, it decides in place of `parse-cache`, and the action prints a warning | `''` | No |
| `project-dir` | Directory holding the `kapi.yaml` project whose parse cache is carried between runs | `.` | No |

## Outputs
Expand Down Expand Up @@ -105,22 +106,23 @@ image.
4. **Add to PATH** — makes `kapi` available to all subsequent steps.
5. **Configure auth** (optional) — exports `BOWRAIN_AUTH_TOKEN`/`BOWRAIN_SERVER_URL` when `auth-token` is set.
6. **Install plugins** — installs each ref in `plugins` (default: `bowrain`) via `kapi plugins install`, cached keyed on the plugin set + OS + arch. Refs use the registry names; a `kapi-` binary prefix is stripped (`kapi-bowrain` → `bowrain`).
7. **Restore the project parse cache** (when a `kapi.yaml` recipe, or legacy `*.kapi`, is present): restores `.kapi/work/cache/docs` from the job cache, and saves it again at job end under a key unique to the job and run attempt. See [Project parse cache](#project-parse-cache). Disable with `cache-tm: false`.
7. **Restore the project parse cache** (when a `kapi.yaml` recipe, or legacy `*.kapi`, is present): restores `.kapi/work/cache/docs` from the job cache, and saves it again at job end under a key unique to the job and run attempt. See [Project parse cache](#project-parse-cache) for what it leaves to `kapi context pull`. Disable with `parse-cache: false`.

## Caching

The binary is cached keyed on version + OS + arch; plugins are cached keyed on the plugin set + OS + arch. Both skip their download step on a cache hit.

### Project parse cache

kapi keeps the state it derives out of git, under `.kapi/work/` (the project's `.kapi/.gitignore` ignores `work/` and `filters.local.json`). With `cache-tm` on and a recipe in `project-dir`, the action restores one directory of it, `.kapi/work/cache/docs`, before your steps run, and `actions/cache` saves it again when the job ends. kapi records there how it parsed each source and target file, so a later run can replay a file instead of parsing it again.
kapi keeps what it derives from a checkout under `.kapi/`, out of git. With `parse-cache` on and a recipe in `project-dir`, the action restores one directory of it, `.kapi/work/cache/docs`, before your steps run, and `actions/cache` saves it again when the job ends. kapi records there how it parsed each source and target file, so a later run can replay a file instead of parsing it again.

A restored parse cache does not change any result. kapi keys each entry by the file's path and content hash, the parse configuration, the recipe and the kapi build, and parses the file again when any of them differs. The test workflow checks this on every change: with the cache restored, `kapi status`, `kapi check`, `kapi check --ship` and `kapi up` must match a cold run exactly, in exit codes, output and every file written, and they must still match after the source changes.

Everything else under `.kapi/` stays out of the cache:
The project's context (its terms, voice profiles, content memory and recorded decisions) lives in kapi's workspace, outside git, and moves between machines through a context backend (kapi 1.3 and later). A runner starts with an empty workspace, so a job that needs the context runs `kapi context pull` before kapi works, and `kapi context push` after a run records something. setup-kapi runs neither command: [`kapi-action`](https://github.com/neokapi/kapi-action)'s `context-sync` input runs them around its command, or your workflow can run them as steps of its own.

- The content memory, terms, voice profile and unit-state record are committed under `.kapi/`, so the checkout already holds them, and kapi builds its local store from them.
- `.kapi/work/store.db` also holds stored targets and staged review decisions. Restored from an earlier run, it reports targets the checkout does not hold, and `kapi status`, `kapi check --ship` and `kapi up` report differently than they would on a cold run.
Everything else under `.kapi/work/` stays out of the cache:

- `.kapi/work/store.db` holds this checkout's block cache and the targets a run wrote. Restored from an earlier run, it reports targets the checkout does not hold, and `kapi status`, `kapi check --ship` and `kapi up` report differently than they would on a cold run.
- `.kapi/work/cache/extractions/` holds `kapi extract` batches for `kapi merge`, and `.kapi/work/cache/redaction/` and `.kapi/work/vault/` hold withheld original values.
- `.kapi/work/cache/sync-cache.json` and `.kapi/work/cache/refs.json` hold server sync state, including a claim token.

Expand Down
64 changes: 46 additions & 18 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,29 @@ inputs:
description: "Bowrain server URL (exported as BOWRAIN_SERVER_URL)"
required: false
default: ""
cache-tm:
parse-cache:
description: >-
Carry kapi's parse cache (.kapi/work/cache/docs) between CI runs with the
job cache. kapi keeps the state it derives out of git, under .kapi/work/.
The content memory, terms and unit-state record are committed under
.kapi/, and kapi builds its local store from them in a fresh checkout, so
they need no cache. Only the parse cache is restored: each entry is keyed
by the file's content, the parse configuration, the recipe and the kapi
build, and kapi parses a file again when any of them differs. The store
and the rest of .kapi/work/ are never restored, because restoring them
changes what kapi status, kapi check and kapi up report. Runs only when a
kapi.yaml recipe (or legacy *.kapi) is present. Set 'false' to disable.
job cache. From kapi 1.3 the project's context (terms, voice profiles,
content memory and recorded decisions) lives in kapi's workspace outside
git and is shared through a context backend, and .kapi/ holds a cache of
what kapi derives from the checkout. A job fetches the context with
`kapi context pull`, through kapi-action's `context-sync` input or a step
of its own; this action restores only the parse cache. Each entry is
keyed by the file's content, the parse configuration, the recipe and the
kapi build, and kapi parses a file again when any of them differs. The
store and the rest of .kapi/work/ are never restored, because restoring
them changes what kapi status, kapi check and kapi up report. Runs only
when a kapi.yaml recipe (or legacy *.kapi) is present. Set 'false' to
disable.
required: false
default: "true"
cache-tm:
description: >-
Deprecated: use parse-cache. When set, it decides in place of
parse-cache, and the action prints a warning.
required: false
default: ""
project-dir:
description: "Directory holding the kapi.yaml project whose parse cache is carried between runs. Default: repository root."
required: false
Expand Down Expand Up @@ -190,12 +199,28 @@ runs:
kapi plugins install ${args[@]+"${args[@]}"} "${plugin}"
done <<< "${PLUGINS}"

# 11. Detect a kapi project: a committed kapi.yaml recipe (or legacy *.kapi).
# 11. Resolve whether to carry the parse cache. cache-tm is the deprecated
# name of parse-cache; a workflow that still sets it keeps its meaning.
- name: Resolve parse cache input
id: parse-cache
shell: bash
env:
INPUT_PARSE_CACHE: ${{ inputs.parse-cache }}
INPUT_CACHE_TM: ${{ inputs.cache-tm }}
run: |
ENABLED="${INPUT_PARSE_CACHE}"
if [ -n "${INPUT_CACHE_TM}" ]; then
echo "::warning title=setup-kapi::The cache-tm input is deprecated; use parse-cache instead."
ENABLED="${INPUT_CACHE_TM}"
fi
echo "enabled=${ENABLED}" >> "$GITHUB_OUTPUT"

# 12. Detect a kapi project: a committed kapi.yaml recipe (or legacy *.kapi).
# The parse cache is carried only for a project. The recipe is the
# signal because .kapi/work/ is gitignored and absent from a checkout.
- name: Detect kapi project
id: detect-project
if: inputs.cache-tm != 'false'
if: steps.parse-cache.outputs.enabled != 'false'
shell: bash
env:
PROJECT_DIR: ${{ inputs.project-dir }}
Expand All @@ -207,7 +232,7 @@ runs:
echo "found=false" >> "$GITHUB_OUTPUT"
fi

# 12. Restore kapi's parse cache, and save it at job end. The key is unique
# 13. Restore kapi's parse cache, and save it at job end. The key is unique
# to the job and run attempt, so actions/cache saves through its own
# post step; the restore takes the newest cache for the same kapi
# version, preferring the same ref.
Expand All @@ -217,19 +242,22 @@ runs:
# recipe and the kapi build, and parses again on any mismatch, so a
# restored entry is either valid for the checkout or ignored. The rest
# of .kapi/work/ stays out of the cache:
# - store.db holds stored targets and the unit working set; restored,
# it changes what kapi status, kapi check --ship and kapi up report.
# - store.db holds the checkout's block cache and the targets a run
# wrote; restored, it changes what kapi status, kapi check --ship
# and kapi up report.
# - cache/extractions holds kapi extract batches that kapi merge reads.
# - cache/redaction and vault/ hold withheld original values.
# - cache/sync-cache.json and cache/refs.json hold server sync state,
# including a claim token.
# The content memory, terms and unit-state record are committed under
# .kapi/ and come with the checkout.
# The project's context (terms, voice profiles, content memory and
# recorded decisions) lives in kapi's workspace outside the checkout. A
# job fetches it with `kapi context pull`, which this action leaves to
# kapi-action's context-sync input or the workflow's own step.
#
# The kapi version is part of the key because the parse cache keeps the
# entries every build wrote; a new version starts from an empty cache.
- name: Restore kapi parse cache
if: inputs.cache-tm != 'false' && steps.detect-project.outputs.found == 'true'
if: steps.parse-cache.outputs.enabled != 'false' && steps.detect-project.outputs.found == 'true'
uses: actions/cache@v5
with:
path: ${{ inputs.project-dir }}/.kapi/work/cache/docs
Expand Down
Loading