Skip to content

Commit ccbf422

Browse files
author
zengfr
committed
fix(governance): use absolute upload paths + env-based out dir
Fixes 4 issues discovered by the first governance workflow run (run 33968073019), addressing A10 verification: 1. perf-budget artifact upload used `website/.perf-budget.json` (rel), but action cwd was repo root and globbing silently missed the file. Switch all 3 uploads to `${{ github.workspace }}/...`. 2. Lighthouse configPath `./.lighthouserc.cjs` did not exist at repo root (it lives in website/). Point to `website/.lighthouserc.cjs`. 3. a11y tests could not find `website/out` because Playwright cwd is `tests/`, not repo root. static-server.ts now walks up to find the output directory, and accepts `WEBSITE_OUT_DIR` env var as override. governance.yml exports `WEBSITE_OUT_DIR` for deterministic paths. These are wiring fixes, not threshold changes.
1 parent bad28c8 commit ccbf422

2 files changed

Lines changed: 31 additions & 9 deletions

File tree

‎.github/workflows/governance.yml‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
uses: actions/upload-artifact@v4
5454
with:
5555
name: perf-budget
56-
path: website/.perf-budget.json
56+
path: ${{ github.workspace }}/website/.perf-budget.json
5757
retention-days: 14
5858

5959
# ---- Soft gate: Lighthouse audit (homepage categories). ----
@@ -80,7 +80,7 @@ jobs:
8080
- name: Run Lighthouse CI
8181
uses: treosh/lighthouse-ci-action@v9
8282
with:
83-
configPath: ./.lighthouserc.cjs
83+
configPath: website/.lighthouserc.cjs
8484
uploadArtifacts: true
8585
temporaryPublicStorage: true
8686

@@ -111,13 +111,15 @@ jobs:
111111
- name: Run axe a11y
112112
working-directory: tests
113113
run: npm run test:a11y
114+
env:
115+
WEBSITE_OUT_DIR: ${{ github.workspace }}/website/out
114116

115117
- name: Upload a11y logs
116118
if: always()
117119
uses: actions/upload-artifact@v4
118120
with:
119121
name: a11y-logs
120-
path: tests/test-results/
122+
path: ${{ github.workspace }}/tests/test-results/
121123
retention-days: 14
122124

123125
# ---- Soft gate: regenerate Go API docs and upload artifact. ----
@@ -144,7 +146,7 @@ jobs:
144146
uses: actions/upload-artifact@v4
145147
with:
146148
name: godocgen
147-
path: website/content/docs/api/
149+
path: ${{ github.workspace }}/website/content/docs/api/
148150
retention-days: 14
149151

150152
# ---- Summary job: aggregates results in the GitHub Actions UI. ----
@@ -162,3 +164,8 @@ jobs:
162164
echo "- lighthouse: ${{ needs.lighthouse.result }}" >> "$GITHUB_STEP_SUMMARY"
163165
echo "- a11y: ${{ needs.a11y.result }}" >> "$GITHUB_STEP_SUMMARY"
164166
echo "- godocgen: ${{ needs.godocgen.result }}" >> "$GITHUB_STEP_SUMMARY"
167+
168+
169+
170+
171+

‎tests/a11y/fixtures/static-server.ts‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
// fixtures/static-server.ts - serves ./website/out on an ephemeral port.
22
//
33
// Used by Playwright + axe-core to crawl the static export without
4-
// spinning up a real Next.js server.
4+
// spinning up a real Next.js server. Resolves the output directory
5+
// by walking up from cwd, so the suite works whether run from
6+
// repo root or from tests/.
57

68
import { createServer, type Server } from 'node:http';
79
import { readFile, stat } from 'node:fs/promises';
8-
import { extname, join, normalize } from 'node:path';
10+
import { existsSync } from 'node:fs';
11+
import { extname, join, normalize, dirname } from 'node:path';
912

1013
export type StaticServer = {
1114
url: string;
@@ -29,8 +32,20 @@ const MIME: Record<string, string> = {
2932
'.xml': 'application/xml; charset=utf-8',
3033
};
3134

35+
function resolveOutDir(): string {
36+
if (process.env.WEBSITE_OUT_DIR) return normalize(process.env.WEBSITE_OUT_DIR);
37+
let dir = process.cwd();
38+
for (let i = 0; i < 6; i++) {
39+
const candidate = normalize(join(dir, 'website/out'));
40+
if (existsSync(candidate)) return candidate;
41+
const parent = dirname(dir);
42+
if (parent === dir) break;
43+
dir = parent;
44+
}
45+
return normalize(join(process.cwd(), 'website/out'));
46+
}
47+
3248
async function resolveFile(rootDir: string, urlPath: string): Promise<string | null> {
33-
// Map "/" -> "/index.html"; "/en/" -> "/en/index.html"; etc.
3449
const cleaned = urlPath.split('?')[0].split('#')[0];
3550
const candidates = [cleaned, join(cleaned, 'index.html')];
3651
for (const c of candidates) {
@@ -47,7 +62,7 @@ async function resolveFile(rootDir: string, urlPath: string): Promise<string | n
4762
}
4863

4964
export async function startStaticServer(): Promise<StaticServer> {
50-
const rootDir = normalize(join(process.cwd(), 'website/out'));
65+
const rootDir = resolveOutDir();
5166
const server: Server = createServer(async (req, res) => {
5267
try {
5368
const urlPath = req.url ?? '/';
@@ -71,7 +86,7 @@ export async function startStaticServer(): Promise<StaticServer> {
7186
if (!addr || typeof addr === 'string') throw new Error('static-server: bad address');
7287

7388
return {
74-
url: `http://127.0.0.1:${addr.port}`,
89+
url: 'http://127.0.0.1:' + addr.port,
7590
close: () =>
7691
new Promise<void>((resolve, reject) =>
7792
server.close((err) => (err ? reject(err) : resolve())),

0 commit comments

Comments
 (0)