fix: add Authorization header to CORS configuration (#71) #81
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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] | |
| pull_request: | |
| branches: [master, main] | |
| # 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@820762786026740c76f36085b0efc47a31fe5020 # v7.0.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@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: '24' | |
| cache: npm | |
| - run: npm ci | |
| # Builds from the committed apps.json (vendored fixture) — no token/network. | |
| - name: Build (headless) | |
| run: npm run build:headless | |
| - name: Upload dist artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: 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@820762786026740c76f36085b0efc47a31fe5020 # v7.0.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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: playwright-report | |
| path: playwright-report/ | |
| retention-days: 7 | |
| # ── 4. publish-single-page-docs action self-test ─────────────────────────── | |
| # | |
| # actions/publish-single-page-docs/ 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 action's node_modules). This job renders a sample | |
| # markdown file through the real pipeline and pins the validation messages — | |
| # they are the action's user interface for onboarding repos. | |
| publish-single-page-docs: | |
| name: publish-single-page-docs action self-test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| # Matches the node-version the composite action pins in action.yml. | |
| node-version: '20' | |
| cache: npm | |
| cache-dependency-path: actions/publish-single-page-docs/package-lock.json | |
| - run: npm ci | |
| working-directory: actions/publish-single-page-docs | |
| # 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/publish-single-page-docs | |
| 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/publish-single-page-docs | |
| # ── 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@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: '24' | |
| cache: npm | |
| - run: npm ci | |
| - name: Download dist artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: 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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| 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@820762786026740c76f36085b0efc47a31fe5020 # v7.0.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 |