From 88b0b1388d665bc78a7083ece77e3f9c5f294e49 Mon Sep 17 00:00:00 2001 From: Asgeir Frimannsson Date: Mon, 13 Jul 2026 10:43:39 +0800 Subject: [PATCH 1/2] fix: resolve `latest` to the CLI, and cache plugins where kapi actually puts them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs, both silent. `version: latest` was broken. It asked GitHub for `releases/latest`, but one repository publishes the CLI, the desktop apps, the bowrain server, AND every plugin — so that flag lands on whichever was published last. Today that is a plugin: `check-v0.1.0`. The Action resolved VERSION=check-v0.1.0, then tried to download `kapi_check-v0.1.0_linux_amd64.tar.gz` from tag `vcheck-v0.1.0` and 404'd. `latest` now scans releases for the newest stable CLI tag (exactly vX.Y.Z, no draft, no prerelease, no plugin/app prefix) — which resolves to 1.1.0 rather than check-v0.1.0. With that fixed, the default version becomes `latest` instead of a pinned, now-stale 1.0.0; the README already claimed `latest`. The plugin cache pointed at the wrong directory. kapi installs plugins under $XDG_DATA_HOME/kapi/plugins (~/.local/share/kapi/plugins on a runner, where XDG_DATA_HOME is unset), but the cache saved ~/.config/kapi/plugins — the CONFIG dir, which kapi never writes a plugin to. So the cache stored nothing, cache-hit was never true, and every run re-downloaded every plugin. Also: the CLI publishes no Intel-macOS archive, so an x64 macOS runner used to fail with a 404 on an asset that does not exist. platform.sh now says so. README: drop `kapi sync` (retired — `up` is the convergence verb), correct the platform table (macOS is arm64-only), and explain why `latest` cannot use GitHub's latest-release flag. --- README.md | 12 ++++++------ action.yml | 11 ++++++++--- scripts/platform.sh | 7 +++++++ scripts/resolve-version.sh | 30 ++++++++++++++++++++++++++---- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 2e23090..4e6db16 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ The built-in `GITHUB_TOKEN` is used by default for release downloads, so no toke steps: - uses: neokapi/setup-kapi@v1 with: - version: "1.0.0" + version: "1.1.0" ``` ### Run a localization pipeline @@ -43,16 +43,16 @@ steps: auth-token: ${{ secrets.BOWRAIN_AUTH_TOKEN }} server: https://your.bowrain.server - - run: kapi sync + - run: kapi up ``` -To run the full sync-and-commit flow, pair this with [`kapi-action`](https://github.com/neokapi/kapi-action). +`kapi up` is the convergence verb: with the bowrain plugin installed and a `server:` block in the recipe, it pushes, converges on the server, and pulls the produced targets back. To run it and commit the results, pair this with [`kapi-action`](https://github.com/neokapi/kapi-action). ## Inputs | Input | Description | Default | Required | |-------|-------------|---------|----------| -| `version` | Kapi CLI version (e.g. `1.0.0` or `latest`) | `latest` | No | +| `version` | Kapi CLI version (e.g. `1.1.0`), or `latest` for the newest stable CLI release | `latest` | No | | `token` | GitHub token for release downloads and API rate limits | `${{ github.token }}` | No | | `plugins` | Newline- or comma-separated plugin refs to install (e.g. `kapi-bowrain`) | — | No | | `auth-token` | Bowrain server JWT, exported as `BOWRAIN_AUTH_TOKEN` | — | No | @@ -67,7 +67,7 @@ To run the full sync-and-commit flow, pair this with [`kapi-action`](https://git ## How it works -1. **Resolve version** — `latest` queries the GitHub API for the most recent release; pinned versions pass through. +1. **Resolve version** — `latest` resolves the newest *stable CLI* release (a `vX.Y.Z` tag). It deliberately does not use GitHub's "latest release" flag: the same repository publishes plugin and app releases (`check-v0.1.0`, `asr-v0.1.1`, `bowrain-v…`), and that flag lands on whichever was published last. Pinned versions pass through. 2. **Cache check** — restores a cached binary keyed on `kapi-{version}-{os}-{arch}`. 3. **Download + verify** (on cache miss) — downloads the archive and `checksums.txt` from the GitHub release, verifies the SHA-256 checksum, and extracts the binary. 4. **Add to PATH** — makes `kapi` available to all subsequent steps. @@ -83,7 +83,7 @@ The binary is cached keyed on version + OS + arch; plugins are cached keyed on t | Runner OS | Architectures | |-----------|---------------| | Linux | amd64, arm64 | -| macOS | amd64, arm64 | +| macOS | arm64 (Apple silicon; no Intel build is published) | | Windows | amd64, arm64 | ## License diff --git a/action.yml b/action.yml index 933ebbf..146fe5e 100644 --- a/action.yml +++ b/action.yml @@ -6,9 +6,9 @@ branding: inputs: version: - description: "Kapi CLI version (e.g. '0.5.0' or 'latest')" + description: "Kapi CLI version (e.g. '1.1.0' or 'latest' for the newest stable CLI release)" required: false - default: "1.0.0" + default: "latest" token: description: "GitHub token used for release downloads and API rate limits. The built-in GITHUB_TOKEN is sufficient now that neokapi/neokapi is public." required: false @@ -134,7 +134,12 @@ runs: id: plugin-cache uses: actions/cache@v5 with: - path: ~/.config/kapi/plugins + # kapi installs plugins under $XDG_DATA_HOME/kapi/plugins — i.e. + # ~/.local/share/kapi/plugins when XDG_DATA_HOME is unset, as it is on a + # runner. The old ~/.config/kapi/plugins is the CONFIG dir: nothing was + # ever written there, so the cache saved an empty path and every run + # re-downloaded every plugin. + path: ~/.local/share/kapi/plugins key: kapi-plugins-${{ steps.plugin-hash.outputs.hash }}-${{ runner.os }}-${{ runner.arch }} restore-keys: | kapi-plugins- diff --git a/scripts/platform.sh b/scripts/platform.sh index 4118387..2188f17 100755 --- a/scripts/platform.sh +++ b/scripts/platform.sh @@ -20,6 +20,13 @@ case "${RUNNER_ARCH}" in *) echo "::error::Unsupported architecture: ${RUNNER_ARCH}" >&2; exit 1 ;; esac +# The CLI ships darwin_arm64 only — there is no Intel-mac archive to download, so +# say that plainly here instead of failing later with a 404 on a release asset. +if [ "${GOOS}" = "darwin" ] && [ "${GOARCH}" = "amd64" ]; then + echo "::error::kapi does not publish an Intel-macOS (darwin_amd64) build. Use an arm64 macOS runner (macos-14 or newer, or macos-latest)." >&2 + exit 1 +fi + case "${GOOS}" in windows) ARCHIVE_EXT="zip" ;; *) ARCHIVE_EXT="tar.gz" ;; diff --git a/scripts/resolve-version.sh b/scripts/resolve-version.sh index bffd45c..0849ba0 100755 --- a/scripts/resolve-version.sh +++ b/scripts/resolve-version.sh @@ -2,7 +2,7 @@ # Resolve kapi CLI version. # # Usage: resolve-version.sh -# If is "latest", queries the GitHub API for the latest release tag. +# If is "latest", resolves the newest STABLE kapi CLI release. # Otherwise, strips a leading "v" and echoes the version. # # Requires: GH_TOKEN for the `gh` CLI (API rate limits). The built-in @@ -13,11 +13,33 @@ set -euo pipefail VERSION="${1:?Usage: resolve-version.sh }" if [ "${VERSION}" = "latest" ]; then - TAG=$(gh api repos/neokapi/neokapi/releases/latest --jq .tag_name 2>/dev/null) || { - echo "::error::Failed to query the latest neokapi/neokapi release." >&2 + # NOT `releases/latest`. One repository publishes the CLI, the desktop apps, + # the bowrain server, and every plugin, so its releases are a mix of tags: + # v1.1.0 ← the CLI (what we want) + # check-v0.1.0 ← the kapi-check plugin + # asr-v0.1.1 ← the kapi-asr plugin + # bowrain-v1.2.0-rc8 + # v1.2.0-rc9 ← a CLI prerelease + # GitHub's "latest" flag lands on whichever of those was published last — as of + # writing, a plugin (check-v0.1.0). Asking for it yielded VERSION=check-v0.1.0, + # a download of `kapi_check-v0.1.0_linux_amd64.tar.gz` from tag `vcheck-v0.1.0`, + # and a 404. So resolve the CLI ourselves: tags of the exact shape vX.Y.Z, + # excluding drafts, prereleases, and every plugin/app tag prefix. + VERSION=$( + gh api "repos/neokapi/neokapi/releases?per_page=100" --paginate \ + --jq '.[] | select(.draft == false and .prerelease == false) | .tag_name' 2>/dev/null | + grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | + sed 's/^v//' | + sort -V | + tail -n 1 + ) || { + echo "::error::Failed to query neokapi/neokapi releases." >&2 exit 1 } - VERSION="${TAG#v}" + if [ -z "${VERSION}" ]; then + echo "::error::No stable kapi CLI release (vX.Y.Z) found in neokapi/neokapi." >&2 + exit 1 + fi else VERSION="${VERSION#v}" fi From 85a117962578098a130d9544eaf5fd4d2ce2c677 Mon Sep 17 00:00:00 2001 From: Asgeir Frimannsson Date: Mon, 13 Jul 2026 10:50:24 +0800 Subject: [PATCH 2/2] test: pin against the CLI resolver, not GitHub's latest-release flag The pinned-version job computed its version with the same `releases/latest` call the action had, and hit the same bug: it resolved "check-v0.1.0" (a plugin release) and then asked the action to install a kapi CLI of that version. It now calls the action's own resolver, so CI exercises the shipped logic instead of a second, divergent copy of it. --- .github/workflows/test.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 754d827..80abfdb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,10 +50,16 @@ jobs: 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: | - TAG=$(gh api repos/neokapi/neokapi/releases/latest --jq .tag_name) - VERSION="${TAG#v}" + VERSION=$(./scripts/resolve-version.sh latest) echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "Pinning to CLI version ${VERSION}" - name: Setup kapi (pinned) id: setup