-
Notifications
You must be signed in to change notification settings - Fork 0
131 lines (114 loc) · 4.97 KB
/
ci.yml
File metadata and controls
131 lines (114 loc) · 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# CI test gate — run the test suites on every pull request and on push to
# `main`, so a red suite can't merge.
#
# WHY: until now the lint/test pipeline was local-only — `scripts/lint.sh`
# + the pre-commit hooks, run on a contributor's laptop before the Merger
# pulled a PR (the deliberate "no CI in pre-alpha" posture). With many
# agents merging in parallel and only CodeQL gating server-side, test
# regressions slipped onto `main` green (e.g. #401 broke
# `tests/test_logentry.py`, undetected until a later full local run —
# #451). This enforces the suites server-side. Resolves the core of #452
# (run the test suites in CI); the no-CI revisit is recorded in
# `SECURITY.md` §8 / `docs/agents/decisions.md` / OQ-A-001.
#
# SCOPE: the backend job runs `pytest` (the regression that motivated
# #452 was a broken test). The frontend job runs the full `pnpm` gate —
# typecheck + lint + test + build — which is already self-consistent and
# green. Enforcing the *Python lint* gate in CI is a deliberate near-term
# follow-up: `scripts/lint.sh` currently runs two formatters (`ruff
# format` + `black`) whose output conflicts, so it is not yet satisfiable
# on a clean tree. That gets de-conflicted and the small existing lint
# debt cleared first (follow-up off #452), then the lint step is added
# here.
#
# SECURITY POSTURE:
# - Least-privilege: top-level `contents: read`; no job needs write.
# - All third-party actions are pinned to a full commit SHA (a tag can
# be moved, a SHA cannot) — consistent with codeql.yml / publish.yml
# and the supply-chain hardening in #331.
# - This is a *gate*, not a publisher: it has no access to PyPI, no
# `id-token`, and no stored secrets.
#
# NOTE: making these checks *required* (branch protection) is a separate
# owner action in repo Settings → Branches — see #452 / #331.
name: ci
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
# A newer push to the same ref supersedes an in-flight run — don't burn
# minutes finishing a run whose commit is already stale.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
backend:
name: Backend (pytest py${{ matrix.python }} / Django ${{ matrix.django }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Django 4.2 LTS is supported through April 2026 (#622). Add it
# alongside 5.x and 6.0 so the package keeps working for the
# majority of installed Django by deployment count.
python: ["3.12"]
django: ["4.2", "5.2"]
# 4.2 added Python 3.13 support in 4.2.16 but pip may resolve to
# an earlier 4.2.x; skip 3.13 + 4.2 conservatively. (We only
# exercise py3.12 here today; the matrix is set up for an easy
# future expansion.)
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: ${{ matrix.python }}
- name: Install Poetry
# Poetry is pinned to the version that generated poetry.lock (see
# its header). `poetry install` compares the lock's content-hash
# against the one it computes from pyproject; a different Poetry
# can compute it differently and reject an otherwise-valid lock.
# Bump this in lockstep whenever the lock is regenerated.
run: pipx install poetry==2.1.4
- name: Install dependencies (locked)
run: poetry install --no-interaction
- name: Pin Django to the matrix version
run: poetry run pip install "django~=${{ matrix.django }}.0"
# pytest with coverage (per pyproject `addopts`), including
# tests/test_security.py. `filterwarnings = ["error"]` means a new
# warning fails the run.
- name: Tests (pytest)
run: poetry run pytest
frontend:
name: Frontend (typecheck + lint + test + build)
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up pnpm
uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- name: Set up Node
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 22
cache: pnpm
cache-dependency-path: frontend/pnpm-lock.yaml
- name: Install dependencies (frozen lockfile)
run: pnpm install --frozen-lockfile
- name: Typecheck (tsc --noEmit, every package)
run: pnpm -r typecheck
- name: Lint (eslint --max-warnings 0 + stylelint + dark-mode coverage)
run: pnpm lint
- name: Unit tests (vitest)
run: pnpm test
- name: Build (Vite bundle, every package)
run: pnpm -r build