76 lines
3.2 KiB
YAML
76 lines
3.2 KiB
YAML
name: E2E UI Required
|
|
|
|
# Required-status gate: a PR that changes web/** must ship a tests/e2e_ui/**
|
|
# test covering the change or carry a maintainer-effective `skip-e2e-ui-test`
|
|
# label. The policy verdict FAILS the job; mark `E2E UI Required` as a required
|
|
# check in branch protection for that to block merge. Whether a change "needs a
|
|
# test" is decided by an LLM judge (check.sh case 2), not file-presence, so
|
|
# refactors/renames/dep-bumps/styling/test-only edits don't trip the gate and a
|
|
# throwaway test doesn't satisfy it.
|
|
#
|
|
# Trigger is `pull_request_target`, so the workflow + gate script run from main
|
|
# with the base token even for fork PRs: the PR-head copy never runs (a PR can't
|
|
# weaken the gate), and `labeled`/`unlabeled` let the skip label re-evaluate it.
|
|
#
|
|
# SECURITY -- the LLM judge reads the PR's (attacker-controlled) diff as TEXT and
|
|
# sends it to the gateway with the rate-limited, revocable test token (same risk
|
|
# profile as fork e2e). The job never checks out or runs PR-head code: it checks
|
|
# out ONLY .github/scripts from main (pinned, no persisted credentials) and reads
|
|
# state via the API. The judge prompt is hardened against injection and fails
|
|
# closed; a wrong "pass" can't merge anything since the required `Maintainer
|
|
# Approval` check + a human reviewer still gate merge.
|
|
#
|
|
# NO `paths:` filter on purpose: a path-filtered required check never reports on
|
|
# non-matching PRs, stranding the status pending forever. This always runs and
|
|
# the gate script self-determines whether web/** was touched.
|
|
#
|
|
# leak-scan-allow: pull_request_target
|
|
on:
|
|
pull_request_target:
|
|
types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
# PR re-syncs / relabels share a group by PR number so old runs cancel.
|
|
group: e2e-ui-required-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
require-e2e-ui:
|
|
name: E2E UI Required
|
|
# Skip drafts; the `ready_for_review` trigger re-fires on un-drafting.
|
|
if: ${{ !github.event.pull_request.draft }}
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- name: Check out gate scripts from main
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
with:
|
|
ref: main # trusted base; never the PR head
|
|
sparse-checkout: .github/scripts
|
|
persist-credentials: false
|
|
|
|
- name: Load maintainers
|
|
id: maintainers
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
REPO: ${{ github.repository }}
|
|
run: bash .github/scripts/merge-ready/load-maintainers.sh
|
|
|
|
- name: Require e2e_ui coverage or effective waiver
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
REPO: ${{ github.repository }}
|
|
PR: ${{ github.event.pull_request.number }}
|
|
MAINTAINERS: ${{ steps.maintainers.outputs.list }}
|
|
# OpenAI-compatible gateway (same secrets the e2e suites use).
|
|
OPENAI_BASE_URL: ${{ secrets.GATEWAY_BASE_URL }}
|
|
OPENAI_API_KEY: ${{ secrets.LLM_API_KEY }}
|
|
E2E_UI_JUDGE_MODEL: databricks-gpt-5-4
|
|
run: bash .github/scripts/e2e-ui-required/check.sh
|