Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 36 additions & 27 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -310,37 +310,37 @@ jobs:
# automatically β€” no `pull_request` round-trip needed for non-fork
# non-release PRs.
required-check:
# The name is conditional on purpose. On an in-repo feature PR the
# `pull_request` run skips this job (the push run owns the status), and
# a skipped job still posts a check run under its name. GitHub treats a
# SKIPPED required check as satisfied β€” so if the skipped job were named
# `βœ… Required PR Check`, it would green-light the ruleset (and enable
# auto-merge) while the push run's real mirror hadn't been created yet
# (it only appears once `ci` finishes), letting a PR merge mid-test or
# even override a red result. An expression name keeps the skipped job
# off the required name: GitHub does not evaluate the name of a skipped
# job, so it shows up as the raw expression text (still not the
# required name), while every path that runs evaluates to
# `βœ… Required PR Check`. The condition is written out anyway so the
# name stays correct if GitHub ever starts evaluating it, and must stay
# identical to the `if:` below.
name: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == true || github.event.pull_request.head.ref == 'next' || github.event.pull_request.head.ref == 'hotfixes') && 'βœ… Required PR Check' || '⏭️ Required PR Check (reported by the push run)' }}
needs: ci
# Mirror the `ci` job's gating exactly. The four cases that run:
# Which name this job reports under is the whole point of it.
#
# The ruleset gates merges on `βœ… Required PR Check`, so a check with that
# name must only ever exist on a head SHA after the full test matrix for
# that SHA has finished, mirroring its result. `needs: ci` guarantees the
# timing: the job is only created once every Build & Test leg and the
# coverage job are done.
#
# On an in-repo feature PR the `pull_request` run does not own the status
# (the push run on the head branch does), so it must not post
# `βœ… Required PR Check` at all. Two traps rule out the obvious shapes:
# - A job SKIPPED by `if:` still posts a check run under its name, and
# GitHub treats a skipped required check as satisfied. Under the real
# name that let PRs merge mid-test (CLDMV/slothlet#553).
# - GitHub does not evaluate the `name:` of a skipped job, so a
# conditional name on a skippable job shows up as the raw expression
# text (#350).
# So the job never skips: it runs on every path, the name expression is
# always evaluated, and the in-repo PR path lands on a readable,
# non-required name and passes as a no-op. The condition below is
# repeated in the step's OWNS_STATUS and must stay identical. The paths
# that own the status:
# 1. push events (job needs CI run)
# 2. fork PRs (push doesn't cover forks)
# 3. release PRs from `next` β†’ master/main (push covers SHA but commit-gate skips chore-bump)
# 4. release PRs from `hotfixes` β†’ master/main (same reason)
# In-repo feature PRs targeting `next` / `hotfixes` skip on
# pull_request β€” the push run on the head branch reports the status
# on the SHA. See the `name:` above for why the skipped job is renamed.
if: |
always() && (
github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.fork == true ||
github.event.pull_request.head.ref == 'next' ||
github.event.pull_request.head.ref == 'hotfixes'
)
name: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == true || github.event.pull_request.head.ref == 'next' || github.event.pull_request.head.ref == 'hotfixes') && 'βœ… Required PR Check' || '⏭️ Required PR Check (reported by the push run)' }}
needs: ci
# always(): run even when `ci` is skipped (the in-repo PR path) or failed
# (so the mirror can report red).
if: always()
# Match the reusable's runner routing (workflow-ci.yml): private CLDMV
# repos run on self-hosted cldmv-runners (GitHub-hosted Actions budget is
# private-metered and exhausted), public repos use free GitHub-hosted, and
Expand All @@ -354,10 +354,19 @@ jobs:
steps:
- name: Mirror reusable result
env:
OWNS_STATUS: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == true || github.event.pull_request.head.ref == 'next' || github.event.pull_request.head.ref == 'hotfixes' }}
IS_MASTER_SYNC: ${{ needs.ci.outputs.is_master_sync }}
DOCS_ONLY: ${{ needs.ci.outputs.docs_only }}
CI_RESULT: ${{ needs.ci.result }}
run: |
# In-repo feature PR: the push run on the head branch reports
# `βœ… Required PR Check`. This job runs under the
# `⏭️ Required PR Check (reported by the push run)` name and
# must not gate anything.
if [ "$OWNS_STATUS" != "true" ]; then
echo "In-repo PR event β€” the push run reports βœ… Required PR Check for this SHA."
exit 0
fi
echo "ci.result=$CI_RESULT docs_only=$DOCS_ONLY is_master_sync=$IS_MASTER_SYNC"
# next/hotfixes was force-synced to master β€” head SHA matches the
# default branch, nothing new to test, green-light without running CI.
Expand Down
Loading