diff --git a/.github/workflows/publish-playwright-report.yml b/.github/workflows/publish-playwright-report.yml index 916ce7a..cd0e41e 100644 --- a/.github/workflows/publish-playwright-report.yml +++ b/.github/workflows/publish-playwright-report.yml @@ -27,20 +27,38 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: + - name: Check S3 credentials + id: s3 + env: + AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }} + shell: bash + run: | + if [[ -n "${AWS_ACCESS_KEY_ID:-}" && -n "${AWS_SECRET_ACCESS_KEY:-}" ]]; then + echo "configured=true" >> "$GITHUB_OUTPUT" + else + echo "::notice::Skipping Playwright visual report publish; S3 credentials are not configured." + echo "configured=false" >> "$GITHUB_OUTPUT" + fi # workflow_run has repository secrets. Always use trusted default-branch code here; # never check out or execute the contributor's pull-request revision. - - uses: actions/checkout@v5 + - if: steps.s3.outputs.configured == 'true' + uses: actions/checkout@v5 with: ref: ${{ github.event.repository.default_branch }} persist-credentials: false lfs: true - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 + - if: steps.s3.outputs.configured == 'true' + uses: pnpm/action-setup@v4 + - if: steps.s3.outputs.configured == 'true' + uses: actions/setup-node@v4 with: node-version: 22 cache: pnpm - - run: pnpm install --frozen-lockfile + - if: steps.s3.outputs.configured == 'true' + run: pnpm install --frozen-lockfile - name: Read Playwright job result + if: steps.s3.outputs.configured == 'true' id: playwright env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -58,7 +76,7 @@ jobs: if [[ "$result" == "timed_out" ]]; then result="failure"; fi echo "result=$result" >> "$GITHUB_OUTPUT" - name: Resolve pull request - if: github.event.workflow_run.event == 'pull_request' + if: steps.s3.outputs.configured == 'true' && github.event.workflow_run.event == 'pull_request' id: pull_request env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -77,6 +95,7 @@ jobs: fi echo "number=$number" >> "$GITHUB_OUTPUT" - name: Check for Playwright artifact + if: steps.s3.outputs.configured == 'true' id: artifact env: ARTIFACT_NAME: playwright-artifacts-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }} @@ -100,7 +119,7 @@ jobs: exit 1 fi - name: Download Playwright artifacts - if: steps.artifact.outputs.exists == 'true' + if: steps.s3.outputs.configured == 'true' && steps.artifact.outputs.exists == 'true' uses: actions/download-artifact@v4 with: name: playwright-artifacts-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }} @@ -108,6 +127,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} - name: Publish trusted Playwright screenshot gallery + if: steps.s3.outputs.configured == 'true' id: publish env: AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }} @@ -131,6 +151,7 @@ jobs: run: bash scripts/publish-playwright-report.sh - name: Link screenshot gallery from pull request if: >- + steps.s3.outputs.configured == 'true' && github.event.workflow_run.event == 'pull_request' && steps.pull_request.outputs.number != '' && steps.publish.outputs.latest_pr_run == 'true' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dfb25fb..bba5d93 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,7 +30,7 @@ pnpm dev | `pnpm check` | TypeScript (`tsc`) across the monorepo. | | `pnpm lint` | Biome lint and format check. | -CI runs `pnpm lint`, `pnpm check`, production builds (including Electron preload smoke), `pnpm test`, `pnpm test:integration`, and `pnpm test:e2e` on every PR. +CI runs `pnpm lint`, `pnpm check`, production builds (including Electron preload smoke), `pnpm test`, `pnpm test:integration`, and `pnpm test:e2e` on every PR. After those jobs on `main` or a PR, `publish Playwright report` uploads the screenshot gallery when `S3_ACCESS_KEY_ID` and `S3_SECRET_ACCESS_KEY` are set; otherwise that workflow skips and stays green. ## Secrets and configuration diff --git a/packages/testkit/src/publish-playwright-report.test.ts b/packages/testkit/src/publish-playwright-report.test.ts new file mode 100644 index 0000000..96ea3ac --- /dev/null +++ b/packages/testkit/src/publish-playwright-report.test.ts @@ -0,0 +1,42 @@ +import { spawnSync } from "node:child_process"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; + +const script = path.resolve("scripts/publish-playwright-report.sh"); + +function runPublish(env: NodeJS.ProcessEnv): { + status: number | null; + output: string; +} { + const result = spawnSync("bash", [script], { + encoding: "utf8", + env, + }); + return { + status: result.status, + output: `${result.stdout}${result.stderr}`, + }; +} + +const isolatedEnv = { + PATH: process.env.PATH ?? "/usr/bin:/bin", + HOME: process.env.HOME ?? "/tmp", +} as const satisfies NodeJS.ProcessEnv; + +describe("publish-playwright-report.sh", () => { + it("skips when S3 credentials are unset", () => { + const result = runPublish(isolatedEnv); + expect(result.status).toBe(0); + expect(result.output).toMatch(/Skipping Playwright visual report publish/); + }); + + it("fails when credentials exist but the S3 destination is incomplete", () => { + const result = runPublish({ + ...isolatedEnv, + AWS_ACCESS_KEY_ID: "test-access-key", + AWS_SECRET_ACCESS_KEY: "test-secret-key", + }); + expect(result.status).not.toBe(0); + expect(result.output).toMatch(/S3_BUCKET is required/); + }); +}); diff --git a/scripts/publish-playwright-report.sh b/scripts/publish-playwright-report.sh index ffdbdcf..d2cc7f3 100644 --- a/scripts/publish-playwright-report.sh +++ b/scripts/publish-playwright-report.sh @@ -1,8 +1,18 @@ #!/usr/bin/env bash set -euo pipefail -: "${AWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID is required}" -: "${AWS_SECRET_ACCESS_KEY:?AWS_SECRET_ACCESS_KEY is required}" +if [[ -z "${AWS_ACCESS_KEY_ID:-}" || -z "${AWS_SECRET_ACCESS_KEY:-}" ]]; then + echo "::notice::Skipping Playwright visual report publish; S3 credentials are not configured." + if [[ -n "${GITHUB_OUTPUT:-}" ]]; then + printf 'latest_pr_run=false\n' >> "$GITHUB_OUTPUT" + printf 'stable_screenshots_url=\n' >> "$GITHUB_OUTPUT" + fi + if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then + printf '%s\n' "Playwright visual report publish skipped because S3 credentials are not configured." >> "$GITHUB_STEP_SUMMARY" + fi + exit 0 +fi + : "${S3_BUCKET:?S3_BUCKET is required}" : "${S3_ENDPOINT:?S3_ENDPOINT is required}" : "${PLAYWRIGHT_PUBLIC_BASE_URL:?PLAYWRIGHT_PUBLIC_BASE_URL is required}"