Repository navigation
274 lines (265 loc) · 12.2 KB
/
Copy pathci.yml
File metadata and controls
274 lines (265 loc) · 12.2 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
# CI — runs on every PR and push to the default branch.
#
# Fully hermetic: the build + E2E suites use the vendored docs-example fixture
# (tests/fixtures/docs-example.dist.tar.gz) via apps.json `prebuilt` entries, so
# no GITHUB_TOKEN, network, or sibling repo is required.
#
# Jobs:
# typecheck — astro check (TypeScript / Astro type errors)
# build — headless build from the vendored fixture; uploads dist/
# e2e — Playwright: embedded web-fragment harness + standalone layer
# image — docker build + container integration tests against real nginx + Trivy scan
# audit — npm dependency vulnerability gate
name: CI
on:
push:
branches: [master, main]
# Every pull request, whatever it targets. A base-branch filter here would
# skip each layer of a stacked PR except the bottom one, which is exactly the
# work that most needs checking before it reaches master.
pull_request:
# Least privilege: jobs only read the repo. Override per-job if more is needed.
permissions:
contents: read
jobs:
# ── 1. TypeScript / Astro type-check ───────────────────────────────────────
# Non-blocking: `astro check` is memory-hungry and can OOM on standard runners
# (tracked separately). The `build` job below is the hard compile gate — a real
# type/template error fails `astro build`. This job surfaces strict diagnostics
# without wedging CI on an OOM.
typecheck:
name: Type-check (non-blocking)
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '24'
cache: npm
- run: npm ci
- name: Astro type-check
run: npx astro check
env:
NODE_OPTIONS: --max-old-space-size=6144
# ── 2. Build (headless, hermetic) ──────────────────────────────────────────
build:
name: Build (headless)
runs-on: ubuntu-latest
needs: typecheck
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '24'
cache: npm
- run: npm ci
# Builds from the vendored fixtures — no token/network. setup-test-apps
# copies the real mermaid bundle into the single-page fixture (gitignored),
# so the container suite's diagram assertions have something to render.
- name: Build (headless)
run: node scripts/setup-test-apps.mjs && npm run build:headless
- name: Upload dist artifact
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ci-dist
path: dist/
retention-days: 7
# ── 3. Playwright E2E ──────────────────────────────────────────────────────
#
# Two layers, both hermetic (each webServer rebuilds from the fixture):
# • embedded (playwright.config.js) — full web-fragment harness: host
# gateway proxies/embeds the fragment, shadow-DOM isolation, SPA routing,
# cross-app nav, asset 404s, history limitation.
# • standalone (playwright.config.ci.js) — fragment server only: HTTP header
# safety, headless contract, CSS-link stability (#297), asset routing.
e2e:
name: E2E (Playwright)
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '24'
cache: npm
- run: npm ci
- name: Install Playwright browser
run: npx playwright install --with-deps chromium
- name: Embedded web-fragment tests
run: npm test
- name: Standalone fragment tests
run: npx playwright test --config=playwright.config.ci.js
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: playwright-report
path: playwright-report/
retention-days: 7
# ── 4. Publishing actions self-test ────────────────────────────────────────
#
# actions/ ships its own pinned dependency tree, so it is not covered by the
# root `npm ci` or by the Playwright suites (which stay hermetic and must not
# depend on the actions' node_modules). This job runs both actions end to end
# and pins their validation messages — those messages are the whole interface a
# docs repo has with the contract.
actions:
name: Publishing actions self-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
# Matches the node-version the composite action pins in action.yml.
node-version: '20'
cache: npm
cache-dependency-path: actions/package-lock.json
- run: npm ci
working-directory: actions
# Action manifests are only parsed by *consuming* repositories' runners, so
# a syntax error here ships green and breaks every downstream workflow at
# "Set up job" (#39). Parse them with the same pinned `yaml` package the
# action already depends on, so this needs no extra tooling.
- name: Validate action manifests
working-directory: actions
run: |
node -e '
const fs = require("node:fs");
const path = require("node:path");
const YAML = require("yaml");
const root = path.resolve("..");
const files = [];
(function walk(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
if (entry.name === "node_modules") continue;
const full = path.join(dir, entry.name);
if (entry.isDirectory()) walk(full);
else if (/\.ya?ml$/.test(entry.name)) files.push(full);
}
})(path.join(root, "actions"));
if (files.length === 0) {
console.error("no action manifests found under actions/ — check this glob");
process.exit(1);
}
let failed = 0;
for (const file of files) {
const name = path.relative(root, file);
try {
YAML.parse(fs.readFileSync(file, "utf8"));
console.log("ok " + name);
} catch (error) {
failed += 1;
console.error("FAILED " + name + ": " + error.message);
}
}
process.exit(failed === 0 ? 0 : 1);
'
- run: npm run selftest
working-directory: actions
# The self-tests call check-docs' entry point directly; this runs the
# composite action itself, the way a docs repo's pull request does, on a
# fixture that must come out clean.
- name: Run check-docs as a docs repo would
id: check-docs
uses: ./actions/check-docs
with:
manifest: tests/fixtures/single-page-bundle/kb-docs.json
dist: tests/fixtures/single-page-bundle
strict: 'true'
- name: check-docs reported no findings
env:
KB_ERRORS: ${{ steps.check-docs.outputs.errors }}
KB_WARNINGS: ${{ steps.check-docs.outputs.warnings }}
run: test "$KB_ERRORS" = 0 && test "$KB_WARNINGS" = 0
# ── 5. Deployment workflow dry run ─────────────────────────────────────────
#
# build-image.yml is consumed by a private deployment repo, so a break in it
# would surface there rather than here — after the change had already merged.
# This calls it the way that repo does, with no image-name, so it builds from
# a registry and pushes nothing.
#
# Not strict: this repo's registry is the vendored fixture, which strict mode
# rejects by design. tests/deployment.spec.js covers what strict refuses.
deployment-workflow:
name: Deployment workflow dry run
uses: ./.github/workflows/build-image.yml
with:
# This commit, not the default branch. The workflow checks the knowledge
# base out by ref, and without this the dry run would build master and
# pass while the change under review was broken.
kb-ref: ${{ github.sha }}
registry: apps.json
strict: false
# The JSON form, so the branch of the runs-on expression that parses it
# runs here; the bare-label default is what every other caller exercises.
runs-on: '["ubuntu-latest"]'
# ── 5. Container image ─────────────────────────────────────────────────────
#
# Builds the runtime image from the dist/ the build job produced, then scans
# it. `npm audit` below covers JS dependencies only — nothing else in CI looks
# at the nginx base image, which is what the digest pin in the Dockerfile
# exists to control. CRITICAL-only so a routine base-image CVE does not block
# unrelated PRs; the fix is to bump the pinned digest.
image:
name: Image build + integration + scan
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '24'
cache: npm
- run: npm ci
- name: Download dist artifact
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: ci-dist
path: dist
# Also proves the Dockerfile's dist/ sanity checks pass on a real build.
- name: Build image
run: docker build -t knowledge-base:ci .
# The CSP checks in the container suite need a real browser: a policy that
# blocks something the page needs fails silently, and only a browser
# reports the violation.
- name: Install Playwright browser
run: npx playwright install --with-deps chromium
# The only place nginx.conf itself is executed. The other suites run
# against tests/fragment-server.mjs, an Express mirror of the rewrites —
# see playwright.config.docker.js for why that is not sufficient.
# KB_SKIP_BUILD reuses the image built above instead of building twice.
- name: Container integration tests
run: npm run test:container
env:
KB_IMAGE: knowledge-base:ci
KB_SKIP_BUILD: 'true'
- name: Scan image
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: knowledge-base:ci
format: table
exit-code: '1'
ignore-unfixed: true
severity: CRITICAL
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: playwright-report-container
path: playwright-report/
retention-days: 7
# ── 6. Dependency audit ────────────────────────────────────────────────────
audit:
name: npm audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '24'
cache: npm
- run: npm ci
# Fail only on production-dependency vulnerabilities (high or above).
- name: Audit production dependencies
run: npm audit --omit=dev --audit-level=high