From 68ea03ffb20a4cd983911746792f49b6708b9a30 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Mon, 17 Aug 2026 16:22:59 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=97=B3=EF=B8=8F=20ci:=20Codegraph=20E2E?= =?UTF-8?q?=20Vote=20Runs=20on=20Dev=20Merges=20(Observe-Only)=20(#14947)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/codegraph-e2e-votes.yml | 192 ++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 .github/workflows/codegraph-e2e-votes.yml diff --git a/.github/workflows/codegraph-e2e-votes.yml b/.github/workflows/codegraph-e2e-votes.yml new file mode 100644 index 0000000000..c5b3b496d6 --- /dev/null +++ b/.github/workflows/codegraph-e2e-votes.yml @@ -0,0 +1,192 @@ +# Codegraph e2e VOTES — observe-only, post-merge, time-boxed. +# +# Playwright never runs on pushes to dev, so evidence for the e2e skip election would +# otherwise wait on rare organic PR spec failures. This workflow runs EXACTLY the skippable +# tier the merged PR's selection computed — every merge becomes a direct trial of "would +# skipping these specs have missed a failure". A green run is a confirmation vote; a failing +# spec here is a tier-miss vote counted AGAINST enabling skipping. The shadow evaluator on +# the codegraph droplet harvests these runs and attributes them back to the merged PR. +# +# It cannot fail the branch: the tier lookup exits 0 on every path and the test step is +# continue-on-error. The newest merge cancels older vote runs. The whole campaign switches +# off by setting repo variable CODEGRAPH_E2E_VOTES=off once the election passes. +name: Codegraph E2E Votes + +on: + push: + branches: + - dev + paths: + - '**' + - '!**.md' + - '!.github/workflows/**' + +permissions: + contents: read + +concurrency: + group: codegraph-e2e-votes + cancel-in-progress: true + +env: + NODE_OPTIONS: '--max-old-space-size=6144' + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' + +jobs: + vote: + name: vote (skippable tier) + if: vars.CODEGRAPH_E2E_VOTES != 'off' + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + CI: 'true' + E2E_CHROMIUM_CHANNEL: chrome + E2E_STREAM_STORE: memory + steps: + - uses: actions/checkout@v5 + + - name: Ask codegraph for this merge's skippable tier + id: tiers + env: + URL: ${{ secrets.CODEGRAPH_URL }} + TOKEN: ${{ secrets.CODEGRAPH_TOKEN }} + GH_TOKEN: ${{ github.token }} + run: | + set +e + N=0 + if [ -n "$URL" ] && [ -n "$TOKEN" ]; then + gh api "repos/${{ github.repository }}/commits/${{ github.sha }}" \ + --jq '[.files[] | {path: .filename, status: .status}]' > files.json 2>/dev/null + if [ -s files.json ]; then + jq -c '{files: .}' files.json > body.json + RESP=$(curl -sS -m 45 -H "Authorization: Bearer $TOKEN" \ + -H 'content-type: application/json' --data-binary @body.json "$URL/v1/select") + if ! echo "$RESP" | jq -e '.e2e.skippable' >/dev/null 2>&1; then + echo "codegraph unavailable or no tiers; skipping" + elif echo "$RESP" | jq -e '.e2e.fail_open == true' >/dev/null 2>&1; then + echo "decision was fail-open; tiers untrusted for this merge; skipping" + else + echo "$RESP" | jq -r '.e2e.skippable[]' | sed 's|^e2e/||' > skippable.txt + N=$(wc -l < skippable.txt | tr -d ' ') + fi + else + echo "could not read merge commit files; skipping" + fi + else + echo "no codegraph config; skipping" + fi + echo "codegraph-votes: running $N skippable specs" + echo "count=$N" >> "$GITHUB_OUTPUT" + exit 0 + + - name: Use Node.js 24.16.0 + if: steps.tiers.outputs.count != '0' + uses: actions/setup-node@v5 + with: + node-version: '24.16.0' + + - name: Restore node_modules cache + if: steps.tiers.outputs.count != '0' + id: cache-node-modules + uses: actions/cache@v5 + with: + path: | + node_modules + client/node_modules + packages/client/node_modules + packages/data-provider/node_modules + packages/data-schemas/node_modules + packages/api/node_modules + api/node_modules + key: node-modules-e2e-${{ runner.os }}-24.16.0-${{ hashFiles('package-lock.json') }} + + - name: Install dependencies + if: steps.tiers.outputs.count != '0' && steps.cache-node-modules.outputs.cache-hit != 'true' + run: npm ci + + - name: Restore data-provider build cache + if: steps.tiers.outputs.count != '0' + id: cache-data-provider + uses: actions/cache@v5 + with: + path: packages/data-provider/dist + key: build-data-provider-${{ runner.os }}-${{ hashFiles('package.json', 'package-lock.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }} + + - name: Build data-provider + if: steps.tiers.outputs.count != '0' && steps.cache-data-provider.outputs.cache-hit != 'true' + run: npm run build:data-provider + + - name: Restore data-schemas build cache + if: steps.tiers.outputs.count != '0' + id: cache-data-schemas + uses: actions/cache@v5 + with: + path: packages/data-schemas/dist + key: build-data-schemas-${{ runner.os }}-${{ hashFiles('package.json', 'package-lock.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }} + + - name: Build data-schemas + if: steps.tiers.outputs.count != '0' && steps.cache-data-schemas.outputs.cache-hit != 'true' + run: npm run build:data-schemas + + - name: Restore api build cache + if: steps.tiers.outputs.count != '0' + id: cache-api + uses: actions/cache@v5 + with: + path: packages/api/dist + key: build-api-${{ runner.os }}-${{ hashFiles('package.json', 'package-lock.json', 'packages/api/src/**', 'packages/api/tsconfig*.json', 'packages/api/tsdown.config.mjs', 'packages/api/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json') }} + + - name: Build api + if: steps.tiers.outputs.count != '0' && steps.cache-api.outputs.cache-hit != 'true' + run: npm run build:api + + - name: Restore client-package build cache + if: steps.tiers.outputs.count != '0' + id: cache-client-package + uses: actions/cache@v5 + with: + path: packages/client/dist + key: build-client-package-${{ runner.os }}-${{ hashFiles('package.json', 'package-lock.json', 'packages/client/src/**', 'packages/client/tsconfig*.json', 'packages/client/tsdown.config.mjs', 'packages/client/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }} + + - name: Build client-package + if: steps.tiers.outputs.count != '0' && steps.cache-client-package.outputs.cache-hit != 'true' + run: npm run build:client-package + + - name: Restore client app build cache + if: steps.tiers.outputs.count != '0' + id: cache-client-app + uses: actions/cache@v5 + with: + path: client/dist + key: build-client-app-e2e-${{ runner.os }}-${{ hashFiles('package.json', 'package-lock.json', 'client/src/**', 'client/public/**', 'client/scripts/post-build.cjs', 'client/index.html', 'client/package.json', 'client/vite.config.*', 'client/tsconfig*.json', 'client/tailwind.config.*', 'client/postcss.config.*', 'packages/client/src/**', 'packages/client/tailwind.preset.cjs', 'packages/client/tsconfig*.json', 'packages/client/tsdown.config.mjs', 'packages/client/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }} + + - name: Build client app + if: steps.tiers.outputs.count != '0' && steps.cache-client-app.outputs.cache-hit != 'true' + run: npm run build:client + + - name: Verify Chrome is present + if: steps.tiers.outputs.count != '0' + run: google-chrome --version + + # ffmpeg for retry video — see the note in playwright-mock.yml. + - name: Install Playwright ffmpeg (best effort) + if: steps.tiers.outputs.count != '0' + timeout-minutes: 3 + continue-on-error: true + run: timeout -k 10 90 npx playwright install ffmpeg + + # Optional fonts only — see the note in playwright-mock.yml. + - name: Install optional Playwright font dependencies (best effort) + if: steps.tiers.outputs.count != '0' + timeout-minutes: 4 + continue-on-error: true + run: .github/scripts/install-playwright-fonts.sh + + - name: Vote — run the skippable tier (cannot fail the branch) + if: steps.tiers.outputs.count != '0' + continue-on-error: true + run: npx playwright test --config=e2e/playwright.config.mock.ts $(tr '\n' ' ' < skippable.txt) + + - name: Done + if: always() + run: echo "codegraph-votes: complete"