diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 346cdcfb..9ed6cb77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,7 +80,9 @@ jobs: echo "changed=true" >> "$GITHUB_OUTPUT" exit 0 fi - if git diff --name-only "$base" "${{ github.sha }}" | grep -qE '^(backend/pkg/graph/schema\.graphqls|frontend/graphql-schema\.graphql|frontend/graphql-codegen\.ts|frontend/pnpm-lock\.yaml)$'; then + # The generated file is in the list too: a push that edits only it would otherwise skip + # the check, and the drift surfaces later on someone else's unrelated codegen push. + if git diff --name-only "$base" "${{ github.sha }}" | grep -qE '^(backend/pkg/graph/schema\.graphqls|frontend/graphql-schema\.graphql|frontend/graphql-codegen\.ts|frontend/pnpm-lock\.yaml|frontend/src/graphql/types\.ts)$'; then echo "changed=true" >> "$GITHUB_OUTPUT" else echo "changed=false" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/e2e-stand.yml b/.github/workflows/e2e-stand.yml index 346f9744..7ed6055f 100644 --- a/.github/workflows/e2e-stand.yml +++ b/.github/workflows/e2e-stand.yml @@ -10,14 +10,6 @@ on: pull_request: types: [labeled] workflow_dispatch: - inputs: - tier: - description: "Run the stand tier" - type: choice - options: - - stand - - all - default: stand permissions: contents: read @@ -96,8 +88,17 @@ jobs: const f = "e2e/test-results/results.json"; if (!fs.existsSync(f)) process.exit(0); let s = fs.readFileSync(f, "utf8"); + // Derived forms too, not just the raw value: auth.setup.ts builds a locator name from + // the regex-escaped user, so a failed login writes `user@host\.com` into the error — + // and since this redacts the JSON text, that backslash is itself encoded as `\\`. + const regexEscaped = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const jsonEncoded = (v) => JSON.stringify(v).slice(1, -1); for (const v of [process.env.E2E_BASE_URL, process.env.E2E_USER, process.env.E2E_PASSWORD]) { - if (v) s = s.split(v).join(""); + if (!v) continue; + const forms = [v, regexEscaped(v), encodeURIComponent(v)]; + for (const form of new Set([...forms, ...forms.map(jsonEncoded)])) { + s = s.split(form).join(""); + } } fs.writeFileSync(f, s); ' diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index e8de8c97..f397f866 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -156,10 +156,23 @@ jobs: working-directory: frontend run: E2E_VISUAL=1 E2E_VISUAL_CONTAINER=1 npx playwright test -c e2e/playwright.config.ts - # Only on a real snapshot diff (not a tag-guard/install/build failure), so its - # presence is the precise "snapshots differ" signal the report keys the advisory on. - - name: Upload diffs + # The step failing is not the same signal as a snapshot differing: a webServer or build + # failure inside it also exits non-zero and still leaves results.json behind, which would + # publish the artifact and make the report say "snapshots differ". Look for the diff images. + - name: Detect snapshot diffs + id: diffs if: failure() && steps.snapshots.outcome == 'failure' + working-directory: frontend + run: | + if find e2e/test-results -name '*-diff.png' -print -quit | grep -q .; then + echo "found=true" >> "$GITHUB_OUTPUT" + else + echo "found=false" >> "$GITHUB_OUTPUT" + fi + + # Its presence is the precise "snapshots differ" signal the report keys the advisory on. + - name: Upload diffs + if: always() && steps.diffs.outputs.found == 'true' uses: actions/upload-artifact@v4 with: name: e2e-visual-diffs @@ -172,8 +185,7 @@ jobs: e2e-local: if: > github.event_name == 'schedule' || - (github.event_name == 'workflow_dispatch' && - contains(fromJSON('["local", "all"]'), inputs.tier)) + (github.event_name == 'workflow_dispatch' && inputs.tier == 'local') runs-on: ubuntu-latest timeout-minutes: 40 steps: