Repository navigation
173 lines (149 loc) · 6.02 KB
/
Copy pathci.yml
File metadata and controls
173 lines (149 loc) · 6.02 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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
# Least privilege: this workflow only reads the repo to run tests.
permissions:
contents: read
jobs:
test-matrix:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12", "3.13"]
services:
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_USER: statewave
POSTGRES_PASSWORD: statewave
POSTGRES_DB: statewave
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U statewave"
--health-interval 5s
--health-timeout 5s
--health-retries 5
env:
STATEWAVE_DATABASE_URL: postgresql+asyncpg://statewave:statewave@localhost:5432/statewave
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
# uv.lock is the install path for CI and for the image, so CI installs
# from it instead of re-resolving pyproject.toml. Pinned by commit SHA
# because setup-uv publishes no floating major tag past v7 — `@v10` does
# not resolve — and `version:` pins uv itself to the one that wrote the
# lockfile.
#
# No updater owns this `version:` string: Dependabot's github-actions
# ecosystem bumps the `uses:` SHA and never a `with:` input, and its uv
# ecosystem only rewrites uv.lock. The Dockerfile's uv stage IS tracked
# (the docker ecosystem parses FROM), so it moves on its own and this
# line stays put. tests/test_uv_pin_consistency.py is what keeps the two
# equal.
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: "0.13.1"
# The guard that stops uv.lock rotting back into a record of versions
# nothing installs. `uv lock --check` asserts the lockfile would be
# unchanged by a fresh resolution, so it fails on real drift between
# pyproject.toml and the lock without failing merely because a newer
# release exists upstream (resolution treats existing pins as
# preferences).
- name: Lockfile is current
run: uv lock --check
# `--system` installs into the setup-python interpreter for this matrix
# leg, which keeps `ruff`, `alembic` and `pytest` on PATH for the steps
# below. `uv sync` would install into a `.venv` that nothing here
# activates, and would prune as well.
- name: Install dependencies
run: |
uv export --locked --extra dev --no-emit-project --no-header -q -o /tmp/requirements-dev.txt
uv pip install --system -r /tmp/requirements-dev.txt
uv pip install --system --no-deps -e .
- name: Lint
run: ruff check .
# pyproject.toml is the only version anyone edits; the README, the issue
# templates and the Helm chart's appVersion carry copies that nothing at
# runtime can derive, so drift is invisible until a reader trusts a stale
# number or a default install pulls a stale image. This step
# lives inside the matrix because branch protection requires the
# aggregate "test" check, which only reflects matrix legs.
- name: Version consistency
run: python scripts/bump_version.py --check
- name: Run migrations
run: alembic upgrade head
- name: Unit tests
run: pytest tests/test_*.py -v
- name: Create test database
run: PGPASSWORD=statewave createdb -h localhost -U statewave statewave_test
- name: Integration tests
run: pytest tests/integration/ -v
# The test job installs the package from the source tree, so it never
# exercises the image we actually ship. A PR can be green and still produce a
# container that exits during boot — which docker-publish would then push as
# `latest`. Build the image and prove it serves /healthz before that happens.
docker-smoke:
runs-on: ubuntu-latest
services:
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_USER: statewave
POSTGRES_PASSWORD: statewave
POSTGRES_DB: statewave
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U statewave"
--health-interval 5s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v7
- name: Build image
run: docker build -t statewave:smoke .
- name: Start container
run: |
docker run -d --name statewave-smoke --network host \
-e STATEWAVE_DATABASE_URL=postgresql+asyncpg://statewave:statewave@localhost:5432/statewave \
statewave:smoke
- name: Probe /healthz
run: |
for i in $(seq 1 90); do
if curl -fsS http://localhost:8100/healthz > /dev/null 2>&1; then
echo "Container healthy after ${i}s."
exit 0
fi
if [ -z "$(docker ps -q --filter name=statewave-smoke --filter status=running)" ]; then
echo "::error::Container exited during boot (code $(docker inspect -f '{{.State.ExitCode}}' statewave-smoke))."
exit 1
fi
sleep 1
done
echo "::error::Timed out waiting for /healthz."
exit 1
- name: Container logs
if: always()
run: docker logs statewave-smoke || true
# Branch protection requires a check literally named "test". A matrix job
# reports per-leg contexts ("test (3.11)"...), which would leave the
# required check permanently "Expected" and block every PR. This aggregate
# keeps the stable name and fails if any leg failed or was cancelled.
test:
needs: test-matrix
if: always()
runs-on: ubuntu-latest
steps:
- name: All Python versions passed
run: |
if [ "${{ needs.test-matrix.result }}" != "success" ]; then
echo "test-matrix result: ${{ needs.test-matrix.result }}"
exit 1
fi