From fc15ec8c1b6e98ce9cfe8860916ea9c23d530c49 Mon Sep 17 00:00:00 2001 From: Sergey Kozyrenko Date: Fri, 24 Jul 2026 18:43:50 +0700 Subject: [PATCH] fix(ci): close four gaps in the workflow gates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codegen freshness watched the four inputs but not the generated types.ts, so a push editing only the generated file skipped the check and left the drift to fail someone else's unrelated codegen push. Watch the output too. Stand redaction split results.json on the raw secret values only. auth.setup builds a locator name from the regex-escaped user, and the file it scrubs is JSON, where that backslash is encoded again — so a failed stand login published the login address in a public artifact. Verified against a synthesised results.json: the old pass leaves `qa\\.bot@…` intact, the new one removes it. Also covers the URL-encoded form. The visual-diffs artifact was gated on the snapshot step failing, which a webServer or build failure inside that step also satisfies while still leaving results.json behind — the report then announced "snapshots differ" for an infra failure. Gate on diff images actually existing. Dispatching `tier: all` did nothing: e2e-stand never read its input, and e2e.yml's matching arm could not be selected. Drop both. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/ci.yml | 4 +++- .github/workflows/e2e-stand.yml | 19 ++++++++++--------- .github/workflows/e2e.yml | 22 +++++++++++++++++----- 3 files changed, 30 insertions(+), 15 deletions(-) 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: