diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d29640..1427653 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,11 +114,26 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max + # Deploying to Pages needs two one-time steps that CI cannot do for itself: + # + # 1. Settings → Pages → Source: "GitHub Actions". + # The default GITHUB_TOKEN cannot do this. Creating a Pages site needs + # `administration: write`, which is not a permission a workflow can grant + # GITHUB_TOKEN, so `configure-pages` with enablement: true fails with + # "Resource not accessible by integration". + # 2. Settings → Secrets and variables → Actions → Variables: + # set DEPLOY_PAGES to "true". + # + # Gated on that variable so the pipeline stays green for anyone who does not + # want Pages, rather than failing on every push forever. pages: name: Deploy to Pages runs-on: ubuntu-latest needs: build - if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + if: >- + github.ref == 'refs/heads/main' && + github.event_name != 'pull_request' && + vars.DEPLOY_PAGES == 'true' permissions: pages: write id-token: write @@ -131,12 +146,6 @@ jobs: name: site path: public - uses: actions/configure-pages@v5 - with: - # Turn Pages on rather than requiring someone to click through - # repository settings first. Needs the pages: write permission above. - # If this still fails, Pages is unavailable for the repository — - # private repos on the free plan cannot use it; delete this job. - enablement: true - uses: actions/upload-pages-artifact@v3 with: path: public diff --git a/Dockerfile b/Dockerfile index 4e76691..98476ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ WORKDIR /app COPY package.json bun.lock ./ RUN bun install --frozen-lockfile -COPY tsconfig.json index.html ./ +COPY tsconfig.json index.html serve.ts ./ COPY src ./src COPY scripts ./scripts COPY fixtures ./fixtures diff --git a/README.md b/README.md index f019ccf..3aa7362 100644 --- a/README.md +++ b/README.md @@ -153,8 +153,14 @@ so iteration never re-fetches. Every event links back to its source. Both `.github/workflows/ci.yml` and `.gitlab-ci.yml` run the same gates on every push — typecheck, tests, and a feed sanity check — then build and publish a container image from the default branch. -GitHub Actions additionally deploys to Pages, enabling it on first run. Pages is unavailable for -private repositories on the free plan — delete the `pages` job if that applies. +GitHub Actions can also deploy to Pages, but that needs two one-time steps it cannot do for itself — +the default `GITHUB_TOKEN` is not allowed to create a Pages site: + +1. **Settings → Pages → Source: GitHub Actions.** +2. **Settings → Secrets and variables → Actions → Variables:** add `DEPLOY_PAGES` = `true`. + +Until then the `pages` job is skipped and the pipeline stays green. Pages is unavailable for private +repositories on the free plan. The feed job fails if the event count collapses. A source that quietly stops yielding events is the failure mode a parser-only pipeline is most prone to, and nothing else would surface it. Everything diff --git a/test/serve.test.ts b/test/serve.test.ts index f64320f..4df53f0 100644 --- a/test/serve.test.ts +++ b/test/serve.test.ts @@ -33,19 +33,32 @@ beforeAll(async () => { proc = Bun.spawn(["bun", "run", "serve.ts"], { env: { ...process.env, PORT: String(port), PUBLIC_DIR: root }, stdout: "ignore", - stderr: "ignore", + stderr: "pipe", }); - for (let i = 0; i < 60; i += 1) { + // Bail as soon as the process dies rather than retrying against a corpse: + // a missing serve.ts otherwise shows up only as "a hook timed out", which + // says nothing about the cause. + for (let i = 0; i < 40; i += 1) { + if (proc.exitCode !== null) { + const stderr = proc.stderr; + const why = + stderr instanceof ReadableStream + ? await new Response(stderr).text() + : "(no stderr captured)"; + throw new Error( + `serve.ts exited with ${proc.exitCode} before listening:\n${why.slice(0, 500)}`, + ); + } try { await fetch(`${base}/api/health`); return; } catch { - await Bun.sleep(100); + await Bun.sleep(50); } } - throw new Error("server did not start"); -}); + throw new Error(`server did not listen on ${base} within 2s`); +}, 10_000); afterAll(async () => { proc.kill(); diff --git a/tsconfig.json b/tsconfig.json index 2009292..d5ac3ec 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,5 +16,5 @@ "jsx": "react-jsx", "verbatimModuleSyntax": true }, - "include": ["src", "test", "scripts"] + "include": ["src", "test", "scripts", "serve.ts"] }