-
Notifications
You must be signed in to change notification settings - Fork 0
356 lines (334 loc) · 14.7 KB
/
Copy pathci.yml
File metadata and controls
356 lines (334 loc) · 14.7 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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
name: CI
on:
push:
# `renovate/**` is here because Renovate is configured for branch-based
# automerge (`:automergeBranch` in renovate.json5): it pushes the update to
# a branch and fast-forwards main once that branch's checks pass, WITHOUT
# ever opening a pull request. A push to such a branch matches neither
# `branches: [main]` nor `pull_request`, so before this entry those
# branches carried zero check runs — the only status on them was Renovate's
# own `renovate/stability-days` soak timer. That made the "CI is the gate"
# premise documented in renovate.json5 vacuous: an update could reach main
# having been verified by nothing but the 3-day wait.
branches: [main, "renovate/**"]
pull_request:
# Least privilege: CI only needs to read the repo.
permissions:
contents: read
# Cancel superseded runs on the same ref to save CI minutes.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# --- Action pinning policy -------------------------------------------------
# The toolchain (Rust, Node, pnpm) is installed by mise from the pinned
# versions in .mise/config.toml + .mise/mise.lock, via `jdx/mise-action`, which
# is pinned to a full commit SHA. The remaining third-party actions use
# major-version tags for readability; for a hardened setup, pin each to a full
# commit SHA (e.g. `actions/checkout@b4ffde6... # v4.2.2`) and let
# Renovate/Dependabot bump them. The `# vX` comments mark the intended pin.
# ---------------------------------------------------------------------------
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
fmt:
name: rustfmt
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5
with:
install_args: rust
# Every gate below is a mise task, so `mise run <task>` locally runs the
# exact same command as CI (`mise run ci` runs the whole matrix).
- run: mise run fmt-check
clippy:
name: clippy
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5
with:
install_args: rust
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 # pin to SHA
- run: mise run clippy
test:
name: test
runs-on: ubuntu-24.04
# The suite runs in seconds; anything approaching this is a hang. Bounded
# for the same reason as the `web` job below, and more sharply needed here:
# these tests spawn subprocesses and bind sockets, and a stub waiting on a
# request that never arrives blocks until its own deadline with no output.
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5
with:
install_args: rust
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 # pin to SHA
with:
key: test
- run: mise run test
test-pg:
name: test-pg
runs-on: ubuntu-24.04
# Same suite as `test`, against the Postgres backend. The runner's Docker
# hosts the database; a per-test CREATE DATABASE keeps tests isolated.
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5
with:
install_args: rust
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 # pin to SHA
with:
key: test-pg
- run: mise run test-pg
saml:
name: saml (feature-gated tests)
runs-on: ubuntu-24.04
# rust:bookworm brings its own toolchain + the apt packages samael's
# xmlsec backend needs at build time; mise is not used here.
container: rust:1-bookworm
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- name: Install xmlsec build dependencies
run: |
apt-get update
apt-get install -y --no-install-recommends \
libxmlsec1-dev libxml2-dev libxslt1-dev libssl-dev libltdl-dev \
pkg-config clang
# The rust:bookworm image ships rustc/cargo but not the clippy
# component; the other CI jobs get it via mise-action.
- run: rustup component add clippy
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 # pin to SHA
with:
key: saml
- run: cargo clippy -p domarinn-server --features saml --all-targets -- -D warnings
- run: cargo test -p domarinn-server --features saml
commit-messages:
name: commit messages parse for release-please
# A message release-please cannot parse is SKIPPED by it rather than
# reported, so the work it carries never reaches a changelog — and when it
# is the only commit since the last release, no release pull request is
# created at all. This is the gate; the lefthook commit-msg hook is the
# same checker run earlier, and skips when node deps are absent.
# Also runs on Renovate's automerge branches: those fast-forward onto main
# without ever being a pull request, so this is the only point at which
# their messages can be checked. Skipped on push to main itself — by then
# the commit has landed and there is nothing left to gate.
if: github.event_name == 'pull_request' || startsWith(github.ref, 'refs/heads/renovate/')
runs-on: ubuntu-24.04
timeout-minutes: 5
env:
MISE_DISABLE_TOOLS: rust
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
# Every commit in the pull request, not just the tip.
fetch-depth: 0
- uses: ./.github/actions/setup-web
# Guard on the guard: if the checker itself is wrong, the messages it
# blesses mean nothing.
# Both invocation paths on purpose: the first failure of this job was the
# pnpm wrapper forwarding a bare `--` as the filename, not the checker.
- name: Self-test the checker
run: |
node web/scripts/check-commit-msg.mjs --self-test
pnpm -C web run check-commit-msg -- --self-test
- name: Check every commit in this change
env:
# Both are empty on a push event — there is no pull_request payload.
BASE: ${{ github.event.pull_request.base.sha }}
HEAD: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
if [ -z "${BASE:-}" ]; then
# Renovate automerge branch: derive the range from main instead.
# `origin/main` is present because checkout above uses
# fetch-depth: 0, which fetches every branch. If it is somehow
# missing this fails loudly rather than checking an empty range —
# a gate that silently passes is worse than no gate.
BASE="$(git merge-base origin/main HEAD)"
HEAD="$(git rev-parse HEAD)"
fi
failed=0
msg="$(mktemp)"
for sha in $(git rev-list "$BASE..$HEAD"); do
git log -1 --format=%B "$sha" > "$msg"
if ! node web/scripts/check-commit-msg.mjs "$msg"; then
echo "::error::commit $sha has a message release-please cannot parse"
failed=1
fi
done
exit "$failed"
web-lint:
name: web lint
runs-on: ubuntu-24.04
timeout-minutes: 10
env:
# `mise run` auto-installs every tool in .mise/config.toml, which would
# pull in the Rust toolchain this job never uses — skip it.
MISE_DISABLE_TOOLS: rust
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: ./.github/actions/setup-web
# Type-checked lint (--max-warnings=0).
- run: mise run web-lint
web-build:
name: web build (typecheck)
runs-on: ubuntu-24.04
timeout-minutes: 10
env:
MISE_DISABLE_TOOLS: rust
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: ./.github/actions/setup-web
# This IS the typecheck gate: the task runs `tsc -b` before vite, so any
# type error fails here rather than in a separate step.
- run: mise run web-build
web-test:
name: web unit tests (vitest)
runs-on: ubuntu-24.04
# The suite takes seconds; anything approaching this is a hang, not
# slowness. Without a bound a wedged run burned ~14 minutes and still
# produced no logs, which is the worst of both — slow *and* undiagnosable.
timeout-minutes: 10
env:
MISE_DISABLE_TOOLS: rust
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: ./.github/actions/setup-web
- run: mise run web-test
e2e:
name: web e2e (playwright)
runs-on: ubuntu-24.04
# The suite itself is fast; the budget is dominated by the chromium
# download. Bounded for the same reason as the `web` job — a wedged
# browser launch produces no logs, so it must fail rather than hang.
timeout-minutes: 15
env:
# Playwright drives a mock build (`VITE_MOCK=1`), served by the config's
# own `webServer`. No Rust backend is involved, so skip that toolchain.
MISE_DISABLE_TOOLS: rust
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: ./.github/actions/setup-web
# Only chromium: playwright.config.ts declares a single chromium project.
# `--with-deps` pulls the system shared libraries the CDN build needs.
- name: Install Playwright chromium
run: pnpm -C web exec playwright install --with-deps chromium
- run: mise run e2e
# The HTML report is the only way to diagnose a failure after the fact —
# the trace/screenshot artifacts are worthless if they stay on the runner.
- if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 # pin to SHA
with:
name: playwright-report
path: web/playwright-report/
retention-days: 7
schema-check:
name: schema is up to date
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5
with:
install_args: rust
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 # pin to SHA
# Regenerates the schema and diffs it against the committed copy; the
# full logic lives in the `schema-check` task (.mise/config.toml) so the
# local run and CI can never drift apart.
- name: Fail on schema drift
run: mise run schema-check
gen-types-check:
name: generated TS types are up to date
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5
with:
install_args: rust
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 # pin to SHA
# Regenerates the TypeScript DTOs and fails on any drift (including
# untracked new files); the full logic lives in the `gen-types-check`
# task (.mise/config.toml) so the local run and CI can never drift apart.
- name: Fail on generated-type drift
run: mise run gen-types-check
workflow-lint:
name: workflows pass zizmor
runs-on: ubuntu-24.04
# .lefthook.toml runs zizmor on staged workflow files pre-commit, but a
# fork PR never runs our hooks — on a public repo this job is the only
# thing standing between a malicious workflow edit and a merge.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5
with:
install_args: zizmor
- run: mise run workflow-lint
musl-build:
name: static musl build (${{ matrix.target }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
# Both shipped targets build natively on a GitHub-hosted runner, so CI
# proves the static link for each one release.yml will publish. No
# cross-compilation and no QEMU: the aarch64 leg is a real arm64 host.
include:
- target: x86_64-unknown-linux-musl
runner: ubuntu-24.04
- target: aarch64-unknown-linux-musl
runner: ubuntu-24.04-arm
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # pin to SHA
with:
persist-credentials: false
- uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5
with:
install_args: rust
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 # pin to SHA
with:
key: ${{ matrix.target }}
# musl-tools gives the C toolchain rusqlite's bundled SQLite (and the
# rustls crypto backend) need; cmake is a hedge for crypto backends.
- name: Install musl toolchain
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends musl-tools cmake
# The `musl-build` task adds the rustup target itself (mise's rust
# delegates to rustup) and then builds; same command locally and in CI.
# MUSL_TARGET defaults to the x86_64 triple, so a bare `mise run
# musl-build` on a dev machine keeps working unchanged.
- name: Build
env:
MUSL_TARGET: ${{ matrix.target }}
run: mise run musl-build
- name: Confirm the binary is static
run: file target/${{ matrix.target }}/release/domarinn || true