Files
FabledCurator/ci-requirements.md
T
bvandeusen fe48e77821
CI / lint (push) Successful in 3s
Build images / sign-extension (push) Successful in 4s
CI / extension-version (push) Successful in 3s
Build images / build-agent (push) Successful in 8s
CI / frontend-build (push) Successful in 22s
extension / lint (push) Successful in 25s
CI / backend-lint-and-test (push) Successful in 29s
Build images / build-web (push) Successful in 1m57s
Build images / build-ml (push) Successful in 2m38s
CI / integration (push) Successful in 3m50s
ci(extension): retire the manual-bump guard, true up the docs (step 5)
The guard asked whether a packaged extension file changed without the
version moving. Since step 4 nobody moves the version by hand, so it was
checking a fact that had stopped existing — and it was not merely dead
weight: it would have failed the lane on every real extension change,
demanding a bump that decides nothing. Removed rather than left running
beside the new mechanism (rule 22).

What replaces it is thinner and true. The extension-version lane now
asserts the derivation resolves on this commit, that the derived value is
the plain dotted-numeric shape AMO accepts, and that MAJOR.MINOR agrees
between manifest.json and package.json. MAJOR.MINOR is the one part still
hand-set, and packaging.sh reads it from manifest.json ALONE, so a
divergence ships a version package.json disagrees with. The lane keeps
fetch-depth: 0 — checking that the derivation survives a real checkout is
half its remaining value.

Deliberately not checked there: that the derived value beats what is
already signed. That guard belongs in build.yml, where it compares against
the real ext-* releases. Comparing against origin/main in a lane would be
wrong, because dev legitimately derives a LOWER value whenever main is
ahead on the extension, and a lane that fails for being behind is a lane
people learn to ignore.

packaging.sh is down to two consumers from three. version.spec.js's
"ci.yml derives its pathspec" test would have gone red on that, so it is
rewritten to assert the property rather than the consumer: no workflow
inlines an :(exclude)extension/ literal, across all three. That keeps the
#2397 anti-regression value while surviving consumers coming and going.
A second test pins build.yml to packaging.sh version and fails if it goes
back to grepping the committed value — which is not a style regression but
the #3092 bug itself. build.yml joins extension.yml's trigger paths, since
the suite now asserts against it.

The lockstep test narrows from the whole version string to MAJOR.MINOR.
The committed patch numbers are inert now; asserting on them would fail
for a difference that changes nothing.

Docs. extension/README.md's Release section described extension.yml
signing on main and committing the XPI into frontend/public/ — untrue
since 2026-05-25, and it told the reader to hand-bump both files, which is
now exactly the wrong instruction. Rewritten, with a Versioning section
that says plainly that editing the patch number does nothing and why the
key is commit time rather than a count. ci-requirements.md drops the third
packaging.sh consumer and names every job that needs full history. Root
README no longer claims the extension is signed on main only.
2026-08-27 11:29:44 -04:00

4.5 KiB

CI Requirements — FabledCurator

Spec: https://git.fabledsword.com/bvandeusen/CI-runner/src/branch/main/docs/process.md

Runtime image

git.fabledsword.com/bvandeusen/ci-python:3.14

Image deps used

  • python 3.14
  • ruff (analyzer for backend/, tests/, alembic/)
  • node (frontend job: npm install + vitest + vite build)
  • docker CLI + buildx (.forgejo/workflows/build.yml: build-web, build-ml — Fabled-Git registry push)

Secondary runtime image

node:24-bookworm-slim — .forgejo/workflows/extension.yml only.

The extension lane is the one job that does NOT run on ci-python:3.14: it needs a current Node for web-ext and vitest and nothing Python at all. Kept on the upstream slim image rather than adding a Node toolchain to ci-python, per docs/process.md's "add deps to the image when used by >1 project".

Per-job tool installs

  • pip install -r requirements.txt pytest pytest-asyncio — in backend-lint-and-test and integration jobs
  • npm install --no-audit --no-fund — in frontend-build job
  • npm install --no-audit --no-fund — in extension.yml's lint job (web-ext + vitest)
  • unzip — in extension.yml's "Verify XPI contents" step, installed via apt only when absent (node:24-bookworm-slim may or may not carry it). Debian package, ~2s. Not worth baking into a shared image for a single consumer, per docs/process.md's ">1 project" rule.

Notes

  • Integration wall time ~3 min, dominated by pgvector container start + the pip install step (~30-45s on cold cache) + alembic + 300+ integration tests.
  • The pip install in two jobs is intentional and per docs/process.md's "add deps to image when used by >1 project" rule: FC alone is one Python project, so the deps live in requirements.txt and install per-job. Reconsider when a second Fabled-family Python backend lands.
  • Integration uses Fabled-Git Actions services: + socket-discovered bridge IPs because act_runner (swarm-runner v0.6+) puts services on the default bridge with no embedded DNS. The pattern is documented in the rulebook's fabled-git.md "CI philosophy" section and FC's ci.yml is the canonical example.
  • No package-lock.json is tracked yet (FC's feedback_no_local_runs memory bans npm install locally). Using npm install rather than npm ci until a lockfile lands.
  • No imagemagick / pandoc per-job installs needed.
  • extension/'s vitest specs load lib/*.js by evaluating the real file as a classic script (test/helpers/loadLib.js) rather than adding module.exports shims to production code — the libs ship as background.scripts, not ES modules, so the specs exercise exactly the bytes packaged into the XPI.
  • extension/scripts/packaging.sh is the single definition of what ships inside the XPI. Two consumers read from it rather than keeping their own copy: web-ext's --ignore-files (extension/package.json), and the git log pathspec inside the script's own version derivation. It was three until 2026-08-27 — ci.yml's extension-version guard held the third and went when the manual bump it guarded did (milestone 271 step 5). Hand-kept copies of that one fact is what allowed issue #2397, so extension/test/version.spec.js asserts no workflow has reintroduced a literal :(exclude)extension/….
  • The shipped extension version is derived, not committed. It is the commit TIME of the newest packaged-extension change (minutes since 2020-01-01, per family rule 149 — never a commit count, which orders by branch rather than by recency). build.yml's sign-extension computes it and stamps it into extension/manifest.json + package.json in the working tree before signing; the stamp is never committed. Treat the version in the repo as a base: only its MAJOR.MINOR is read, and its patch component is inert.
  • Every job that calls packaging.sh version checks out with fetch-depth: 0build.yml's sign-extension and build-web, and ci.yml's extension-version. A depth-1 clone sees one commit and derives a wrong, too-low value rather than failing, so the full-history checkout is load-bearing rather than incidental.
  • Callers MUST set -f before substituting the script's output. Without it the shell expands test/** against the working tree and silently narrows the pattern to whatever files exist at that moment — a failure that looks like nothing until dev files start appearing in the XPI. test/version.spec.js asserts every --ignore-files consumer sets it, and that no consumer has quietly reinstated a hardcoded list.