-
Notifications
You must be signed in to change notification settings - Fork 14
260 lines (225 loc) · 10.3 KB
/
Copy pathci.yml
File metadata and controls
260 lines (225 loc) · 10.3 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
name: CI
on:
# Gate PRs, and the mainline itself after merge. Feature-branch pushes are
# covered by their PR run, so this avoids the push+pull_request double-run.
push:
branches: [master]
pull_request:
workflow_dispatch:
# Least privilege: CI only needs to read the checkout. Overrides the repo
# default token scope so a compromised action can't write to the repo.
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
# Cancel superseded PR runs, but let every master run finish so the
# mainline history has no coverage gaps between rapid merges.
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
env:
CARGO_TERM_COLOR: always
# The `--tests` gate links 100+ integration-test binaries; with debug
# symbols (the bulk of a debug binary) that `target/` exhausts the runner's
# disk ("No space left on device"). Debug symbols are useless in CI, so drop
# them — much smaller target/, faster linking, identical test behavior.
CARGO_PROFILE_DEV_DEBUG: "0"
CARGO_PROFILE_TEST_DEBUG: "0"
# Runners ship tmux, so `require_tmux_or_skip` would RUN the TUI snapshot
# tests — which are terminal-/timing-fragile in CI and fail deterministically.
# Force them to skip; they still run locally for anyone who wants them.
PUFFER_SKIP_TMUX_TESTS: "1"
# The Ubuntu mirror intermittently accepts a connection then stops sending.
# apt has no read timeout by default, so it hangs until the job's
# timeout-minutes kills it (seen on the 25 MB libwebkit2gtk-4.1-0 fetch).
# Timeout turns the hang into a failure; Retries then re-fetches it.
APT_RESILIENCE: -o Acquire::http::Timeout=30 -o Acquire::Retries=3
jobs:
rust:
name: Rust (build + test)
runs-on: ubuntu-latest
# Safety net: the workspace suite runs in ~3 min; a stuck test (e.g. a
# blocked global-singleton channel) must not burn the 6h GitHub default.
timeout-minutes: 25
steps:
- uses: actions/checkout@v7
- name: Install system dependencies
run: |
sudo apt-get $APT_RESILIENCE update
sudo apt-get $APT_RESILIENCE install -y pkg-config libssl-dev
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
# Only master writes to the 10GB Actions cache quota; PR branches
# read it but don't evict the mainline baseline with per-PR entries.
save-if: ${{ github.ref == 'refs/heads/master' }}
- uses: taiki-e/install-action@nextest
# --- Hard gate: compile the whole workspace including test, integration,
# example, and bench targets (`--all-targets`), so a compile break outside
# production code (e.g. in a `crates/*/tests/` file) fails CI. This is
# also the only step that truly LINKS every target; nextest and clippy
# below share its artifacts, so it costs little beyond that link. ---
- name: Build
run: cargo build --workspace --all-targets --locked
# --- Hard gate: the whole-workspace test suite via nextest (process-
# per-test isolation, ~40% faster in CI than cargo test, and the `ci`
# profile in .config/nextest.toml kills any single wedged test instead
# of letting it stall the job). Runs lib + bin unit tests (incl. the
# bin-only `puffer-cli`) AND the integration test targets under
# `crates/*/tests/`. Runs in parallel — `ConfigPaths::discover` isolates
# `user_config_dir` under the OS temp dir, so no race on the real
# ~/.puffer.
#
# Integration tests that need infra CI does not provide self-exclude:
# tmux TUI tests are skipped via PUFFER_SKIP_TMUX_TESTS, and the few
# needing the workflow-runtime Docker image / a real browser are
# `#[ignore]`d. Run the full set locally with tmux installed. ---
- name: Test (nextest)
run: cargo nextest run --workspace --locked --profile ci
# nextest cannot run doctests; without this step any future doctest
# would silently never execute in CI. Currently 0 doctests, ~30s warm.
- name: Doc tests
run: cargo test --workspace --doc --locked
# --- Hard gate: formatting drift was settled in one workspace-wide
# rustfmt commit; from here on any drift fails fast. ---
- name: Rustfmt
run: cargo fmt --all --check
# --- Hard gate: `correctness` flags real defects (bad comparisons,
# ignored #[must_use], always-true/false conditions) and `suspicious`
# catches bug-adjacent patterns like MutexGuard held across await.
# Style/pedantic warnings stay informational and never block. ---
- name: Clippy (correctness + suspicious gate)
run: cargo clippy --workspace --all-targets --locked -- -D clippy::correctness -D clippy::suspicious
desktop:
name: Desktop (build + check)
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: apps/puffer-desktop
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
# Single source of truth for the Node version (24 = Active LTS
# until 2028-04; Node 20 went EOL 2026-04-30).
node-version-file: apps/puffer-desktop/.nvmrc
cache: npm
cache-dependency-path: apps/puffer-desktop/package-lock.json
- name: Install deps
run: npm ci
# --- Hard gates: all currently green, so the desktop job is now a real
# gate (previously every step was informational and could never fail). ---
- name: Build (vite)
run: npm run build
- name: svelte-check
run: npm run check
# Pure-node config/asset assertions (no browser). The Playwright UI
# suites run in the separate desktop-ui job.
- name: Node tests
run: |
npm run test:tauri-config
npm run test:sidebar-css
npm run test:provider-visuals
# Detects whether a PR touches the desktop app so desktop-ui can skip on
# Rust-only PRs. Master pushes always run the suite (no mainline gaps).
# Plain `gh api` instead of a third-party paths-filter action: one fewer
# supply-chain dependency and no Node-runtime deprecation churn.
desktop-changes:
name: Desktop change filter
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
pull-requests: read
outputs:
desktop: ${{ steps.filter.outputs.desktop }}
steps:
- name: List PR files and match desktop paths
id: filter
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
if [ "${{ github.event_name }}" != "pull_request" ]; then
echo "desktop=true" >> "$GITHUB_OUTPUT"
exit 0
fi
files=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" \
--paginate --jq '.[].filename')
if echo "$files" | grep -qE '^(apps/puffer-desktop/|\.github/workflows/ci\.yml$)'; then
echo "desktop=true" >> "$GITHUB_OUTPUT"
else
echo "desktop=false" >> "$GITHUB_OUTPUT"
fi
desktop-ui:
name: Desktop UI (playwright ${{ matrix.shard }}/2)
runs-on: ubuntu-latest
timeout-minutes: 20
needs: desktop-changes
if: github.event_name != 'pull_request' || needs.desktop-changes.outputs.desktop == 'true'
strategy:
# One shard's failure should not cancel the other: both halves' results
# tell you whether a red is isolated or systemic.
fail-fast: false
matrix:
shard: [1, 2]
defaults:
run:
working-directory: apps/puffer-desktop
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version-file: apps/puffer-desktop/.nvmrc
cache: npm
cache-dependency-path: apps/puffer-desktop/package-lock.json
- name: Install deps
run: npm ci
- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium
# Full mocked-daemon UI suite, split file-wise into two parallel shards
# (~4-5 min each vs ~9 min unsharded on the 2-core runners). Playwright
# starts its own vite webServer; specs run against FakeDaemon.
# NOTE: if tests/visual/ gains macOS-only screenshot baselines, exclude
# it here (see AGENTS.md — visual baselines diverge on ubuntu runners).
- name: Playwright UI suite (shard ${{ matrix.shard }}/2)
run: npx playwright test --shard=${{ matrix.shard }}/2
- name: Upload failure artifacts
if: failure()
uses: actions/upload-artifact@v7
with:
name: playwright-test-results-shard-${{ matrix.shard }}
path: apps/puffer-desktop/test-results/
retention-days: 7
desktop-backend:
name: Desktop backend (test + clippy)
runs-on: ubuntu-latest
# `apps/puffer-desktop/src-tauri` is a separate Cargo workspace that the
# root `cargo build --workspace` skips. Its 100+ unit tests were never
# run in CI (a plain `cargo check` doesn't even compile #[cfg(test)]
# code) — one of them rotted unnoticed. Runs in parallel with the other
# jobs, so this full gate has no critical-path cost.
timeout-minutes: 20
steps:
- uses: actions/checkout@v7
- name: Install system dependencies
run: |
sudo apt-get $APT_RESILIENCE update
sudo apt-get $APT_RESILIENCE install -y pkg-config libssl-dev libwebkit2gtk-4.1-dev \
libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
workspaces: apps/puffer-desktop/src-tauri
save-if: ${{ github.ref == 'refs/heads/master' }}
- uses: taiki-e/install-action@nextest
- name: Test (nextest, src-tauri)
run: cargo nextest run --manifest-path apps/puffer-desktop/src-tauri/Cargo.toml --locked --profile ci
- name: Rustfmt (src-tauri)
run: cargo fmt --all --check --manifest-path apps/puffer-desktop/src-tauri/Cargo.toml
# Same deny groups as the root workspace so both codebases meet one bar.
- name: Clippy (correctness + suspicious gate)
run: cargo clippy --manifest-path apps/puffer-desktop/src-tauri/Cargo.toml --all-targets --locked -- -D clippy::correctness -D clippy::suspicious