Skip to content

fix: cache the parse cache kapi 1.2 writes, not retired paths #98

fix: cache the parse cache kapi 1.2 writes, not retired paths

fix: cache the parse cache kapi 1.2 writes, not retired paths #98

Workflow file for this run

name: Test
on:
push:
branches: [main]
pull_request:
branches: [main]
# Daily canary: the action installs from neokapi/neokapi releases, so it can
# break without a commit here (a renamed release asset did exactly that for
# 1.2.0). The schedule catches that class of breakage; the prerelease job
# below catches it while it is still an RC.
schedule:
- cron: "17 5 * * *"
workflow_dispatch:
env:
# The state cache jobs need kapi 1.2, the first release that keeps its state
# under .kapi/work/.
STATE_CACHE_KAPI_VERSION: 1.2.0-rc32
jobs:
test-latest:
name: "Latest (${{ matrix.os }})"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- name: Setup kapi (latest)
id: setup
uses: ./
with:
token: ${{ secrets.NEOKAPI_GITHUB_TOKEN }}
- name: Verify version output
shell: bash
env:
INSTALLED_VERSION: ${{ steps.setup.outputs.version }}
run: |
echo "Installed version: ${INSTALLED_VERSION}"
test -n "${INSTALLED_VERSION}"
- name: Verify kapi is on PATH
shell: bash
run: kapi version
test-pinned:
name: "Pinned version (${{ matrix.os }})"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- name: Get latest version for pinned test
id: version
shell: bash
env:
GH_TOKEN: ${{ secrets.NEOKAPI_GITHUB_TOKEN }}
# Use the action's own resolver rather than a second copy of the logic.
# This step used to call `releases/latest` directly and hit exactly the
# bug the action had: neokapi/neokapi publishes plugin and app releases
# alongside CLI ones, so that flag lands on whichever was published last
# (a plugin, today), and the pinned test then asked for version
# "check-v0.1.0".
run: |
VERSION=$(./scripts/resolve-version.sh latest)
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "Pinning to CLI version ${VERSION}"
- name: Setup kapi (pinned)
id: setup
uses: ./
with:
token: ${{ secrets.NEOKAPI_GITHUB_TOKEN }}
version: ${{ steps.version.outputs.version }}
- name: Verify correct version
shell: bash
env:
EXPECTED: ${{ steps.version.outputs.version }}
ACTUAL: ${{ steps.setup.outputs.version }}
run: |
echo "Expected: ${EXPECTED}, Actual: ${ACTUAL}"
test "${EXPECTED}" = "${ACTUAL}"
# Canary against the newest CLI prerelease. Release packaging changes land
# on RCs first (the kapi_ → kapi-cli_ archive rename shipped on 1.2.0-rc1),
# so installing the newest prerelease here fails weeks before the same
# breakage would reach a stable release and every consumer workflow.
test-prerelease:
name: "Prerelease canary"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Resolve newest CLI prerelease
id: prerelease
shell: bash
env:
GH_TOKEN: ${{ secrets.NEOKAPI_GITHUB_TOKEN }}
# Same tag discipline as resolve-version.sh: only vX.Y.Z-… CLI tags,
# never plugin/app tags (check-v…, asr-v…, bowrain-v…).
run: |
VERSION=$(
gh api "repos/neokapi/neokapi/releases?per_page=100" --paginate \
--jq '.[] | select(.draft == false and .prerelease == true) | .tag_name' |
grep -E '^v[0-9]+\.[0-9]+\.[0-9]+-' |
sed 's/^v//' |
sort -V |
tail -n 1
) || true
if [ -z "${VERSION}" ]; then
echo "No CLI prerelease found — nothing to canary."
else
echo "Newest CLI prerelease: ${VERSION}"
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Setup kapi (prerelease)
if: steps.prerelease.outputs.version != ''
id: setup
uses: ./
with:
token: ${{ secrets.NEOKAPI_GITHUB_TOKEN }}
version: ${{ steps.prerelease.outputs.version }}
- name: Verify kapi is on PATH
if: steps.prerelease.outputs.version != ''
shell: bash
run: kapi version
# The state cache round trip on a kapi 1.2 project. This job runs the fixture
# and the action saves the cache at job end; the next job restores it. Each
# job compares its run in the cached project directory with a cold run of a
# pristine copy, and the comparison covers every command's exit code and
# output and every file the project holds afterwards.
test-state-cache-save:
name: "State cache save (${{ matrix.os }})"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# kapi 1.2 prereleases publish no Windows CLI build.
os: [ubuntu-latest, macos-latest]
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
- name: Run the fixture and a cold copy
shell: bash
run: |
test/state-cache/cold-copy.sh "${RUNNER_TEMP}/cold"
test/state-cache/run.sh test/fixture "${RUNNER_TEMP}/results/cached"
test/state-cache/run.sh "${RUNNER_TEMP}/cold/test/fixture" "${RUNNER_TEMP}/results/cold"
test/state-cache/compare.sh "${RUNNER_TEMP}/results/cold" "${RUNNER_TEMP}/results/cached"
# The restore job needs something to restore; this fails first if kapi
# stops writing the path the action caches.
- name: Require the parse cache kapi writes
shell: bash
run: |
test -s test/fixture/.kapi/work/cache/docs/index.db
cp test/fixture/.kapi/work/store.db "${RUNNER_TEMP}/results/store.db"
- uses: actions/upload-artifact@v7
with:
name: state-cache-save-${{ matrix.os }}
path: ${{ runner.temp }}/results
test-state-cache-restore:
name: "State cache restore (${{ matrix.os }})"
needs: test-state-cache-save
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
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
- name: Require the restored parse cache
shell: bash
run: |
docs=test/fixture/.kapi/work/cache/docs
test -s "${docs}/index.db"
ls "${docs}"/parts-*.log > /dev/null
echo "Restored $(find "${docs}" -type f | wc -l | tr -d ' ') files into ${docs}"
cp -R "${docs}" "${RUNNER_TEMP}/restored-docs"
- uses: actions/download-artifact@v8
with:
name: state-cache-save-${{ matrix.os }}
path: ${{ runner.temp }}/saved
- name: A warm run matches a cold run
shell: bash
run: |
test/state-cache/cold-copy.sh "${RUNNER_TEMP}/cold"
test/state-cache/run.sh test/fixture "${RUNNER_TEMP}/results/warm"
test/state-cache/run.sh "${RUNNER_TEMP}/cold/test/fixture" "${RUNNER_TEMP}/results/cold"
test -s "${RUNNER_TEMP}/results/warm/docs-before.txt"
test/state-cache/compare.sh "${RUNNER_TEMP}/results/cold" "${RUNNER_TEMP}/results/warm"
test/state-cache/compare.sh "${RUNNER_TEMP}/saved/cold" "${RUNNER_TEMP}/results/warm"
- name: A cache saved before a source change does not hide it
shell: bash
run: |
git clean -fdxq test/fixture
git checkout -- test/fixture
mkdir -p test/fixture/.kapi/work/cache
cp -R "${RUNNER_TEMP}/restored-docs" test/fixture/.kapi/work/cache/docs
test/state-cache/change-source.sh test/fixture
test/state-cache/cold-copy.sh "${RUNNER_TEMP}/cold-changed"
test/state-cache/change-source.sh "${RUNNER_TEMP}/cold-changed/test/fixture"
test/state-cache/run.sh test/fixture "${RUNNER_TEMP}/results/warm-changed"
test/state-cache/run.sh "${RUNNER_TEMP}/cold-changed/test/fixture" "${RUNNER_TEMP}/results/cold-changed"
test/state-cache/compare.sh "${RUNNER_TEMP}/results/cold-changed" "${RUNNER_TEMP}/results/warm-changed"
# The negative control: .kapi/work/store.db holds stored targets and the
# unit working set, so restoring it has to change a result. If the
# comparison reports no difference here, it cannot be trusted above.
- name: The comparison catches a restored store
shell: bash
run: |
git clean -fdxq test/fixture
git checkout -- test/fixture
mkdir -p test/fixture/.kapi/work/cache
cp -R "${RUNNER_TEMP}/restored-docs" test/fixture/.kapi/work/cache/docs
cp "${RUNNER_TEMP}/saved/store.db" test/fixture/.kapi/work/store.db
test/state-cache/run.sh test/fixture "${RUNNER_TEMP}/results/warm-store"
if test/state-cache/compare.sh "${RUNNER_TEMP}/results/cold" "${RUNNER_TEMP}/results/warm-store"; then
echo "::error::Restoring .kapi/work/store.db changed no result, so the comparison cannot detect restored state."
exit 1
fi
- uses: actions/upload-artifact@v7
if: always()
with:
name: state-cache-restore-${{ matrix.os }}
path: ${{ runner.temp }}/results
test-cache:
name: "Cache hit (${{ matrix.os }})"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
# First install — populates cache
- name: Setup kapi (first)
uses: ./
with:
token: ${{ secrets.NEOKAPI_GITHUB_TOKEN }}
# Second install — should hit cache
- name: Setup kapi (cached)
uses: ./
with:
token: ${{ secrets.NEOKAPI_GITHUB_TOKEN }}
- name: Verify kapi works from cache
shell: bash
run: kapi version