potiuk opened a new pull request, #75616: URL: https://github.com/apache/airflow/pull/75616
A pull request gated to fork CI (AIP-120) shows one aggregate `Fork CI / Tests (AMD)` status, so to see which jobs are running, which failed and which passed, a maintainer or the contributor has to open the run in the fork. This reports every job of the fork run as its own commit status on the pull request, each linking to the job in the fork, the way project CI shows its jobs. ## What it does - The fork CI monitor lists the jobs of the fork run it already tracks (`GET /repos/<fork>/actions/runs/<id>/jobs?filter=latest`) and posts one status per job as `Fork CI job / <job name>`: yellow while queued or running, green when passed, red when failed, timed out, cancelled or unable to start. Skipped jobs and the jobs every run has (`Fork CI gate`, `Check if the run is needed`, `Platform: AMD`, `CI passed`) are left out. - Only differences from the head commit's current statuses are posted, failures first. Queued and running share one status, so a job costs at most two writes. - Once every job of a completed run is reported, the run (`<run id>:<attempt>:<conclusion>`) is recorded in the state comment's marker, so later monitor runs don't read its jobs again. A run in progress is read on every monitor run, and a re-run in the fork is read again because its attempt changes. - A job context left over from an earlier run of the same commit, which the latest completed run doesn't have, is overwritten as `success` with "Not in the latest fork run - see Fork CI / Tests (AMD)", since commit statuses can't be deleted and have no neutral state. - The job statuses are informational. Merging still depends on `CI passed` and the state machine on `Fork CI / Tests (AMD)`; neither changes. ## Safety - Job names come from the contributor's workflow, so contexts get their own `Fork CI job / ` prefix. No job can post the aggregate status the reconciler reads its decisions back from, or `CI passed`, which the merge ruleset requires. Non-printable characters are replaced, names are shortened to 100 characters, and contexts are grouped case-insensitively as GitHub compares them, keeping the worst result. - Reporting job statuses is caught on its own: a failure there is listed for Slack, and the pull request is still classified, so a `CI passed` that no longer holds is still revoked. - `read_head_status` now reads every page of the combined status, because with job statuses a commit can carry more than 100 contexts. ## Write budget, and turning it off GraphQL has no mutation for commit statuses (it can batch check runs, but a check run created by a workflow links to that workflow's run, not to the fork job), so each status is one REST write, counted against GitHub's limit of 500 content-creating requests an hour that every workflow of `apache/airflow` shares. Measured on 2026-10-25 from `apache/airflow`'s own `Tests (AMD)` PR runs (the same workflow runs in forks): | Pull request | Run | Started jobs | Reported job statuses | |---|---|---|---| | Breeze test fix | [40138784120](https://github.com/apache/airflow/actions/runs/40138784120) | 11 | 7 | | Documentation fix | [40169750323](https://github.com/apache/airflow/actions/runs/40169750323) | 16 | 12 | | Google provider change | [40158907296](https://github.com/apache/airflow/actions/runs/40158907296) | 43 | 39 | | CI change (full tests) | [40207881565](https://github.com/apache/airflow/actions/runs/40207881565) | 131 | 127 | `apache/airflow` had 1898 `Tests (AMD)` PR runs from 2026-10-18 to 2026-10-24, about 11 an hour. During the contributor pilot (a few gated pushes a day) job statuses use under 5% of the content-creating limit. With the gate enabled, and assuming about half of the runs come from gated pull requests, they would need about 400 writes an hour, so each monitor run posts at most 60 (at most 240 an hour, 48% of the limit). What doesn't fit is posted by later runs; once the budget is spent, no further jobs or statuses are read in that run, and the monitor posts a Slack note (fixed wording, so once while deferrals persist). The share of gated runs and the jobs per push are assumptions; the measurements and the estimate are in the "Write budget" section of `dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md`. `job_statuses = false` in `.github/fork-ci-gate.toml` turns job statuses off; the aggregate status and `CI passed` are posted either way. `dev/AGENTS.md` says to do that when the budget is exceeded and lists the signs. ## Verified - The `dev/breeze` test suite passes (1854 passed, 7 skipped), with new tests for the job statuses, the budget, the marker key, stale contexts, case-insensitive contexts, the switch, the Slack note and the paginated head status. - prek pre-commit hooks pass (the Docker-based lychee link check was not run), and so does `mypy-dev`. - Not yet run against a real fork run: that needs the monitor on `main`, or a dry run with `breeze ci fork-ci-reconcile --dry-run` against a gated pull request. --- ##### Was generative AI tooling used to co-author this PR? - [X] Yes — Claude Code (Opus 5) Generated-by: Claude Code (Opus 5) following [the guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions) 🤖 Generated with [Claude Code](https://claude.com/claude-code) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
