From 0191ea6c50812d5ccaa97e91bb9afc135a52d17d Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sat, 19 Sep 2026 14:55:06 -0700 Subject: [PATCH 1/6] test: pull request workflows with macOS jobs must cancel superseded runs agent-notification-tests.yml, iroh-v2.yml and relay-tls.yml have no concurrency group, so every push to a pull request leaves the previous run queued for a macOS runner. On 2026-09-19, 58 of 72 queued Agent notification semantics runs were for commits that were no longer their pull request's head or whose pull request was closed. Co-Authored-By: Claude Fable 5.1 --- tests/test_ci_self_hosted_guard.sh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_ci_self_hosted_guard.sh b/tests/test_ci_self_hosted_guard.sh index 8a9b4bf247bf..7565a666fe51 100755 --- a/tests/test_ci_self_hosted_guard.sh +++ b/tests/test_ci_self_hosted_guard.sh @@ -1292,6 +1292,30 @@ check_signing_intermediate_imports check_signing_intermediate_helper_behavior check_sentry_cli_install_portability check_sentry_cli_helper_behavior +check_pr_macos_workflows_cancel_superseded_runs() { + # A pull request workflow without a concurrency group never cancels the run + # for the previous push. On a macOS runner pool with a fixed slot count those + # runs stay queued behind everything else: on 2026-09-19, 58 of 72 queued + # "Agent notification semantics" runs were for commits that were no longer + # their pull request's head or whose pull request was closed. + local file failed=0 + for file in "$ROOT_DIR"/.github/workflows/*.yml; do + grep -qE '^ pull_request(_target)?:' "$file" || continue + grep -qE 'runs-on:.*(macos|MACOS_RUNNER)' "$file" || continue + if ! awk ' + /^concurrency:/ { in_block=1; next } + in_block && /^[^[:space:]]/ { in_block=0 } + in_block && /cancel-in-progress:[[:space:]]*(true|\$\{\{)/ { ok=1 } + END { exit !ok } + ' "$file"; then + echo "FAIL: $(basename "$file") runs macOS jobs on pull requests but never cancels a superseded run; add a concurrency group with cancel-in-progress" + failed=1 + fi + done + [ "$failed" -eq 0 ] || exit 1 + echo "PASS: pull request workflows with macOS jobs cancel superseded runs" +} + check_dmg_signing_uses_build_keychain check_create_dmg_uses_run_local_npm_prefix check_gui_smoke_unsupported_launch_handling @@ -1300,3 +1324,4 @@ check_no_ci_swift_package_skips check_web_db_behavior_tests check_web_test_runner_behavior check_tmux_terminal_nightly_isolation +check_pr_macos_workflows_cancel_superseded_runs From c67d220b79e742804fa99d5559c9ad226ef39545 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sat, 19 Sep 2026 14:55:30 -0700 Subject: [PATCH 2/6] ci: cancel superseded pull request runs in three macOS workflows Agent notification semantics, IROH v2 and Relay TLS had no concurrency group, so every push to a pull request left its previous run queued for a macOS runner. With about eight Blacksmith macOS slots that backlog sits in front of every other job. Key the group on the pull request number and cancel in progress for pull_request only; pushes to main and manual runs key on the run id and behave as before. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/agent-notification-tests.yml | 8 ++++++++ .github/workflows/iroh-v2.yml | 8 ++++++++ .github/workflows/relay-tls.yml | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/.github/workflows/agent-notification-tests.yml b/.github/workflows/agent-notification-tests.yml index 0ab5503c849c..7b5da02a6ef2 100644 --- a/.github/workflows/agent-notification-tests.yml +++ b/.github/workflows/agent-notification-tests.yml @@ -18,6 +18,14 @@ on: - scripts/ci/classify-app-host-test-output.py - Sources/AgentJournalLifecycleCenter*.swift - .github/workflows/agent-notification-tests.yml +# Cancel the run for the previous push to the same pull request. Without this +# every push leaves a run queued for a macOS runner that nothing will read. +# Pushes to main and manual runs key on the run id, so they never cancel or +# replace each other. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + permissions: contents: read jobs: diff --git a/.github/workflows/iroh-v2.yml b/.github/workflows/iroh-v2.yml index a36e38aa6434..cbe53d6ea0e0 100644 --- a/.github/workflows/iroh-v2.yml +++ b/.github/workflows/iroh-v2.yml @@ -14,6 +14,14 @@ on: - .github/workflows/iroh-v2.yml workflow_dispatch: +# Cancel the run for the previous push to the same pull request. Without this +# every push leaves a run queued for a macOS runner that nothing will read. +# Pushes to main and manual runs key on the run id, so they never cancel or +# replace each other. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + permissions: contents: read diff --git a/.github/workflows/relay-tls.yml b/.github/workflows/relay-tls.yml index 4bd693a7f4f4..0889be2be400 100644 --- a/.github/workflows/relay-tls.yml +++ b/.github/workflows/relay-tls.yml @@ -9,6 +9,14 @@ on: - .github/workflows/relay-tls.yml workflow_dispatch: +# Cancel the run for the previous push to the same pull request. Without this +# every push leaves a run queued for a macOS runner that nothing will read. +# Pushes to main and manual runs key on the run id, so they never cancel or +# replace each other. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + permissions: contents: read From d0e234faa1d500e4e5c18d27c759f408f4563240 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sat, 19 Sep 2026 14:59:49 -0700 Subject: [PATCH 3/6] chore: keep incident history out of code comments The dates and counts belong to the commit and pull request that made the change. The comments now state only why the code is this way. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/agent-notification-tests.yml | 6 ++---- .github/workflows/iroh-v2.yml | 6 ++---- .github/workflows/relay-tls.yml | 6 ++---- tests/test_ci_self_hosted_guard.sh | 7 ++----- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/.github/workflows/agent-notification-tests.yml b/.github/workflows/agent-notification-tests.yml index 7b5da02a6ef2..6d7322977c19 100644 --- a/.github/workflows/agent-notification-tests.yml +++ b/.github/workflows/agent-notification-tests.yml @@ -18,10 +18,8 @@ on: - scripts/ci/classify-app-host-test-output.py - Sources/AgentJournalLifecycleCenter*.swift - .github/workflows/agent-notification-tests.yml -# Cancel the run for the previous push to the same pull request. Without this -# every push leaves a run queued for a macOS runner that nothing will read. -# Pushes to main and manual runs key on the run id, so they never cancel or -# replace each other. +# A new push cancels the previous run for the same pull request. Other events +# key on the run id, so they never cancel each other. concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} diff --git a/.github/workflows/iroh-v2.yml b/.github/workflows/iroh-v2.yml index cbe53d6ea0e0..19740f669d25 100644 --- a/.github/workflows/iroh-v2.yml +++ b/.github/workflows/iroh-v2.yml @@ -14,10 +14,8 @@ on: - .github/workflows/iroh-v2.yml workflow_dispatch: -# Cancel the run for the previous push to the same pull request. Without this -# every push leaves a run queued for a macOS runner that nothing will read. -# Pushes to main and manual runs key on the run id, so they never cancel or -# replace each other. +# A new push cancels the previous run for the same pull request. Other events +# key on the run id, so they never cancel each other. concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} diff --git a/.github/workflows/relay-tls.yml b/.github/workflows/relay-tls.yml index 0889be2be400..84444dd03657 100644 --- a/.github/workflows/relay-tls.yml +++ b/.github/workflows/relay-tls.yml @@ -9,10 +9,8 @@ on: - .github/workflows/relay-tls.yml workflow_dispatch: -# Cancel the run for the previous push to the same pull request. Without this -# every push leaves a run queued for a macOS runner that nothing will read. -# Pushes to main and manual runs key on the run id, so they never cancel or -# replace each other. +# A new push cancels the previous run for the same pull request. Other events +# key on the run id, so they never cancel each other. concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} diff --git a/tests/test_ci_self_hosted_guard.sh b/tests/test_ci_self_hosted_guard.sh index 7565a666fe51..2046d12bf39f 100755 --- a/tests/test_ci_self_hosted_guard.sh +++ b/tests/test_ci_self_hosted_guard.sh @@ -1293,11 +1293,8 @@ check_signing_intermediate_helper_behavior check_sentry_cli_install_portability check_sentry_cli_helper_behavior check_pr_macos_workflows_cancel_superseded_runs() { - # A pull request workflow without a concurrency group never cancels the run - # for the previous push. On a macOS runner pool with a fixed slot count those - # runs stay queued behind everything else: on 2026-09-19, 58 of 72 queued - # "Agent notification semantics" runs were for commits that were no longer - # their pull request's head or whose pull request was closed. + # Without a concurrency group a push never cancels the previous run, and on + # a fixed pool of macOS runners those dead runs queue ahead of live ones. local file failed=0 for file in "$ROOT_DIR"/.github/workflows/*.yml; do grep -qE '^ pull_request(_target)?:' "$file" || continue From 1a5ce0d9c4fd2a7d7e6dbbd447bfb9d5c6de9499 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sat, 19 Sep 2026 15:46:26 -0700 Subject: [PATCH 4/6] test: require the superseded-run guard to check the group and the condition The guard accepted any expression for cancel-in-progress and ignored the group, so a block keyed on github.sha or one that only cancels on push passed. It now requires a group keyed on the pull request or ref and a cancel condition that holds for pull_request events, with a self-test of ineffective blocks. Co-Authored-By: Claude Fable 5.1 --- tests/test_ci_self_hosted_guard.sh | 43 ++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/tests/test_ci_self_hosted_guard.sh b/tests/test_ci_self_hosted_guard.sh index 2046d12bf39f..8c30d6cfaf52 100755 --- a/tests/test_ci_self_hosted_guard.sh +++ b/tests/test_ci_self_hosted_guard.sh @@ -1292,20 +1292,47 @@ check_signing_intermediate_imports check_signing_intermediate_helper_behavior check_sentry_cli_install_portability check_sentry_cli_helper_behavior +pr_concurrency_cancels_superseded_runs() { + # The group must be the same for every push to one pull request, and + # cancel-in-progress must be true for pull request events. + awk ' + /^concurrency:/ { in_block=1; next } + in_block && /^[^[:space:]]/ { in_block=0 } + in_block && /^[[:space:]]+group:/ \ + && /github\.(event\.pull_request\.number|ref|head_ref)([^_a-z]|$)/ { group_ok=1 } + in_block && /^[[:space:]]+cancel-in-progress:[[:space:]]*true[[:space:]]*$/ { cancel_ok=1 } + in_block && /^[[:space:]]+cancel-in-progress:/ \ + && /github\.event_name[[:space:]]*==[[:space:]]*\047pull_request(_target)?\047/ { cancel_ok=1 } + END { exit !(group_ok && cancel_ok) } + ' "$1" +} + check_pr_macos_workflows_cancel_superseded_runs() { # Without a concurrency group a push never cancels the previous run, and on # a fixed pool of macOS runners those dead runs queue ahead of live ones. - local file failed=0 + local file failed=0 probe + probe="$(mktemp)" + for bad in \ + 'group: ci-${{ github.sha }}|cancel-in-progress: true' \ + 'group: ci-${{ github.run_id }}|cancel-in-progress: true' \ + 'group: ci-${{ github.ref }}|cancel-in-progress: ${{ false }}' \ + "group: ci-\${{ github.ref }}|cancel-in-progress: \${{ github.event_name == 'push' }}" \ + "group: ci-\${{ github.ref }}|cancel-in-progress: \${{ github.event_name != 'pull_request' }}" + do + printf 'concurrency:\n %s\n %s\njobs:\n' "${bad%%|*}" "${bad##*|}" > "$probe" + if pr_concurrency_cancels_superseded_runs "$probe"; then + echo "FAIL: superseded-run guard self-test accepted an ineffective block: $bad" + rm -f "$probe" + exit 1 + fi + done + rm -f "$probe" + for file in "$ROOT_DIR"/.github/workflows/*.yml; do grep -qE '^ pull_request(_target)?:' "$file" || continue grep -qE 'runs-on:.*(macos|MACOS_RUNNER)' "$file" || continue - if ! awk ' - /^concurrency:/ { in_block=1; next } - in_block && /^[^[:space:]]/ { in_block=0 } - in_block && /cancel-in-progress:[[:space:]]*(true|\$\{\{)/ { ok=1 } - END { exit !ok } - ' "$file"; then - echo "FAIL: $(basename "$file") runs macOS jobs on pull requests but never cancels a superseded run; add a concurrency group with cancel-in-progress" + if ! pr_concurrency_cancels_superseded_runs "$file"; then + echo "FAIL: $(basename "$file") runs macOS jobs on pull requests but a new push does not cancel the previous run; key the concurrency group on the pull request and set cancel-in-progress for pull_request events" failed=1 fi done From 55dcd208dacc3379e8ee89a87bf1500596e9bae5 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sat, 19 Sep 2026 16:00:28 -0700 Subject: [PATCH 5/6] test: match the cancel condition to the events a workflow triggers on The guard accepted a pull_request_target condition on a workflow that only triggers on pull_request, skipped .yaml files, and only read the mapping form of on:. It now reads the pull request events from the mapping, list and scalar forms and requires cancellation for each one. Co-Authored-By: Claude Fable 5.1 --- tests/test_ci_self_hosted_guard.sh | 87 ++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 21 deletions(-) diff --git a/tests/test_ci_self_hosted_guard.sh b/tests/test_ci_self_hosted_guard.sh index 8c30d6cfaf52..5c62ffffd160 100755 --- a/tests/test_ci_self_hosted_guard.sh +++ b/tests/test_ci_self_hosted_guard.sh @@ -1292,47 +1292,92 @@ check_signing_intermediate_imports check_signing_intermediate_helper_behavior check_sentry_cli_install_portability check_sentry_cli_helper_behavior +pr_workflow_events() { + # Prints the pull request events a workflow triggers on, for the mapping, + # list and scalar forms of `on:`. + awk ' + /^on:/ { + in_on=1 + line=$0 + sub(/^on:[[:space:]]*/, "", line) + gsub(/[][,]/, " ", line) + n=split(line, words, /[[:space:]]+/) + for (i=1; i<=n; i++) if (words[i] ~ /^pull_request(_target)?$/) print words[i] + next + } + in_on && /^[^[:space:]#]/ { in_on=0 } + in_on && /^ (- )?pull_request(_target)?:?[[:space:]]*$/ { + event=$0 + gsub(/[-:[:space:]]/, "", event) + print event + } + ' "$1" | sort -u +} + pr_concurrency_cancels_superseded_runs() { # The group must be the same for every push to one pull request, and - # cancel-in-progress must be true for pull request events. + # cancel-in-progress must be true for every pull request event the workflow + # triggers on. + local file="$1" event + local events + events="$(pr_workflow_events "$file")" + [ -n "$events" ] || return 1 awk ' /^concurrency:/ { in_block=1; next } in_block && /^[^[:space:]]/ { in_block=0 } in_block && /^[[:space:]]+group:/ \ && /github\.(event\.pull_request\.number|ref|head_ref)([^_a-z]|$)/ { group_ok=1 } - in_block && /^[[:space:]]+cancel-in-progress:[[:space:]]*true[[:space:]]*$/ { cancel_ok=1 } - in_block && /^[[:space:]]+cancel-in-progress:/ \ - && /github\.event_name[[:space:]]*==[[:space:]]*\047pull_request(_target)?\047/ { cancel_ok=1 } - END { exit !(group_ok && cancel_ok) } - ' "$1" + END { exit !group_ok } + ' "$file" || return 1 + for event in $events; do + EVENT="$event" awk ' + /^concurrency:/ { in_block=1; next } + in_block && /^[^[:space:]]/ { in_block=0 } + in_block && /^[[:space:]]+cancel-in-progress:[[:space:]]*true[[:space:]]*$/ { ok=1 } + in_block && /^[[:space:]]+cancel-in-progress:/ \ + && index($0, "github.event_name == \047" ENVIRON["EVENT"] "\047") { ok=1 } + END { exit !ok } + ' "$file" || return 1 + done } check_pr_macos_workflows_cancel_superseded_runs() { # Without a concurrency group a push never cancels the previous run, and on # a fixed pool of macOS runners those dead runs queue ahead of live ones. - local file failed=0 probe + local file failed=0 probe case_text probe="$(mktemp)" - for bad in \ - 'group: ci-${{ github.sha }}|cancel-in-progress: true' \ - 'group: ci-${{ github.run_id }}|cancel-in-progress: true' \ - 'group: ci-${{ github.ref }}|cancel-in-progress: ${{ false }}' \ - "group: ci-\${{ github.ref }}|cancel-in-progress: \${{ github.event_name == 'push' }}" \ - "group: ci-\${{ github.ref }}|cancel-in-progress: \${{ github.event_name != 'pull_request' }}" - do - printf 'concurrency:\n %s\n %s\njobs:\n' "${bad%%|*}" "${bad##*|}" > "$probe" - if pr_concurrency_cancels_superseded_runs "$probe"; then - echo "FAIL: superseded-run guard self-test accepted an ineffective block: $bad" + # trigger ~ group ~ cancel-in-progress ~ expected + while IFS='~' read -r trigger group cancel expected; do + [ -n "$trigger" ] || continue + printf '%s\nconcurrency:\n group: %s\n cancel-in-progress: %s\njobs:\n' \ + "$(printf '%b' "$trigger")" "$group" "$cancel" > "$probe" + if pr_concurrency_cancels_superseded_runs "$probe"; then case_text=accept; else case_text=reject; fi + if [ "$case_text" != "$expected" ]; then + echo "FAIL: superseded-run guard self-test expected $expected for: $trigger | $group | $cancel" rm -f "$probe" exit 1 fi - done + done <<'CASES' +on:\n pull_request:~ci-${{ github.ref }}~true~accept +on: pull_request~ci-${{ github.ref }}~${{ github.event_name == 'pull_request' }}~accept +on: [push, pull_request]~ci-${{ github.event.pull_request.number || github.run_id }}~${{ github.event_name == 'pull_request' }}~accept +on:\n pull_request_target:~ci-${{ github.ref }}~${{ github.event_name == 'pull_request_target' }}~accept +on:\n pull_request:~ci-${{ github.sha }}~true~reject +on:\n pull_request:~ci-${{ github.run_id }}~true~reject +on:\n pull_request:~ci-${{ github.ref }}~${{ false }}~reject +on:\n pull_request:~ci-${{ github.ref }}~${{ github.event_name == 'push' }}~reject +on:\n pull_request:~ci-${{ github.ref }}~${{ github.event_name != 'pull_request' }}~reject +on:\n pull_request:~ci-${{ github.ref }}~${{ github.event_name == 'pull_request_target' }}~reject +on:\n pull_request:\n pull_request_target:~ci-${{ github.ref }}~${{ github.event_name == 'pull_request' }}~reject +CASES rm -f "$probe" - for file in "$ROOT_DIR"/.github/workflows/*.yml; do - grep -qE '^ pull_request(_target)?:' "$file" || continue + for file in "$ROOT_DIR"/.github/workflows/*.yml "$ROOT_DIR"/.github/workflows/*.yaml; do + [ -f "$file" ] || continue + [ -n "$(pr_workflow_events "$file")" ] || continue grep -qE 'runs-on:.*(macos|MACOS_RUNNER)' "$file" || continue if ! pr_concurrency_cancels_superseded_runs "$file"; then - echo "FAIL: $(basename "$file") runs macOS jobs on pull requests but a new push does not cancel the previous run; key the concurrency group on the pull request and set cancel-in-progress for pull_request events" + echo "FAIL: $(basename "$file") runs macOS jobs on pull requests but a new push does not cancel the previous run; key the concurrency group on the pull request and set cancel-in-progress for its pull request events" failed=1 fi done From 695a7ad4ea8546722807a86bc8d55f57dea3f89c Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sat, 19 Sep 2026 16:25:48 -0700 Subject: [PATCH 6/6] test: tighten the superseded-run guard's group and condition checks The condition must be exactly the event equality expression, so a compound that is always false no longer passes. A pull_request_target workflow must key its group on the pull request number, since github.ref is the base branch there, and github.head_ref alone is no longer accepted. A macOS workflow whose on: section names pull_request in a form the reader does not understand now fails instead of being skipped. Co-Authored-By: Claude Fable 5.1 --- tests/test_ci_self_hosted_guard.sh | 40 ++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/tests/test_ci_self_hosted_guard.sh b/tests/test_ci_self_hosted_guard.sh index 5c62ffffd160..3255673eb82c 100755 --- a/tests/test_ci_self_hosted_guard.sh +++ b/tests/test_ci_self_hosted_guard.sh @@ -1319,14 +1319,19 @@ pr_concurrency_cancels_superseded_runs() { # cancel-in-progress must be true for every pull request event the workflow # triggers on. local file="$1" event - local events + local events group_key events="$(pr_workflow_events "$file")" [ -n "$events" ] || return 1 - awk ' + # github.ref is the base branch on pull_request_target, so only the pull + # request number separates two pull requests there. + group_key='github\.(event\.pull_request\.number|ref)([^_a-z]|$)' + if grep -qx 'pull_request_target' <<<"$events"; then + group_key='github\.event\.pull_request\.number([^_a-z]|$)' + fi + GROUP_KEY="$group_key" awk ' /^concurrency:/ { in_block=1; next } in_block && /^[^[:space:]]/ { in_block=0 } - in_block && /^[[:space:]]+group:/ \ - && /github\.(event\.pull_request\.number|ref|head_ref)([^_a-z]|$)/ { group_ok=1 } + in_block && /^[[:space:]]+group:/ && $0 ~ ENVIRON["GROUP_KEY"] { group_ok=1 } END { exit !group_ok } ' "$file" || return 1 for event in $events; do @@ -1334,8 +1339,12 @@ pr_concurrency_cancels_superseded_runs() { /^concurrency:/ { in_block=1; next } in_block && /^[^[:space:]]/ { in_block=0 } in_block && /^[[:space:]]+cancel-in-progress:[[:space:]]*true[[:space:]]*$/ { ok=1 } - in_block && /^[[:space:]]+cancel-in-progress:/ \ - && index($0, "github.event_name == \047" ENVIRON["EVENT"] "\047") { ok=1 } + in_block && /^[[:space:]]+cancel-in-progress:/ { + value=$0 + sub(/^[[:space:]]+cancel-in-progress:[[:space:]]*/, "", value) + sub(/[[:space:]]+$/, "", value) + if (value == "${{ github.event_name == \047" ENVIRON["EVENT"] "\047 }}") ok=1 + } END { exit !ok } ' "$file" || return 1 done @@ -1361,7 +1370,10 @@ check_pr_macos_workflows_cancel_superseded_runs() { on:\n pull_request:~ci-${{ github.ref }}~true~accept on: pull_request~ci-${{ github.ref }}~${{ github.event_name == 'pull_request' }}~accept on: [push, pull_request]~ci-${{ github.event.pull_request.number || github.run_id }}~${{ github.event_name == 'pull_request' }}~accept -on:\n pull_request_target:~ci-${{ github.ref }}~${{ github.event_name == 'pull_request_target' }}~accept +on:\n pull_request_target:~ci-${{ github.event.pull_request.number }}~${{ github.event_name == 'pull_request_target' }}~accept +on:\n pull_request_target:~ci-${{ github.ref }}~true~reject +on:\n pull_request:~ci-${{ github.head_ref }}~true~reject +on:\n pull_request:~ci-${{ github.ref }}~${{ github.event_name == 'pull_request' && false }}~reject on:\n pull_request:~ci-${{ github.sha }}~true~reject on:\n pull_request:~ci-${{ github.run_id }}~true~reject on:\n pull_request:~ci-${{ github.ref }}~${{ false }}~reject @@ -1374,8 +1386,20 @@ CASES for file in "$ROOT_DIR"/.github/workflows/*.yml "$ROOT_DIR"/.github/workflows/*.yaml; do [ -f "$file" ] || continue - [ -n "$(pr_workflow_events "$file")" ] || continue grep -qE 'runs-on:.*(macos|MACOS_RUNNER)' "$file" || continue + if [ -z "$(pr_workflow_events "$file")" ]; then + # A quoted "on" key, flow mapping or other indentation is not read + # above. Fail instead of skipping a workflow that may run on pull requests. + if awk ' + /^["\047]?on["\047]?:/ { in_on=1; print; next } + in_on && /^[^[:space:]#]/ { in_on=0 } + in_on { print } + ' "$file" | grep -q 'pull_request'; then + echo "FAIL: $(basename "$file") names pull_request in a form this guard cannot read; write on: as a block mapping, a list or a single event" + failed=1 + fi + continue + fi if ! pr_concurrency_cancels_superseded_runs "$file"; then echo "FAIL: $(basename "$file") runs macOS jobs on pull requests but a new push does not cancel the previous run; key the concurrency group on the pull request and set cancel-in-progress for its pull request events" failed=1