Skip to content
Merged
Show file tree
Hide file tree
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
61 changes: 61 additions & 0 deletions scripts/apply-repo-settings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,17 @@ DRY_RUN="${DRY_RUN:-false}"

info() { echo "[INFO] $*"; }
ok() { echo "[OK] $*"; }
warn() { echo "[WARN] $*" >&2; }
err() { echo "[ERROR] $*" >&2; }
skip() { echo "[SKIP] $*"; }

# App IDs whose auto_trigger_checks must be disabled — these apps create
# orphaned "queued" suites on every push that are never completed, permanently
# blocking GitHub auto-merge.
# 1236702 = Claude (anthropics/claude-code-action)
# 347564 = CodeRabbit
CHECK_SUITE_APP_IDS=(1236702 347564)

# Source the shared push-protection library — provides
# pp_apply_security_and_analysis() and the PP_REQUIRED_SA_SETTINGS list.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
Expand Down Expand Up @@ -226,6 +234,57 @@ apply_codeql_default_setup() {
fi
}

# ---------------------------------------------------------------------------
# apply_check_suite_prefs — disable auto-trigger check suites for Claude and
# CodeRabbit. Without this, GitHub auto-creates "queued" suites on every push
# that are never completed, permanently blocking auto-merge.
# ---------------------------------------------------------------------------
apply_check_suite_prefs() {
local repo="$1"
info "Configuring check-suite auto-trigger preferences for $ORG/$repo ..."

local prefs
if ! prefs=$(gh api "repos/$ORG/$repo/check-suites/preferences" 2>&1); then
err " Could not read check-suite preferences for $repo. API response: $prefs"
return 1
Comment thread
coderabbitai[bot] marked this conversation as resolved.
fi

local all_disabled=true
for app_id in "${CHECK_SUITE_APP_IDS[@]}"; do
local setting
setting=$(echo "$prefs" | jq -r --argjson id "$app_id" \
'.preferences.auto_trigger_checks // [] | map(select(.app_id == $id)) | first | .setting // "missing"')
# "missing" means the app has never run in this repo — no orphaned suite possible, skip
if [ "$setting" != "false" ] && [ "$setting" != "missing" ]; then
all_disabled=false
fi
done

if [ "$all_disabled" = true ]; then
ok "$ORG/$repo check-suite prefs already correct"
return 0
fi

if [ "${DRY_RUN:-false}" = "true" ]; then
skip "DRY_RUN — skipping check-suite prefs PATCH for $repo"
return 0
fi

local payload
payload=$(printf '%s\n' "${CHECK_SUITE_APP_IDS[@]}" | \
jq -Rs 'split("\n") | map(select(. != "")) | map(tonumber) |
{"auto_trigger_checks": map({"app_id": ., "setting": false})}')

local api_err
if api_err=$(gh api -X PATCH "repos/$ORG/$repo/check-suites/preferences" \
--input - <<< "$payload" 2>&1 >/dev/null); then
ok " auto-trigger disabled for app_ids: ${CHECK_SUITE_APP_IDS[*]}"
else
err " PATCH failed for $repo. API response: $api_err"
return 1
fi
}

# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -263,6 +322,7 @@ if [ "$1" = "--all" ]; then
apply_labels "$repo"
pp_apply_security_and_analysis "$repo" || failed=$((failed + 1))
apply_codeql_default_setup "$repo" || failed=$((failed + 1))
apply_check_suite_prefs "$repo" || failed=$((failed + 1))
done

if [ "$failed" -gt 0 ]; then
Expand All @@ -282,4 +342,5 @@ else
apply_labels "$1"
pp_apply_security_and_analysis "$1"
apply_codeql_default_setup "$1"
apply_check_suite_prefs "$1"
fi
40 changes: 40 additions & 0 deletions scripts/compliance-audit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ REQUIRED_LABEL_SPECS=(
"in-progress:fbca04:An agent is actively working this issue"
)

# App IDs whose auto_trigger_checks must be disabled org-wide.
# 1236702 = Claude (anthropics/claude-code-action)
# 347564 = CodeRabbit
CHECK_SUITE_APP_IDS=(1236702 347564)

REQUIRED_SETTINGS_BOOL=(
"allow_auto_merge:true:warning:Allow auto-merge must be enabled for Dependabot workflow"
"delete_branch_on_merge:true:warning:Automatically delete head branches must be enabled"
Expand Down Expand Up @@ -853,6 +858,40 @@ check_agents_md() {
fi
}

# ---------------------------------------------------------------------------
# Check: check-suite auto-trigger disabled for Claude and CodeRabbit
# ---------------------------------------------------------------------------
check_check_suite_prefs() {
local repo="$1"

local prefs
prefs=$(gh_api "repos/$ORG/$repo/check-suites/preferences" 2>/dev/null || true)
if [ -z "$prefs" ]; then
add_finding "$repo" "settings" "check-suite-prefs-unreadable" "warning" \
"Could not read check-suite preferences — verify GH_TOKEN has repo scope and the repo is accessible. Check-suite auto-trigger compliance was not evaluated." \
"standards/github-settings.md"
return
fi

Comment thread
coderabbitai[bot] marked this conversation as resolved.
for app_id in "${CHECK_SUITE_APP_IDS[@]}"; do
local setting
setting=$(echo "$prefs" | jq -r --argjson id "$app_id" \
'.preferences.auto_trigger_checks // [] | map(select(.app_id == $id)) | first | .setting // "missing"')

# "missing" means the app has never run in this repo — no orphaned suite possible
[ "$setting" = "missing" ] && continue
[ "$setting" = "false" ] && continue

local app_label="app_id=$app_id"
[ "$app_id" = "1236702" ] && app_label="Claude (1236702)"
[ "$app_id" = "347564" ] && app_label="CodeRabbit (347564)"

add_finding "$repo" "settings" "check-suite-auto-trigger-${app_id}" "error" \
"$app_label auto-trigger is enabled: GitHub creates a queued check suite on every push that is never completed, permanently blocking auto-merge. Run: \`bash scripts/apply-repo-settings.sh $repo\`" \
"standards/github-settings.md"
Comment thread
don-petry marked this conversation as resolved.
done
}

# ---------------------------------------------------------------------------
# Issue management
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -1299,6 +1338,7 @@ main() {
check_centralized_check_names "$repo"
check_claude_md "$repo"
check_agents_md "$repo"
check_check_suite_prefs "$repo"
pp_run_all_checks "$repo"

log_end
Expand Down
29 changes: 29 additions & 0 deletions standards/github-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,35 @@ See [CI Standards](ci-standards.md) for workflow templates and patterns.
| **SonarQube Cloud (SonarCloud)** | Code quality, security hotspots, coverage tracking | 2026-03-25 |
| **CodeRabbit AI** | AI-powered code review on PRs | 2026-03-25 |

### Check-Suite Auto-Trigger Preferences

GitHub automatically creates a check suite for any app that has previously created check runs in a repo, on every push.
Some apps (Claude, CodeRabbit) create these suites proactively but only complete them when they have real work to do.
When they have nothing to do, the suite stays in `queued` state indefinitely —
**GitHub auto-merge waits for all check suites to reach a terminal state before merging**,
so these orphaned suites permanently block auto-merge.

**Required configuration** (enforced by `scripts/apply-repo-settings.sh` and detected by `scripts/compliance-audit.sh`):

| App | app_id | Setting |
|-----|--------|---------|
| Claude (`anthropics/claude-code-action`) | `1236702` | `auto_trigger_checks: false` |
| CodeRabbit | `347564` | `auto_trigger_checks: false` |

Disabling auto-trigger stops GitHub from creating suites on every push. The apps still create suites explicitly when they have work to report.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

If an app has never created a check run in a repository, GitHub omits that app from `auto_trigger_checks` entirely.
Both `scripts/apply-repo-settings.sh` and `scripts/compliance-audit.sh` treat this `missing` state as compliant —
no PATCH is needed and no finding is raised until the app is first seen in the repo.

**Applying manually** (requires a classic PAT with `repo` scope — OAuth app tokens are rejected by this API endpoint):

```bash
GH_TOKEN=<classic-pat> bash scripts/apply-repo-settings.sh <repo-name>
# or for all org repos:
GH_TOKEN=<classic-pat> bash scripts/apply-repo-settings.sh --all
```

### Other Integrations

| Integration | Purpose | Scope |
Expand Down
Loading