[Continuous integration]

GitHub Actions matrix for lint, Python tests, backend, VSIX, AXIS PWA, e2e smoke, and nightly gates.

Continuous integration

Abstract

CI is the public invariant check for the monorepo-like layout. A single PR can touch the Python evaluator, the Flask API, the VS Code extension, the AXIS PWA, and a Cloudflare Worker typecheck — the workflow graph is deliberately fan-out with concurrency cancellation on newer pushes to the same ref.

Primary definition: .github/workflows/ci.yml. Nightly AXIS expansion lives in axis-nightly.yml. Release builds are separate (release.yml).

Conceptual model

Interface surface

Triggers

WorkflowOnPurpose
ci.ymlpush/PR to mainRequired-path confidence
axis-nightly.ymlcron 0 4 * * * + workflow_dispatchFull Playwright + security + coverage gate
release.ymltags v* + dispatchLSP binaries + VSIX + GitHub Release

ci.yml jobs

JobRuntimeWhat it asserts
lintPython 3.13ruff check src/ tests/; mypy src/ --ignore-missing-imports
testMatrix 3.10–3.13Editable .[lsp]; LSP unit/e2e tests; backend tests; Codecov upload on 3.13 only
backend-test3.13Explicit Flask/numpy/matplotlib install + test_backend.py
vscode-ext-testNode 22npm ci → compile → vsce package → upload VSIX artifact (7-day)
pwaBun 1.3Root + worker install, dual tsc, frontend coverage gate + security tests; upload lcov
axis-e2eBun 1.3Playwright Chromium smoke (test:e2e:smoke)
cf-workerBun 1.3frontend/worker typecheck only

Concurrency:

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

Nightly (axis-nightly.yml)

  • Full e2e suite (bun run test:e2e) with report artifact on failure
  • Coverage gate + security tests (stricter than PR smoke)

Internals

PathRole
.github/workflows/ci.ymlPR/main fan-out
.github/workflows/axis-nightly.ymlScheduled AXIS depth
.github/workflows/release.ymlTag release (see Release)
frontend/scripts/check-coverage.mjsCoverage gate helper for AXIS
codecov actionOptional; fail_ci_if_error: false

Secrets referenced in CI surface

SecretUsed byNotes
VSCE_PATVSIX package / publishExtension packaging
METADATA_KEYRelease / Cloud Build encryptStable Fernet key for metadata
GITHUB_TOKENRelease uploadDefault Actions token

�SLOT0� — CI test job currently does not run the full tests/ corpus on every matrix cell; it prioritizes LSP + backend. Local make test is broader.

Invariants & edge cases

  1. Fail-fast is off on the Python matrix — one version failure does not hide others.
  2. Codecov only on 3.13 to avoid noisy multi-upload; coverage XML path depends on pytest-cov configuration in the job.
  3. PWA install is resilient: bun install --frozen-lockfile || bun install — lockfile drift should still be fixed, not papered forever.
  4. VSIX artifacts are not the Marketplace. Packaging on every PR proves buildability; publish is release-gated.
  5. Worker typecheck ≠ deploy. cf-worker job does not call wrangler deploy.

Worked examples

Reproduce lint locally as CI does

pip install ruff flake8 mypy types-python-dateutil antlr4-python3-runtime
ruff check src/ tests/ --output-format=github
mypy src/ --ignore-missing-imports

Reproduce Python job slice

pip install -e ".[lsp]"
pip install pytest pytest-cov pytest-xdist pytest-asyncio
python -m pytest tests/test_langserver.py tests/test_lsp_features.py -v --tb=short
python -m pytest tests/test_backend.py -v --tb=short

Reproduce PWA job

bun install --frozen-lockfile
cd frontend/worker && bun install --frozen-lockfile && bunx tsc --noEmit
cd ../.. && bunx tsc -p tsconfig.json
cd frontend && bun install && bun run test:coverage:gate && bun run test:security

Failure modes

SymptomLikely causeFix
Matrix red only on 3.10Version-specific typing / APIGuard with sys.version_info or drop unsupported API
Playwright smoke flakeTiming / missing depsInstall chromium with deps; re-run; check nightly for full suite
VSIX job fails on vsceNode/engine mismatchPin Node 22 as workflow does
Coverage gate redNew untested AXIS pathsAdd unit tests or adjust gate intentionally
Cancelled mid-runNewer push to same branchExpected concurrency behavior

See also