Skip to content

Commit 91910a1

Browse files
authored
Merge branch 'main' into feat/analytics-and-playwright
2 parents 06bfbdb + f69e28d commit 91910a1

98 files changed

Lines changed: 4642 additions & 739 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Copy this file to .env and fill in values.
22
# .env is gitignored — never commit secrets.
3+
#
4+
# CONVENTION:
5+
# NEXT_PUBLIC_* → safe to expose in the browser bundle
6+
# (no prefix) → server-only; NEVER import in client components
7+
# use `import '@/lib/server-guard'` to enforce this
38

49
# ── Redis ─────────────────────────────────────────────────────────────────────
510
REDIS_HOST=127.0.0.1

.github/workflows/ci.yml

Lines changed: 132 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,44 @@ name: CI
22

33
on:
44
push:
5-
branches: [main, "feat/**", "fix/**"]
5+
branches:
6+
- main
67
pull_request:
8+
branches:
9+
- main
710

8-
env:
9-
CARGO_TERM_COLOR: always
11+
permissions:
12+
contents: read
1013

1114
jobs:
15+
# ── Frontend quality gate ─────────────────────────────────────────────────
16+
frontend:
17+
name: Frontend (lint → typecheck → build)
18+
runs-on: ubuntu-latest
19+
defaults:
20+
run:
21+
working-directory: frontend
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- uses: actions/setup-node@v4
26+
with:
27+
node-version-file: .nvmrc
28+
cache: npm
29+
cache-dependency-path: frontend/package-lock.json
30+
31+
- name: Install dependencies
32+
run: npm ci
33+
34+
- name: Lint (fail on warnings)
35+
run: npm run lint -- --max-warnings=0
36+
37+
- name: Typecheck
38+
run: npm run typecheck
39+
40+
- name: Build
41+
run: npm run build
42+
1243
# ── Soroban ABI golden-vector drift guard ────────────────────────────────
1344
golden-vectors:
1445
name: Soroban ABI golden vectors
@@ -46,58 +77,73 @@ jobs:
4677
contract:
4778
name: Contract (Rust / Soroban)
4879
runs-on: ubuntu-latest
49-
env:
50-
RUSTFLAGS: "-D warnings"
5180
steps:
5281
- uses: actions/checkout@v4
53-
54-
- uses: dtolnay/rust-toolchain@stable
55-
with:
56-
targets: wasm32-unknown-unknown
57-
components: rustfmt, clippy
58-
59-
- uses: actions/cache@v4
82+
- uses: actions/setup-node@v4
6083
with:
61-
path: |
62-
~/.cargo/registry
63-
~/.cargo/git
64-
target
65-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
66-
67-
- run: cargo fmt --all -- --check
68-
- run: cargo clippy --target wasm32-unknown-unknown --release -- -D warnings
69-
- run: cargo test
70-
- run: cargo build --target wasm32-unknown-unknown --release
71-
72-
- name: Record wasm SHA-256
73-
run: sha256sum target/wasm32-unknown-unknown/release/niffyinsure.wasm | tee niffyinsure.wasm.sha256
74-
75-
- name: Simulate wasm drift (staging acceptance test)
84+
node-version: '20'
85+
- name: Determine package manager
86+
id: pkgmgr
7687
run: |
77-
ACTUAL=$(cat niffyinsure.wasm.sha256 | awk '{print $1}')
78-
EXPECTED=$(jq -r '.contracts[0].expectedWasmHash' contracts/deployment-registry.json)
79-
# In CI the registry holds a placeholder; drift is detected when they differ.
80-
# In staging, set NIFFYINSURE_EXPECTED_WASM_HASH to a known-wrong value to
81-
# verify the alert path fires. Exit 0 here — alerting is runtime, not build-time.
82-
if [ "$EXPECTED" = "\${NIFFYINSURE_EXPECTED_WASM_HASH}" ]; then
83-
echo "Registry uses env placeholder — skipping drift comparison in CI"
84-
elif [ "$ACTUAL" != "$EXPECTED" ]; then
85-
echo "::warning::Wasm drift detected: expected=$EXPECTED actual=$ACTUAL"
88+
if [ -f pnpm-lock.yaml ]; then
89+
echo "manager=pnpm" >> "$GITHUB_OUTPUT"
90+
elif [ -f package-lock.json ]; then
91+
echo "manager=npm" >> "$GITHUB_OUTPUT"
92+
elif [ -f yarn.lock ]; then
93+
echo "manager=yarn" >> "$GITHUB_OUTPUT"
8694
else
87-
echo "Wasm hash matches registry: $ACTUAL"
95+
echo "manager=npm" >> "$GITHUB_OUTPUT"
8896
fi
89-
90-
- uses: actions/upload-artifact@v4
97+
- name: Setup pnpm when needed
98+
if: steps.pkgmgr.outputs.manager == 'pnpm'
99+
uses: pnpm/action-setup@v2
100+
with:
101+
version: 8
102+
- name: Cache node modules
103+
uses: actions/cache@v4
91104
with:
92-
name: niffyinsure-wasm-${{ github.sha }}
93105
path: |
94-
target/wasm32-unknown-unknown/release/niffyinsure.wasm
95-
niffyinsure.wasm.sha256
96-
retention-days: 30
106+
node_modules
107+
~/.pnpm-store
108+
key: ${{ runner.os }}-node-${{ steps.pkgmgr.outputs.manager }}-${{ hashFiles('**/package-lock.json', '**/pnpm-lock.yaml', '**/yarn.lock') }}
109+
restore-keys: ${{ runner.os }}-node-${{ steps.pkgmgr.outputs.manager }}-
110+
- name: Cache .next cache
111+
uses: actions/cache@v4
112+
with:
113+
path: .next/cache
114+
key: ${{ runner.os }}-next-cache-${{ hashFiles('**/package-lock.json', '**/pnpm-lock.yaml', '**/yarn.lock') }}
115+
restore-keys: ${{ runner.os }}-next-cache-
116+
- name: Install dependencies
117+
run: |
118+
if [ "${{ steps.pkgmgr.outputs.manager }}" = "pnpm" ]; then
119+
pnpm install --frozen-lockfile
120+
else
121+
npm ci
122+
fi
123+
- name: Lint (fail on warnings)
124+
run: |
125+
if [ "${{ steps.pkgmgr.outputs.manager }}" = "pnpm" ]; then
126+
pnpm eslint --max-warnings=0 .
127+
else
128+
npm run lint -- --max-warnings=0
129+
fi
130+
- name: TypeScript compile
131+
run: |
132+
if [ "${{ steps.pkgmgr.outputs.manager }}" = "pnpm" ]; then
133+
pnpm tsc --noEmit
134+
else
135+
npm run build --if-present -- --noEmit
136+
fi
137+
- name: Build
138+
run: |
139+
if [ "${{ steps.pkgmgr.outputs.manager }}" = "pnpm" ]; then
140+
pnpm build
141+
else
142+
npm run build
143+
fi
97144
98-
# ── Backend ───────────────────────────────────────────────────────────────
99-
backend:
100-
name: Backend (Node / TypeScript)
145+
unit-tests:
146+
name: Unit tests
101147
runs-on: ubuntu-latest
102148
defaults:
103149
run:
@@ -121,60 +167,35 @@ jobs:
121167
122168
- uses: actions/setup-node@v4
123169
with:
124-
node-version: 22
125-
126-
- run: npm install
127-
- run: npm run lint
128-
- run: npm run build
129-
- run: npm test
130-
131-
# ── Dependency audit / supply-chain ─────────────────────────────────────
132-
# Policy: CRITICAL CVEs fail the build. HIGH CVEs produce a warning and
133-
# must be triaged within 7 days. Accepted risks require a signed-off entry
134-
# in docs/ops/audit-exceptions.md before the override label is applied.
135-
# Override process:
136-
# 1. Engineer opens a PR adding the CVE to audit-exceptions.md with
137-
# justification, mitigations, and a review-by date.
138-
# 2. A second engineer approves the PR.
139-
# 3. Add the GitHub label `audit-exception-approved` to the failing PR.
140-
# 4. Re-run this job — it will pass once the exception is documented.
141-
dependency-audit:
142-
name: Dependency Audit (npm / SBOM)
170+
node-version: '20'
171+
- name: Install dependencies
172+
run: npm ci
173+
- name: Run unit tests
174+
run: npm test
175+
176+
e2e-tests:
177+
name: Playwright E2E tests
143178
runs-on: ubuntu-latest
179+
needs: frontend
144180
steps:
145181
- uses: actions/checkout@v4
146-
147182
- uses: actions/setup-node@v4
148183
with:
149-
node-version: 22
150-
151-
- name: Audit backend dependencies
152-
working-directory: backend
153-
run: |
154-
npm install --ignore-scripts
155-
# Fail on critical; warn on high (exit 0 so we can capture output)
156-
npm audit --audit-level=critical
157-
npm audit --audit-level=high || echo "::warning::High-severity advisories found — triage within 7 days per audit policy"
158-
159-
- name: Audit frontend dependencies
160-
working-directory: frontend
161-
run: |
162-
npm ci --ignore-scripts
163-
npm audit --audit-level=critical
164-
npm audit --audit-level=high || echo "::warning::High-severity advisories found — triage within 7 days per audit policy"
165-
166-
- name: Generate SBOM (backend)
167-
working-directory: backend
168-
run: npx --yes @cyclonedx/cyclonedx-npm --output-format JSON --output-file ../sbom-backend.json
169-
170-
- name: Generate SBOM (frontend)
171-
working-directory: frontend
172-
run: npx --yes @cyclonedx/cyclonedx-npm --output-format JSON --output-file ../sbom-frontend.json
173-
174-
- uses: actions/upload-artifact@v4
184+
node-version: '20'
185+
- name: Install dependencies
186+
run: npm ci
187+
- name: Install Playwright browsers
188+
run: npx playwright install --with-deps
189+
- name: Run Playwright tests
190+
run: npx playwright test --reporter=html
191+
continue-on-error: true
192+
- name: Upload Playwright artifacts on failure
193+
if: failure()
194+
uses: actions/upload-artifact@v3
175195
with:
176-
name: sbom-${{ github.sha }}
196+
name: playwright-failure-${{ github.run_id }}
177197
path: |
198+
feat/accessibility-audit
178199
sbom-backend.json
179200
sbom-frontend.json
180201
retention-days: 90
@@ -201,37 +222,13 @@ jobs:
201222
- run: npm run build
202223
- run: npm test
203224

204-
# ── Playwright e2e ────────────────────────────────────────────────────────
205-
# Runs headless Chromium tests against a production build of the frontend.
206-
# Traces and screenshots are uploaded as artifacts on failure so engineers
207-
# can debug within minutes without reproducing locally.
208-
#
209-
# Secrets: no testnet accounts are used — all API and wallet calls are
210-
# mocked at the network layer via Playwright route interception.
211-
#
212-
# Flake policy: tests retry up to 2 times (configured in playwright.config.ts).
213-
# Persistent flakes must be quarantined in e2e/quarantine/ and tracked in a
214-
# GitHub issue within 48 hours of detection.
215-
playwright:
216-
name: Playwright e2e
225+
# ── Accessibility (axe) ───────────────────────────────────────────────────
226+
accessibility:
227+
name: Accessibility (axe / Playwright)
217228
runs-on: ubuntu-latest
218-
needs: frontend
219229
defaults:
220230
run:
221231
working-directory: frontend
222-
env:
223-
CI: true
224-
# Analytics disabled in CI — no real Plausible domain configured
225-
NEXT_PUBLIC_ANALYTICS_ENABLED: "false"
226-
NEXT_PUBLIC_API_URL: "http://localhost:3001"
227-
NEXT_PUBLIC_SOROBAN_RPC_URL: "https://soroban-testnet.stellar.org"
228-
NEXT_PUBLIC_HORIZON_URL: "https://horizon-testnet.stellar.org"
229-
NEXT_PUBLIC_NETWORK: "testnet"
230-
NEXT_PUBLIC_CONTRACT_ID: "mock-contract-id"
231-
NEXT_PUBLIC_IPFS_GATEWAY: "https://ipfs.io/ipfs"
232-
NEXT_PUBLIC_CAPTCHA_SITE_KEY: ""
233-
NEXT_PUBLIC_CAPTCHA_PROVIDER: "turnstile"
234-
PLAYWRIGHT_BASE_URL: "http://localhost:3000"
235232
steps:
236233
- uses: actions/checkout@v4
237234

@@ -242,29 +239,25 @@ jobs:
242239
cache-dependency-path: frontend/package-lock.json
243240

244241
- run: npm ci
242+
- run: npm run build
245243

246244
- name: Install Playwright browsers
247245
run: npx playwright install --with-deps chromium
248246

249-
- name: Build Next.js app
250-
run: npm run build
251-
252-
- name: Start Next.js preview server
253-
run: npx next start --port 3000 &
254-
# Give the server a moment to start; Playwright will wait for baseURL
255-
256-
- name: Wait for server
257-
run: npx wait-on http://localhost:3000 --timeout 30000
258-
259-
- name: Run Playwright tests
260-
run: npx playwright test
247+
- name: Run axe accessibility checks
248+
run: npx playwright test tests/accessibility.spec.ts --reporter=list
249+
env:
250+
BASE_URL: http://localhost:3000
261251

262-
- name: Upload Playwright traces and screenshots
252+
- uses: actions/upload-artifact@v4
263253
if: failure()
264-
uses: actions/upload-artifact@v4
265254
with:
266-
name: playwright-artifacts-${{ github.sha }}
267-
path: |
268-
frontend/playwright-report/
269-
frontend/test-results/
270-
retention-days: 7
255+
name: axe-report-${{ github.sha }}
256+
path: frontend/playwright-report/
257+
retention-days: 14
258+
259+
test-results
260+
playwright-report
261+
traces
262+
.playwright/traces
263+

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
22

0 commit comments

Comments
 (0)