Skip to content

CI/CD Pipeline — GitHub Actions

This document describes the project's continuous integration (CI) pipeline in GitHub Actions. It focuses on fast feedback, trunk-based development, and safe defaults when parts of the stack are not present yet (Python and/or frontend).

  • Workflow file: .github/workflows/ci.yml
  • Triggers: push to any branch, pull requests to main, and manual workflow_dispatch
  • Permissions: minimal (contents: read)
  • Concurrency: uses ci- prefix with github.ref to avoid overlapping runs on the same ref (cancel-in-progress enabled)

Jobs

Python — Lint and Tests

  • Version: Python 3.12 on ubuntu-latest
  • Step-level detection for Python files to avoid running setup and tooling when the repo has no Python yet
  • Caching: pip cache keyed by requirements*.txt, pyproject.toml, setup.cfg
  • Tooling: ruff (lint), black (format check), pytest (tests)

Key steps:

  1. Detect Python files
    • Uses git ls-files -- '*.py' to set an output has_python=true|false
  2. Set up Python and cache pip only when has_python == 'true'
  3. Install project deps if requirements*.txt or pyproject.toml exist
  4. Install dev tools (ruff, black, pytest)
  5. Run: ruff check ., black --check ., pytest -q (tests only if tests/**/*.py exists)

Why step-level detection? GitHub Actions expressions like hashFiles() are not supported at the job level. Guarding steps with conditional expressions on steps.detect_py.outputs.has_python avoids context warnings and keeps the job defined while doing no work when Python isn't present.

Frontend — Build and Tests

  • Step-level detection for Node project by checking for package.json
  • Node setup: actions/setup-node@v4 with LTS and npm caching
  • Commands: npm ci, npm run build --if-present, npm test --if-present

Key steps:

  1. Detect Node project
    • Uses git ls-files -- 'package.json' to set has_node=true|false
  2. Set up Node only when has_node == 'true'
  3. Install, build, and test with --if-present to stay no-op-safe in early stages

Troubleshooting

  • Job-level if: supports hashFiles() and similar contexts syntactically, but these are evaluated before checkout and will not work as expected; keep file-system checks at the step level, or use on.paths filters in on.push/on.pull_request if you want to avoid triggering the workflow at all.
  • If your repo has no Python yet, the Python job will run with steps skipped — this is expected and fast. Same for frontend when there’s no package.json.
  • Cache misses on first run are normal; subsequent runs will warm the cache.

Optional enhancements (future)

  • Add a docs build job (e.g., VuePress) once the docs site exists.
  • Add coverage reporting (e.g., pytest-cov + Codecov) and upload artifacts.
  • Add paths filters to narrow triggers when the repo grows.

Publishing container images

Publishing images is handled by a dedicated workflow (to be added) that runs on merges to main and on releases. See docs/devops/registry.md for the phased GHCR strategy (naming, tagging, access, retention) and for where the publish workflow lives under .github/workflows/.

Skipping CI runs

To preserve build minutes you can opt out per change:

  • Include the token "[skip ci]" in the commit message (for push events), or in the PR title (for pull_request events). Jobs in .github/workflows/ci.yml will be skipped when this token is present.
  • You can also trigger runs manually via the Actions tab using "Run workflow" (workflow_dispatch).

See .github/workflows/ci.yml for the exact conditions.

Documentation generated with VitePress