Skip to content

Commit d233257

Browse files
committed
update deployment pipeline
1 parent 751be7b commit d233257

2 files changed

Lines changed: 281 additions & 23 deletions

File tree

.github/workflows/run-full-ci.yml

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,18 @@
99
# For setting up python see: https://github.com/actions/setup-python
1010
# For setting up node see: https://github.com/actions/setup-node
1111
# For running pre-commit see: https://github.com/pre-commit/action
12+
# For setting up uv see: https://github.com/astral-sh/setup-uv
1213
# For setting up postgres service container see: https://docs.github.com/en/actions/using-containerized-services/creating-postgresql-service-containers
1314

1415
name: Run full CI
1516
on: push
17+
18+
permissions: read-all
19+
20+
concurrency:
21+
group: ${{ github.workflow }}-${{ github.ref }}
22+
cancel-in-progress: true
23+
1624
jobs:
1725
# Lint job: (Run on all branch pushes)
1826
# - Checks out our branch
@@ -21,24 +29,22 @@ jobs:
2129
lint:
2230
runs-on: ubuntu-latest
2331
steps:
24-
- uses: actions/checkout@v4
25-
- uses: actions/setup-python@v5
32+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
33+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
34+
with:
35+
python-version: "3.14"
36+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
2637
with:
27-
python-version: "3.12"
28-
cache: "pip"
29-
cache-dependency-path: "requirements-dev.txt"
30-
- uses: actions/setup-node@v4
38+
node-version: 22
39+
- name: Install uv
40+
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
3141
with:
32-
node-version: 20
42+
enable-cache: true
3343
- name: Install python dependencies
34-
run: |
35-
pip install uv
36-
uv venv venv
37-
source venv/bin/activate
38-
uv pip install -r requirements-dev.txt
44+
run: uv pip install -r requirements-dev.txt --system
3945
- name: Install npm dependencies
4046
run: npm install
41-
- uses: pre-commit/action@v3.0.1
47+
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
4248

4349
# Test job: (Run on all branch pushes)
4450
# - Checks out our branch
@@ -48,17 +54,17 @@ jobs:
4854
test:
4955
runs-on: ubuntu-latest
5056
steps:
51-
- uses: actions/checkout@v4
52-
- name: Set up Python 3.11
53-
uses: actions/setup-python@v5
57+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
58+
- name: Set up Python 3.14
59+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
60+
with:
61+
python-version: "3.14"
62+
- name: Install uv
63+
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
5464
with:
55-
python-version: "3.11"
56-
cache: "pip"
57-
cache-dependency-path: "requirements-dev.txt"
65+
enable-cache: true
5866
- name: Install dependencies
59-
run: |
60-
pip install uv
61-
uv pip install -r requirements-dev.txt --system
67+
run: uv pip install -r requirements-dev.txt --system
6268
- name: Test with pytest
6369
run: pytest --tb=line
6470

@@ -77,7 +83,7 @@ jobs:
7783
- test
7884
runs-on: ubuntu-latest
7985
steps:
80-
- uses: actions/checkout@v4
86+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
8187
with:
8288
ref: "release-branch"
8389
- name: Update Release Branch
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# Spec 0002: Update GitHub Actions Pipeline
2+
3+
## Status: Complete
4+
5+
---
6+
7+
## 1. Background & Motivation
8+
9+
The current `run-full-ci.yml` workflow has become outdated in several ways:
10+
11+
- Action versions are significantly behind latest releases
12+
- Python versions are wrong (test job uses 3.11, lint job uses 3.12; project requires ≥3.14)
13+
- uv is installed via `pip install uv` rather than the official `astral-sh/setup-uv` action
14+
- No uv package cache — each run reinstalls all packages from scratch
15+
- Node.js version 20 is behind the current LTS (Node 22)
16+
- No top-level `permissions` restriction (security best practice)
17+
- Inconsistent virtual environment strategy between `lint` and `test` jobs
18+
- No `concurrency` group to cancel redundant runs on the same branch
19+
20+
---
21+
22+
## 2. Research Findings
23+
24+
### 2.1 Action Version Inventory
25+
26+
| Action | Current | Latest | Notes |
27+
| ---------------------- | ------- | ---------- | ----------------------------------------- |
28+
| `actions/checkout` | v4 | **v6.0.2** | v5 and v6 both released since v4 |
29+
| `actions/setup-python` | v5 | **v6.2.0** | v6 is a breaking change (Node 24 runtime) |
30+
| `actions/setup-node` | v4 | **v6.4.0** | v5 and v6 both released since v4 |
31+
| `pre-commit/action` | v3.0.1 | **v3.0.1** | Already at latest |
32+
33+
### 2.2 Python Version Status (as of May 2026)
34+
35+
| Version | Status | Notes |
36+
| -------- | ------------------- | ----------------------------------------------------------------------------- |
37+
| 3.15 | Pre-release | Planned release October 2026 |
38+
| **3.14** | **Bugfix (active)** | Released October 7, 2025. Latest: 3.14.4 (April 7, 2026). **Project target.** |
39+
| 3.13 | Bugfix | Released October 7, 2024 |
40+
| 3.12 | Security-only | Released October 2, 2023 — used in current lint job |
41+
| 3.11 | Security-only | Released October 24, 2022 — used in current test job |
42+
43+
The project's `pyproject.toml` declares `requires-python = ">=3.14"`, and `ty.toml` sets `python-version = "3.14"`. Both the `lint` and `test` jobs must use **Python 3.14**.
44+
45+
### 2.3 Node.js LTS Status
46+
47+
Node.js 20 (used in the lint job) is in "maintenance LTS" as of May 2026. Node.js **22** is the current active LTS. The workflow should target Node 22.
48+
49+
### 2.4 uv GitHub Actions Best Practices
50+
51+
The official Astral documentation recommends using the `astral-sh/setup-uv` action instead of manually running `pip install uv`. Benefits:
52+
53+
- Automatically adds uv to PATH
54+
- Built-in uv package **cache** support (`enable-cache: true`) — avoids reinstalling packages on every run
55+
- Cross-platform support
56+
- Version pinning support
57+
- Eliminates the need for `pip install uv` + manual venv creation steps
58+
- Supports setting `UV_SYSTEM_PYTHON: 1` env var to install into the system Python (avoids needing to activate a venv in CI)
59+
60+
Example pattern (from uv docs):
61+
62+
```yaml
63+
- name: Install uv
64+
uses: astral-sh/setup-uv@v8
65+
with:
66+
enable-cache: true
67+
```
68+
69+
The latest version of `astral-sh/setup-uv` is **v8** (as of the uv 0.11.8 documentation).
70+
71+
### 2.5 GitHub Actions Security Best Practices
72+
73+
**Minimal permissions principle**: GitHub recommends setting a top-level `permissions: read-all` (or `permissions: {}`) to restrict the default `GITHUB_TOKEN` to read-only, then explicitly grant write permissions only to the specific jobs that need them. Currently only the `release` job has an explicit `permissions` block; the `lint` and `test` jobs implicitly have broad write permissions.
74+
75+
**Action pinning**: GitHub's security guidance recommends pinning actions to full commit SHAs (most secure) or at minimum to a specific major version tag. Using floating tags like `@v4` risks supply chain attacks if a tag is moved. Version tags from trusted/verified publishers (e.g., GitHub's own `actions/` org) are low-risk, but SHA pinning is the gold standard for high-security environments.
76+
77+
**Dependabot for actions**: GitHub recommends enabling Dependabot version updates for GitHub Actions to automatically keep action versions current via PRs.
78+
79+
**Concurrency groups**: Best practice is to add a `concurrency` block to cancel in-progress runs when a new push arrives on the same branch/ref, reducing wasted CI minutes.
80+
81+
### 2.6 Current Workflow Issues Summary
82+
83+
1. **Wrong Python versions**: lint uses 3.12, test uses 3.11; project requires ≥3.14
84+
2. **Outdated action versions**: checkout@v4, setup-python@v5, setup-node@v4
85+
3. **Manual uv install**: `pip install uv` is the old way; no cache means slow CI
86+
4. **Inconsistent venv strategy**: lint job creates and activates a venv; test job uses `--system`. Both should use the same approach.
87+
5. **Old Node.js version**: Node 20 is maintenance LTS; Node 22 is current LTS
88+
6. **No workflow-level `permissions`**: All jobs have implicit broad permissions
89+
7. **No concurrency group**: Redundant pushes waste CI minutes
90+
8. **No uv cache**: Package installation time is not amortized across runs
91+
92+
---
93+
94+
## 3. Update Plan
95+
96+
### Goal
97+
98+
Bring the CI pipeline up to date with current best practices: correct Python/Node versions, latest action versions, proper uv integration with caching, and improved security posture via permission restrictions and a concurrency group.
99+
100+
### Jobs to Update
101+
102+
#### 3.1 `lint` Job
103+
104+
- Update `actions/checkout` from `v4` → `v6`
105+
- Update `actions/setup-python` from `v5` → `v6`, change `python-version` from `"3.12"` → `"3.14"`
106+
- Update `actions/setup-node` from `v4` → `v6`, change `node-version` from `20` → `22`
107+
- Replace the manual `pip install uv` + `uv venv venv` + `source venv/bin/activate` + `uv pip install` pattern with `astral-sh/setup-uv@v8` with `enable-cache: true`
108+
- Use `uv pip install -r requirements-dev.txt --system` (consistent with test job, no venv activation needed)
109+
- `pre-commit/action@v3.0.1` stays as-is (already latest)
110+
111+
#### 3.2 `test` Job
112+
113+
- Update `actions/checkout` (implicit from `pre-commit/action`) — the test job itself doesn't use checkout directly, but add it for clarity and consistency
114+
- Actually: the test job _does_ use `actions/checkout@v4` (via the `uses: actions/checkout@v4` step) — update to `v6`
115+
- Update `actions/setup-python` from `v5` → `v6`, change `python-version` from `"3.11"` → `"3.14"`
116+
- Fix the job name label comment (currently says "Set up Python 3.11" — update to "3.14")
117+
- Replace `pip install uv` + `uv pip install --system` with `astral-sh/setup-uv@v8` + `uv pip install --system` (with cache enabled)
118+
119+
#### 3.3 `release` Job
120+
121+
- Update `actions/checkout` from `v4` → `v6`
122+
- No other changes needed (no Python/Node needed here)
123+
124+
### 3.4 Workflow-Level Changes
125+
126+
- Add `permissions: read-all` at the top of the workflow (before `jobs:`) to restrict the default `GITHUB_TOKEN` to read-only across all jobs. The `release` job already has its own `permissions: contents: write` block which continues to override this.
127+
128+
```yaml
129+
permissions: read-all
130+
```
131+
132+
- Add a `concurrency` block to cancel in-progress runs when a new push arrives on the same ref, reducing wasted CI minutes:
133+
134+
```yaml
135+
concurrency:
136+
group: ${{ github.workflow }}-${{ github.ref }}
137+
cancel-in-progress: true
138+
```
139+
140+
### 3.5 Action SHA Pinning
141+
142+
GitHub's security guidance recommends pinning third-party actions to their full commit SHA rather than a mutable version tag. This prevents supply chain attacks where a tag is silently moved to malicious code.
143+
144+
For each action used, look up the commit SHA corresponding to the target version tag and pin to it, keeping the version tag as an inline comment for readability:
145+
146+
```yaml
147+
# Example pattern
148+
uses: actions/checkout@<full-commit-sha> # v6.0.2
149+
```
150+
151+
All five actions used in this workflow require SHA pinning:
152+
153+
| Action | Target Version | Commit SHA |
154+
| ---------------------- | -------------- | ------------------------------------------ |
155+
| `actions/checkout` | v6.0.2 | `de0fac2e4500dabe0009e67214ff5f5447ce83dd` |
156+
| `actions/setup-python` | v6.2.0 | `a309ff8b426b58ec0e2a45f0f869d46889d02405` |
157+
| `actions/setup-node` | v6.4.0 | `48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e` |
158+
| `pre-commit/action` | v3.0.1 | `2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd` |
159+
| `astral-sh/setup-uv` | v8.1.0 | `08807647e7069bb48b6ef5acd8ec9567f424441b` |
160+
161+
SHAs were verified via the GitHub API (`/repos/{owner}/{repo}/git/ref/tags/{tag}`).
162+
163+
---
164+
165+
## 4. Reference: Action Latest Versions
166+
167+
| Action | Latest Version | URL |
168+
| ---------------------- | -------------- | -------------------------------------------------- |
169+
| `actions/checkout` | v6.0.2 | <https://github.com/actions/checkout/releases> |
170+
| `actions/setup-python` | v6.2.0 | <https://github.com/actions/setup-python/releases> |
171+
| `actions/setup-node` | v6.4.0 | <https://github.com/actions/setup-node/releases> |
172+
| `pre-commit/action` | v3.0.1 | <https://github.com/pre-commit/action/releases> |
173+
| `astral-sh/setup-uv` | v8 (v8.1.0) | <https://github.com/astral-sh/setup-uv/releases> |
174+
175+
---
176+
177+
## 5. Risk Assessment
178+
179+
| Change | Risk Level | Notes |
180+
| ------------------------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------- |
181+
| Python 3.11 → 3.14 | Medium | Project already targets 3.14, but CI may expose previously hidden 3.14-specific issues. Tests pass locally. |
182+
| `actions/checkout` v4 → v6 | Low | Breaking change in v6 is credentials storage location; doesn't affect this workflow's use case. |
183+
| `actions/setup-python` v5 → v6 | Low | v6 breaking change is Node 24 runtime upgrade — transparent to Python consumers. |
184+
| `actions/setup-node` v4 → v6 | Low | v6 breaking change limits automatic caching to npm only — our workflow doesn't rely on automatic caching. |
185+
| `astral-sh/setup-uv` adoption | Low | Well-maintained official action from Astral. Simplifies setup; `--system` flag usage unchanged. |
186+
| `permissions: read-all` | Very Low | Only affects `GITHUB_TOKEN` scope; this workflow doesn't use it in lint/test. Release job keeps its explicit override. |
187+
| Concurrency group | Very Low | Only cancels in-progress runs when a new push arrives. Does not affect final push results. |
188+
| Node 20 → 22 | Low | Used only for Tailwind CSS / npm tooling in pre-commit. No breaking changes expected. |
189+
| SHA pinning all actions | Very Low | Increases security; no functional change. SHAs must be verified against the correct release tags. |
190+
191+
---
192+
193+
## 6. Detailed Implementation Checklist
194+
195+
### Pre-work
196+
197+
- [x] Review the current workflow file end-to-end to catch any additional issues not covered in this spec
198+
- [x] Verify that Python 3.14 is available on `ubuntu-latest` GitHub-hosted runners (it is — GitHub Actions runners include Python 3.14 via `actions/setup-python@v6`)
199+
- [x] Verify that `astral-sh/setup-uv@v8` is the correct latest major version (confirmed from uv docs showing `v8.1.0` as latest)
200+
- [x] Confirm Node 22 is the current LTS (verified — Node 22 became LTS in October 2024)
201+
202+
### Workflow-Level Changes
203+
204+
- [x] Add top-level `permissions: read-all` block (before `jobs:`) to restrict default `GITHUB_TOKEN` scope
205+
- [x] Add `concurrency` block to cancel in-progress runs for the same `github.ref`
206+
207+
### `lint` Job
208+
209+
- [x] Update `actions/checkout` from `@v4` → `@v6`
210+
- [x] Update `actions/setup-python` from `@v5` → `@v6`
211+
- [x] Change `python-version` in setup-python from `"3.12"` → `"3.14"`
212+
- [x] Update `actions/setup-node` from `@v4` → `@v6`
213+
- [x] Change `node-version` in setup-node from `20` → `22`
214+
- [x] Replace the "Install python dependencies" step with `astral-sh/setup-uv@v8` with `enable-cache: true`
215+
- [x] Remove manual `pip install uv`, `uv venv venv`, and `source venv/bin/activate` commands
216+
- [x] Install python dependencies using `uv pip install -r requirements-dev.txt --system` (no venv needed)
217+
- [x] Verify `pre-commit/action@v3.0.1` remains unchanged (already latest)
218+
219+
### `test` Job
220+
221+
- [x] Update `actions/checkout` from `@v4` → `@v6`
222+
- [x] Update `actions/setup-python` step name label from `"Set up Python 3.11"` → `"Set up Python 3.14"` (cosmetic, but accurate)
223+
- [x] Update `actions/setup-python` from `@v5` → `@v6`
224+
- [x] Change `python-version` in setup-python from `"3.11"` → `"3.14"`
225+
- [x] Replace the "Install dependencies" step: swap `pip install uv` for `astral-sh/setup-uv@v8` with `enable-cache: true`
226+
- [x] Keep `uv pip install -r requirements-dev.txt --system` (the `--system` flag remains correct here)
227+
228+
### `release` Job
229+
230+
- [x] Update `actions/checkout` from `@v4` → `@v6`
231+
- [x] Verify `permissions: contents: write` is still present (it is — this overrides the new top-level `read-all`)
232+
- [x] No other changes needed
233+
234+
### SHA Pinning
235+
236+
- [x] Look up the commit SHA for `actions/checkout@v6.0.2` on GitHub and pin
237+
- [x] Look up the commit SHA for `actions/setup-python@v6.2.0` on GitHub and pin
238+
- [x] Look up the commit SHA for `actions/setup-node@v6.4.0` on GitHub and pin
239+
- [x] Look up the commit SHA for `pre-commit/action@v3.0.1` on GitHub and pin
240+
- [x] Confirm the SHA for `astral-sh/setup-uv@v8.1.0` (documented in uv docs as `08807647e7069bb48b6ef5acd8ec9567f424441b`) and pin
241+
- [x] Add the version tag as an inline comment on each pinned line, e.g. `@<sha> # v6.0.2`
242+
243+
### Post-work Validation
244+
245+
> **Note**: All git and remote-push validation steps are to be performed manually by the developer after implementation. No mutating git actions are performed by the agent.
246+
247+
- [ ] Run `pre-commit run --all-files` locally to confirm nothing broke
248+
- [ ] Commit and push to a non-main branch to trigger the CI pipeline
249+
- [ ] Verify all three jobs (`lint`, `test`, `release`) pass successfully
250+
- [ ] Confirm the correct Python version (3.14) is reported in CI logs
251+
- [ ] Confirm uv cache is populated and subsequent runs show cache hits
252+
- [ ] Confirm the `concurrency` group works by pushing two commits in quick succession

0 commit comments

Comments
 (0)