Repository navigation
457 lines (408 loc) · 19.6 KB
/
Copy pathcommunity.yml
File metadata and controls
457 lines (408 loc) · 19.6 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
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
name: Community
# Spec 0090 §1: beweist, dass das öffentliche Repo (dieser Workflow läuft
# aus einem frischen Klon, ohne Secrets) eigenständig baut — schlägt ein
# Schritt hier fehl, weil etwas außerhalb dieses Repositorys fehlt, ist das
# ein Fehler. Der Test-Job (Spec 0090 §2) umfasst die Rust-Schritte (A9),
# die Grenzprüfung (A10), die Drittlizenzen (A11) und das Frontend (A5).
# `push` nur auf `main`: Commits eines PR-Branches laufen über
# `pull_request`. Vorher lief jeder PR-Commit zweimal unter denselben
# Check-Namen, und ein roter Lauf von beiden blockierte den Merge (#26).
# `workflow_call`: `release.yml` ruft denselben Workflow als Gate vor dem
# Release-Build auf, damit die Prüfschritte nur an dieser Stelle stehen.
# Push-Läufe auf den langlebigen Branches des Versionszyklus: `main`
# (Live-Version), `develop` (nächste Version) sowie `release/*` und
# `hotfix/*`. Der Merge-Schritt wertet den letzten Push-Lauf des
# Basis-Branches aus, um einen roten Basis-Branch zu erkennen.
on:
push:
branches: [main, develop, "release/**", "hotfix/**"]
pull_request:
workflow_call:
# Ein neuer Push auf denselben PR bricht den noch laufenden Lauf ab.
# Jeder Push-Lauf (`main`, `develop`, `release/*`, `hotfix/*`) bekommt eine eigene Gruppe (`run_id`): Eine
# gemeinsame Gruppe würde einen wartenden `main`-Lauf verwerfen, sobald
# ein weiterer ansteht, auch ohne `cancel-in-progress`.
concurrency:
group: community-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
CARGO_TERM_COLOR: always
jobs:
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Cache cargo registry & build artifacts
uses: Swatinem/rust-cache@v2
# Nur auf Linux nötig: Tauri (genauer `wry`) linkt gegen WebKitGTK,
# ohne diese System-Pakete schlägt bereits `cargo build`/`clippy` für
# `apps/smart-ssh-community` fehl — dieselben Pakete wie in
# `release.yml`s Linux-Leg.
- name: Install Linux dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf \
libssl-dev \
libasound2-dev \
cmake \
libclang-dev \
xvfb
- name: cargo fmt --check
run: cargo fmt --all --check
- name: cargo clippy
run: cargo clippy --workspace --all-targets -- -D warnings
- name: cargo test
# Spec 0090, A9: ohne `--no-fail-fast` bricht der Schritt beim
# ersten scheiternden Test-Binary ab und ein einzelner Lauf zeigt
# nie alle betroffenen Crates auf einmal.
# Issue #358: the clipboard round-trip test needs a display; Linux
# runs under a virtual X server, and the test must not be skipped.
# Two steps instead of one `shell: bash` step: on Windows bash is Git
# Bash, whose perl cannot build the vendored OpenSSL (missing
# Locale::Maketext::Simple), so Windows keeps the default shell.
if: runner.os == 'Linux'
env:
SMART_SSH_REQUIRE_CLIPBOARD_TESTS: "1"
run: xvfb-run -a cargo test --workspace --no-fail-fast
- name: cargo test
if: runner.os != 'Linux'
env:
SMART_SSH_REQUIRE_CLIPBOARD_TESTS: "1"
run: cargo test --workspace --no-fail-fast
- name: cargo build
run: cargo build --workspace
# Spec 0084, A3/T4: `app-logic` (die Tauri-freie Anwendungslogik) darf
# `tauri`/`tauri-*`/`wry`/`tao`/`rfd` in ihrem gesamten
# Abhängigkeitsbaum nicht enthalten — weder direkt noch transitiv,
# weder normal noch als Build- oder Dev-Abhängigkeit, unter keinem
# Feature. `-p app-logic` (nicht an der Workspace-Wurzel, s. Spec 0084
# §6 T4): der Baum von `app-shell` selbst enthält `tauri` natürlich
# weiterhin. `--prefix none` liefert eine Paketzeile je Zeile
# (`name vX.Y.Z`) ohne die Baum-Zeichnung, damit der Regex-Anker `^`
# exakt den Paketnamen trifft, nicht irgendeine Teilzeichenkette davon
# (z. B. dürfte ein fiktives `not-tauri`-Paket hier nicht mit
# anschlagen).
- name: Grenzprüfung app-logic ist Tauri-frei (Spec 0084, A3)
shell: bash
run: |
# spec-reviewer-Fund (Teil 2): `cargo tree | grep` in einem
# `if`-Pipeline liesse den Schritt bei einem Fehlschlag von
# `cargo tree` selbst (kaputtes Lockfile, Feature-Konflikt) still
# gruen durchlaufen, weil `grep` (kein Treffer -> Exit 1) den
# Pipeline-Status dominiert. Deshalb: Baum zuerst in eine Variable,
# mit eigenem, explizitem Fehlerpfad.
tree_output="$(cargo tree -p app-logic -e normal,build,dev --all-features --prefix none)"
if [ $? -ne 0 ]; then
echo "::error::cargo tree -p app-logic ist fehlgeschlagen - Grenzpruefung (Spec 0084 A3) konnte nicht laufen"
exit 1
fi
if echo "$tree_output" | grep -E '^(tauri|tauri-[a-zA-Z0-9_-]*|wry|tao|rfd)( |$)'; then
echo "::error::app-logic haengt von tauri/tauri-*/wry/tao/rfd ab (Spec 0084, A3) - siehe Zeile(n) oben"
exit 1
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
cache-dependency-path: apps/smart-ssh-community/frontend/package-lock.json
- name: Install frontend dependencies
working-directory: apps/smart-ssh-community/frontend
run: npm ci
# Spec 0099, A3.1 (Spec 0090, A11): das Drittlizenz-Skript läuft bei
# jedem CI-Lauf auf **allen drei** Matrix-Plattformen, nicht nur auf
# einer — ein plattformspezifischer Fehler (etwa unter PowerShell)
# fällt so schon hier auf, nicht erst im Release-Lauf.
# Dieselbe feste Version wie in
# `scripts/generate-third-party-notices.mjs`
# (`EXPECTED_CARGO_ABOUT_VERSION`, A1.6) — ein Auseinanderlaufen fällt
# dort als Abbruch auf, nicht erst als stiller Unterschied.
- name: Install cargo-about
uses: taiki-e/install-action@v2
with:
tool: cargo-about@0.9.2
# Erzeugt `public/third-party-notices.txt`, damit die anschließenden
# Frontend-Tests (Spec 0099, A3.1) gegen eine echte Ausgabe statt
# nur gegen ihren eigenen Skip-Zweig laufen. Kein `continue-on-error`:
# ein Lizenz-/Werkzeug-Fehler hier färbt den Job rot (A1.3, A3.1).
- name: Generate third-party license notices
working-directory: apps/smart-ssh-community/frontend
run: npm run generate-notices
# Spec 0090, A5: oxlint findings of severity "error" fail the job.
# Spec 0100, A6.1: every `jsx-a11y` rule is configured as "error" in
# `.oxlintrc.json`, so accessibility findings fail the job too.
# Remaining warnings do not fail the job (oxlint's exit code already
# makes that distinction); see the `npm run lint` output for them.
- name: Frontend lint
working-directory: apps/smart-ssh-community/frontend
run: npm run lint
# Spec 0090, A5: ein fehlschlagender vitest-Fall färbt den Job rot.
- name: Frontend tests
working-directory: apps/smart-ssh-community/frontend
run: npm test
- name: Frontend build
working-directory: apps/smart-ssh-community/frontend
run: npm run build
# Spec 0090 §3: Dependency-Audit (cargo-audit, RustSec-Advisories) und
# Lizenz-/Herkunfts-Compliance (cargo-deny, A12/A13). Eigenständiger,
# nicht-Matrix-Job auf `ubuntu-latest` (das Ergebnis hängt nicht vom
# Betriebssystem ab, ein einzelner Lauf reicht).
#
# Spec 0090, A2: beide Schritte unten (`cargo deny check licenses sources
# bans` und `cargo audit`) sind Pflichtschritte ohne `continue-on-error` —
# ein Lizenz-, Herkunfts- oder Bann-Verstoß sowie jede nicht ausgenommene
# Sicherheitslücke färben diesen Job rot. Hinweise (unmaintained/unsound)
# und yanked-Crates bleiben sichtbare, nicht blockierende Warnungen (s.
# Kommentare bei den einzelnen Schritten und in `.cargo/audit.toml`).
dependency-audit:
name: Dependency Audit (cargo-deny / cargo-audit)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry & build artifacts
uses: Swatinem/rust-cache@v2
- name: Install cargo-deny and cargo-audit
uses: taiki-e/install-action@v2
with:
tool: cargo-deny,cargo-audit
# Advisories bleiben cargo-audits Aufgabe (Spec 0090, A3: Ausnahmen
# nur an einer Stelle) — dieser Aufruf prüft bewusst nur Lizenzen,
# Herkunft und Banne (A12, A13). Konfiguration: `deny.toml` im
# Repo-Root; bewusst freigegebene Fälle sind dort mit Begründung
# eingetragen (s. dortige Kommentare).
#
# Spec 0090, A2: dieser Schritt ist blockierend, kein
# `continue-on-error` — ein Lizenz-, Herkunfts- oder Bann-Verstoß
# färbt den Job rot.
- name: cargo deny check (licenses, sources, bans)
run: cargo deny check licenses sources bans
# Konfiguration: `.cargo/audit.toml` (der von cargo-audit selbst
# automatisch erkannte Pfad, s. dortiger Kommentar zum Schema/zur
# Pfad-Konvention).
- name: cargo audit
run: cargo audit
# Spec 0090, A4: eine Ausnahme in `.cargo/audit.toml`, die auf keinen
# Befund im aktuellen Lockfile mehr trifft (weil ein Advisory
# zurückgezogen wurde oder das betroffene Crate nicht mehr im Baum
# steht), soll auffallen statt unbemerkt stehen zu bleiben. Dazu läuft
# `cargo audit` hier ein zweites Mal, in einem frischen Verzeichnis
# ohne `.cargo/audit.toml` (also ohne die Ausnahmeliste), gegen
# dasselbe Lockfile — die JSON-Ausgabe zeigt dann jeden Befund, Lücken
# UND Hinweise. Jede in `.cargo/audit.toml` eingetragene ID, die darin
# nicht mehr vorkommt, ist eine überholte Ausnahme. Das Verbot aus A2
# (kein `continue-on-error`) gilt nur für die beiden Schritte oben,
# nicht für diesen Zwischenaufruf: `cargo audit` endet hier
# erwartungsgemäß mit Exit != 0 (RUSTSEC-2023-0071 hat keinen Fix) —
# das ist kein Fehler dieses Schritts, ausgewertet wird ausschließlich
# seine JSON-Ausgabe. Fehlt sie oder lässt sie sich nicht lesen, ist
# dieser Schritt rot.
- name: Audit-Ausnahmen gegen aktuelle Funde abgleichen (Spec 0090, A4)
shell: bash
run: |
# spec-reviewer-Fund (Runde 1): `shell: bash` läuft in GitHub
# Actions mit `-eo pipefail` (errexit). Ein einfaches
# `var=$(cmd)` reicht den Exit-Code von `cmd` an die Zuweisung
# weiter - und `cargo audit` OHNE Ausnahmeliste endet hier bei
# JEDEM Lauf mit Exit != 0 (RUSTSEC-2023-0071 hat keinen Fix,
# s. Kommentar oben). Ohne den `if !`-Kontext unten würde
# errexit die Shell an dieser Stelle sofort beenden, bevor die
# eigentliche Stale-Prüfung je läuft - der Schritt wäre dann
# immer rot, unabhängig vom Zustand der Ausnahmeliste. Ein
# `if`/`if !`-Kontext ist von errexit ausgenommen (POSIX/bash),
# deshalb dort geprüft, nie per separatem `$?` danach.
scratch="$(mktemp -d)"
cp Cargo.lock "$scratch/Cargo.lock"
json_output=""
if ! json_output="$(cd "$scratch" && cargo audit --json -f Cargo.lock)"; then
: # erwarteter Exit != 0, s. Kommentar oben - kein Fehler dieses Schritts
fi
if [ -z "$json_output" ]; then
echo "::error::cargo audit ohne Ausnahmeliste lieferte keine Ausgabe - Abgleich (Spec 0090 A4) konnte nicht laufen"
exit 1
fi
found_ids=""
if ! found_ids="$(printf '%s' "$json_output" | jq -r '
[ (.vulnerabilities.list[]?.advisory.id),
(.warnings[]?[]?.advisory.id // empty) ]
| unique[]
')"; then
echo "::error::JSON-Ausgabe von cargo audit liess sich nicht lesen - Abgleich (Spec 0090 A4) konnte nicht laufen"
exit 1
fi
# spec-reviewer-Fund (Runde 1, zurückgestellt->jetzt mitbehoben):
# nur der `ignore = [ ... ]`-Block wird durchsucht, nicht die
# gesamte Datei - ein künftiger Kommentar, der eine ID in
# Anführungszeichen zitiert (z. B. beim Dokumentieren einer
# entfernten Ausnahme), würde sonst fälschlich als aktive
# Ausnahme gezählt.
stale=0
while IFS= read -r id; do
[ -z "$id" ] && continue
if ! grep -qx "$id" <<< "$found_ids"; then
echo "::error::Ausnahme $id in .cargo/audit.toml trifft auf keinen aktuellen Befund mehr (Spec 0090, A4) - entfernen"
stale=1
fi
done < <(sed -n '/^ignore = \[/,/^\]/p' .cargo/audit.toml | grep -oE '"RUSTSEC-[0-9]{4}-[0-9]+"' | tr -d '"' | sort -u)
exit "$stale"
# Browser tests (issue #166, ADR 0120): Playwright runs the real frontend
# in Chromium and WebKit, in two window sizes, against a fake backend that
# replaces the Tauri IPC layer. A separate job, so the names of the
# existing checks stay unchanged. Linux only: the layout checks use
# geometry, not screenshots, and `--with-deps` installs the system
# packages through apt.
browser-tests:
name: Browser Tests (Playwright)
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/smart-ssh-community/frontend
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
cache-dependency-path: apps/smart-ssh-community/frontend/package-lock.json
- name: Install frontend dependencies
run: npm ci
# The browser builds depend only on the installed Playwright version,
# so that version is the cache key.
- name: Read Playwright version
id: playwright
run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright.outputs.version }}-chromium-webkit
- name: Install Playwright browsers (with system dependencies)
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: npx playwright install --with-deps chromium webkit
# The cache holds the browsers only, not the system packages.
- name: Install Playwright system dependencies
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: npx playwright install-deps chromium webkit
# With `CI` set, the config forbids `test.only`, starts its own dev
# server and writes the HTML report. Traces are kept for failed tests.
- name: Browser tests (Playwright)
run: npm run test:e2e
- name: Upload Playwright traces and report
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: playwright-traces
path: |
apps/smart-ssh-community/frontend/e2e/test-results/
apps/smart-ssh-community/frontend/e2e/playwright-report/
retention-days: 14
# Spec 0090 §2a (A14–A17): starts the real app through tauri-driver on a
# seeded data directory and checks the keychain, upgrade and password
# startup paths. Linux only; on pull requests only when a startup-relevant
# path changed.
real-app-changes:
name: Real app changes
runs-on: ubuntu-latest
outputs:
run: ${{ steps.filter.outputs.run }}
steps:
- name: Checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Decide whether the real-app job runs
id: filter
env:
EVENT: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
if [ "$EVENT" != "pull_request" ]; then
echo "run=true" >> "$GITHUB_OUTPUT"
exit 0
fi
git fetch --no-tags --depth=1 origin "$BASE_SHA" "$HEAD_SHA"
files="$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")"
if echo "$files" | grep -E '^(crates/app-shell/|crates/app-logic/src/database_startup|crates/app-logic/src/master_password|crates/real-app-tests/|apps/smart-ssh-community/frontend/src/main\.tsx$|apps/smart-ssh-community/frontend/src/.*StartupGate\.tsx$|\.github/workflows/community\.yml$)'; then
echo "run=true" >> "$GITHUB_OUTPUT"
else
echo "run=false" >> "$GITHUB_OUTPUT"
fi
real-app-tests:
name: Real app startup (tauri-driver)
needs: real-app-changes
if: needs.real-app-changes.outputs.run == 'true'
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- name: Cache cargo registry & build artifacts
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 20
cache: 'npm'
cache-dependency-path: apps/smart-ssh-community/frontend/package-lock.json
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf \
libssl-dev \
libasound2-dev \
cmake \
libclang-dev \
webkit2gtk-driver \
xvfb \
gnome-keyring \
dbus-x11
- name: Install tauri-driver
run: cargo install tauri-driver --locked
# The frontend has to be embedded: a plain debug build loads the dev URL.
- name: Build frontend
working-directory: apps/smart-ssh-community/frontend
run: |
npm ci
npm run build
- name: Build app (frontend embedded)
run: cargo build -p smart-ssh-community --features tauri/custom-protocol
- name: Real app startup tests
env:
SMART_SSH_REAL_APP_LOG_DIR: ${{ runner.temp }}/real-app-logs
run: |
xvfb-run -a dbus-run-session -- bash -c '
echo -n "ci-keyring-pw" | gnome-keyring-daemon --unlock --components=secrets
cargo test -p real-app-tests -- --ignored --test-threads=1'
- name: Upload app logs
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: real-app-logs
path: ${{ runner.temp }}/real-app-logs/
if-no-files-found: ignore
retention-days: 14