Repository navigation
fix(fullsend): allow Claude Code Vertex authentication #45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Refresh E2E coverage snapshot | |
| # Closes the coverage loop so snapshots stay current with ZERO manual work. | |
| # | |
| # When the e2e bot reports a PASSED run on a PR, this regenerates that | |
| # workspace's committed coverage snapshot (coverage-snapshots/<workspace>.lcov) | |
| # from the run's coverage artifacts and commits it back to the PR. On merge, | |
| # seed-coverage-main.yaml re-attributes it to the current main commit — so the | |
| # Codecov default-branch number tracks every workspace change automatically, | |
| # including the daily upstream repo-ref bumps (update-plugins-repo-refs.yaml). | |
| # | |
| # The only thing that produces fresh coverage is a real e2e run on the cluster, | |
| # so this fires off the e2e result rather than off a code change. See the | |
| # E2E coverage section in README.md. | |
| # | |
| # Assumptions / caveats: | |
| # | |
| # - One e2e invocation posts SEVERAL "Passed E2E Tests" comments (one per RHDH | |
| # version in the matrix, e.g. 1.10 + 1.11), so this job fires once per comment. | |
| # The per-PR concurrency group serializes them and the "snapshot unchanged" | |
| # guard below makes the repeats no-ops: the same PR-built instrumented image | |
| # runs on every RHDH version, so coverage is identical and converges to one | |
| # commit. Only if coverage genuinely differed across versions would the | |
| # committed number be last-writer-wins by comment order — accepted, not pinned | |
| # to a canonical version (there's no canonical RHDH version in versions.json to | |
| # pin to without hardcoding). | |
| # | |
| # - The refresh commit becomes the new PR HEAD via a GITHUB_TOKEN push, which does | |
| # NOT re-trigger GitHub Actions (only webhooks). Checked against this repo's | |
| # checks and it doesn't wedge: SonarCloud (no Actions workflow — runs via its | |
| # App on the webhook) and the Prow presubmits (e2e / publish / smoketest) all | |
| # re-run on the new commit regardless of pusher. The native Actions checks | |
| # (e2e-code-quality, validate-app-config-examples, validate-catalog-scripts) are | |
| # path-filtered to workspaces/*/e2e-tests/**, workspaces/**/metadata/**, | |
| # scripts/*.py and scripts/validate-app-config-examples/** — none of which the | |
| # refresh commit (only coverage-snapshots/ | |
| # <ws>.lcov) touches, so they aren't expected on it. The one residual edge to | |
| # re-check is a PR that itself changed e2e-tests where e2e-code-quality is a | |
| # *required* status check on a protected branch. | |
| # | |
| # - Prow (unlike GitHub Actions) DOES re-run the e2e on the refresh commit via the | |
| # synchronize webhook on PRs where it auto-runs on push (e.g. /ok-to-test'd bot | |
| # bump PRs) — it ignores [skip ci] and isn't gated by GITHUB_TOKEN. That fresh | |
| # "Passed" comment re-fires this workflow, and with flaky coverage the | |
| # "unchanged" guard never catches it → a refresh/e2e loop. The "Regenerate" | |
| # step's loop guard (skip if HEAD is already this workspace's refresh commit) | |
| # bounds it to the first refresh after a real change. | |
| on: | |
| issue_comment: | |
| types: [created] | |
| # contents: write to push the regenerated snapshot back to the PR branch (pushes | |
| # made with GITHUB_TOKEN do not trigger further workflow runs, so there is no | |
| # loop); pull-requests: read for the parse step's pulls.get (head ref + fork check). | |
| permissions: | |
| contents: write | |
| pull-requests: read | |
| # Serialize per PR (not per workspace) so two refreshes don't race to push the | |
| # same branch. The workspace name isn't known until the parse step runs, so it | |
| # can't key the group; per-PR is coarser but simpler and safe — multiple | |
| # workspaces passing on one PR just refresh serially. | |
| concurrency: | |
| group: refresh-coverage-${{ github.event.issue.number }} | |
| cancel-in-progress: false | |
| jobs: | |
| refresh: | |
| name: Regenerate coverage snapshot from a passing e2e run | |
| # Trusted bot only, passed runs only, real PRs only. The same-repo / fork | |
| # check is in the parse step (it needs the API), which also guards against | |
| # running PR-head scripts with a write token for an untrusted fork head. | |
| if: > | |
| github.event.issue.pull_request && | |
| github.event.comment.user.login == 'rhdh-test-bot' && | |
| contains(github.event.comment.body, 'Passed E2E Tests') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| # Before the parse step, because that step requires a module from the | |
| # checkout — and specifically BEFORE the PR branch is checked out below. | |
| # An issue_comment run checks out the default branch here, so the parser | |
| # is the one on main. Taking it from the PR head instead would mean | |
| # `require`-ing code the PR author wrote into a job holding a write token, | |
| # which is the thing the fork guard further down exists to prevent. | |
| - name: Checkout the default branch for the comment parser | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| # Its own directory, so the PR-branch checkout below lands on a clean | |
| # workspace and this copy of the parser cannot be replaced by the PR's. | |
| path: .trusted | |
| # One file off the tip is all this step needs, and it runs on every | |
| # passing e2e comment. Sparse is safe precisely BECAUSE of `path` | |
| # above: the sparse state belongs to this checkout's own .git, so it | |
| # cannot leave the PR checkout below partially populated. | |
| fetch-depth: 1 | |
| sparse-checkout: scripts/e2e-comment.cjs | |
| sparse-checkout-cone-mode: false | |
| - name: Parse the e2e result comment | |
| id: parse | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| # Untrusted comment body passed via env (repo convention — see | |
| # pr-actions.yaml) and read with process.env, never interpolated into | |
| # the script source where it could break out of the string. | |
| COMMENT_BODY: ${{ github.event.comment.body }} | |
| with: | |
| script: | | |
| // The shape of the bot's comment is owned by one module, tested in | |
| // scripts/tests/test_e2e_comment.py. This step used to carry its own | |
| // copy of the same patterns, and the copies had drifted: this one | |
| // matched the gcsweb host as `gcsweb-ci\.apps\.[\w.-]+`, which admits | |
| // any host somebody can put under a domain they own. Nothing but the | |
| // author pin above stood in the way, and one guard is not two. | |
| const { parsePassedE2eComment } = | |
| require(`${process.env.GITHUB_WORKSPACE}/.trusted/scripts/e2e-comment.cjs`); | |
| const body = process.env.COMMENT_BODY || ""; | |
| // One place for the skip contract: log + set the output later steps | |
| // gate on. Format drift warns (surfaces as a run annotation); an | |
| // expected skip (fork) is info. | |
| const skip = (msg, level = "warning") => { | |
| core[level](msg); | |
| core.setOutput("skip", "true"); | |
| }; | |
| // The job-level `if` already confirmed this is a bot "Passed E2E | |
| // Tests" comment, so any refusal here means the comment format | |
| // drifted — warn so it surfaces instead of the refresh silently going | |
| // quiet and snapshots never updating again. | |
| const parsed = parsePassedE2eComment(body); | |
| if (parsed.reason !== null) { | |
| const named = parsed.rejected ? ` naming '${parsed.rejected}'` : ""; | |
| skip(`Passed-e2e comment${named} could not be read (${parsed.reason}); skipping (comment format may have drifted).`); | |
| return; | |
| } | |
| const { workspace, coverageUrl } = parsed; | |
| const pr = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.issue.number, | |
| }); | |
| // Same-repo PRs only: a fork head can't receive our push, and we | |
| // won't run PR-head scripts with a write token from an untrusted fork. | |
| if (pr.data.head.repo.full_name !== pr.data.base.repo.full_name) { | |
| skip("Fork PR — skipping snapshot refresh.", "info"); | |
| return; | |
| } | |
| core.setOutput("skip", "false"); | |
| core.setOutput("workspace", workspace); | |
| core.setOutput("coverage_url", coverageUrl); | |
| core.setOutput("head_ref", pr.data.head.ref); | |
| - name: Checkout PR branch | |
| if: steps.parse.outputs.skip == 'false' | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| ref: ${{ steps.parse.outputs.head_ref }} | |
| # Full history so the push-retry's `git rebase origin/<branch>` has a | |
| # merge base if the branch advanced under us (a shallow clone can't | |
| # always compute it). | |
| fetch-depth: 0 | |
| - name: Setup Node | |
| if: steps.parse.outputs.skip == 'false' | |
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version: "24" | |
| - name: Regenerate snapshot and commit if it changed | |
| if: steps.parse.outputs.skip == 'false' | |
| env: | |
| WS: ${{ steps.parse.outputs.workspace }} | |
| COVERAGE_URL: ${{ steps.parse.outputs.coverage_url }} | |
| HEAD_REF: ${{ steps.parse.outputs.head_ref }} | |
| run: | | |
| set -euo pipefail | |
| # Only workspaces with committed anchors have coverage enabled. This | |
| # pre-check intentionally pre-empts refresh-coverage-snapshot.sh, which | |
| # treats a missing coverage-anchors/ dir as a fatal error (exit 1) — here | |
| # a workspace without coverage enabled is a normal skip, not a failure. | |
| if [[ ! -d "workspaces/${WS}/coverage-anchors" ]]; then | |
| echo "Workspace '${WS}' has no coverage-anchors (coverage not enabled) — skipping." | |
| exit 0 | |
| fi | |
| # Subject of our own refresh commits — pinned once so the guard below | |
| # and the commit at the end can't drift apart (a mismatch would silently | |
| # disable the guard and re-open the loop it exists to prevent). | |
| refresh_subject="refresh ${WS} snapshot from passing e2e run" | |
| # Loop guard. On a PR where Prow auto-runs the e2e on every push (e.g. | |
| # an /ok-to-test'd bot bump PR), our snapshot commit re-triggers the e2e | |
| # via the synchronize webhook — Prow ignores [skip ci] and isn't gated by | |
| # GITHUB_TOKEN. That posts a fresh "Passed" comment which fires this | |
| # workflow again. The "unchanged" guard below would normally stop the | |
| # second pass, but if a workspace's coverage is flaky run-to-run the lcov | |
| # differs every time and it never catches — an unbounded refresh/e2e loop. | |
| # | |
| # Walk the contiguous run of our own refresh commits at the tip: if THIS | |
| # workspace already has one there, this run came from the e2e Prow ran on | |
| # top of it — keep the first snapshot and stop. Looking back past HEAD | |
| # (not just at it) also bounds the multi-workspace case the concurrency | |
| # comment allows: two workspaces' refresh commits would otherwise | |
| # alternate at HEAD forever, each missing the other's. A real (non-refresh) | |
| # commit breaks the run, so a genuine new change still refreshes. | |
| # [[ ]] matching (not `git log | grep -q`) keeps this off a pipe, so a | |
| # SIGPIPE under `set -o pipefail` can't misread a match as a miss. | |
| n=0 | |
| while subject=$(git log -1 --format='%s' "HEAD~${n}" 2>/dev/null) \ | |
| && [[ "$(git log -1 --format='%an' "HEAD~${n}" 2>/dev/null)" == "github-actions[bot]" ]] \ | |
| && [[ "$subject" == *"snapshot from passing e2e run"* ]]; do | |
| if [[ "$subject" == *"$refresh_subject"* ]]; then | |
| echo "Workspace '${WS}' already has a refresh commit in the bot run at the tip — skipping to avoid a re-refresh loop." | |
| exit 0 | |
| fi | |
| n=$((n + 1)) | |
| done | |
| # A passed run can still carry zero browser coverage (backend-only, or | |
| # uninstrumented images). refresh-coverage-snapshot.sh is non-fatal in | |
| # that case; guard the commit on an actual change below. | |
| ./scripts/refresh-coverage-snapshot.sh "${WS}" "${COVERAGE_URL}" | |
| if [[ -z "$(git status --porcelain -- "coverage-snapshots/${WS}.lcov")" ]]; then | |
| echo "Snapshot unchanged — nothing to commit." | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add "coverage-snapshots/${WS}.lcov" | |
| # GITHUB_TOKEN pushes don't trigger GitHub Actions (no Actions loop); | |
| # Prow re-running the e2e is handled by the loop guard above. [skip ci] | |
| # still skips any push-triggered Actions check on this data-only commit. | |
| git commit -m "chore(coverage): ${refresh_subject} [skip ci]" | |
| # The push is unforced so it never clobbers concurrent work. The per-PR | |
| # concurrency group serializes bot refreshes, but a human push landing | |
| # between the checkout above and here would reject this push and fail the | |
| # job on a data-only commit. Re-sync onto the branch tip and retry once; | |
| # the commit only touches coverage-snapshots/${WS}.lcov, so a rebase | |
| # rarely conflicts. If it still fails, let the job fail — we genuinely | |
| # couldn't persist a real update. | |
| if ! git push origin "HEAD:${HEAD_REF}"; then | |
| echo "[WARN] Push rejected (branch advanced?) — re-syncing and retrying once." | |
| # Rebase onto FETCH_HEAD, not origin/${HEAD_REF}: actions/checkout | |
| # uses a narrow refspec, so `git fetch` reliably moves FETCH_HEAD but | |
| # may leave the remote-tracking ref stale (which would rebase onto the | |
| # old tip and fail again for the same reason). | |
| git fetch origin "${HEAD_REF}" | |
| git rebase FETCH_HEAD | |
| git push origin "HEAD:${HEAD_REF}" | |
| fi |