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
70 changes: 36 additions & 34 deletions .github/scripts/tests/apply-repo-settings-workflow.bats
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
# Tests for .github/workflows/apply-repo-settings.yml — static YAML assertions,
# no live API calls.
#
# This workflow is now a THIN CALLER STUB synced from the org source of truth
# (petry-projects/.github/standards/workflows/apply-repo-settings.yml). All
# settings/ruleset logic — including the #420 GH_TOKEN preflight guard — moved
# into the org reusable workflow (apply-repo-settings-reusable.yml); the consumer
# repo carries no script copy and the caller job has no steps. These tests were
# rewritten to pin the stub contract instead of the retired inline job.
#
# Regression guard for issue #403 (ruleset-drift-pr-quality-require_last_push_approval):
# the codified pr-quality ruleset already sets require_last_push_approval: true, but the
# live ruleset kept drifting back to false because apply-repo-settings.yml only re-applied
# the codified standard on manual dispatch or on a push touching the ruleset scripts.
# A scheduled trigger makes the codified rulesets self-heal drift automatically. These
# tests pin that the schedule trigger exists and that the pre-existing triggers survive.
# a scheduled trigger makes the codified rulesets self-heal live drift automatically.
# The schedule/workflow_dispatch guards below survive the migration to the stub.

WORKFLOW="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." && pwd)/.github/workflows/apply-repo-settings.yml"

Expand Down Expand Up @@ -36,52 +40,50 @@ WORKFLOW="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." && pwd)/.github/workf
[ "$output" = "true" ]
}

@test "workflow preserves the push trigger on main touching the ruleset scripts" {
@test "workflow preserves the push trigger on main touching the stub itself" {
run yq '.on.push.branches[0]' "$WORKFLOW"
[ "$status" -eq 0 ]
[ "$output" = "main" ]

run yq '.on.push.paths | contains([".github/scripts/apply-pr-quality-ruleset.sh"])' "$WORKFLOW"
# The stub carries no script copy, so it re-applies on a push to its own file.
run yq '.on.push.paths | contains([".github/workflows/apply-repo-settings.yml"])' "$WORKFLOW"
[ "$status" -eq 0 ]
[ "$output" = "true" ]
}

# Regression guard for issue #420 (ruleset-drift-pr-quality-require_last_push_approval
# recurred after #403). The #403 schedule cannot self-heal drift because every run —
# including the scheduled one — fails immediately with "GH_TOKEN is required": the
# GH_TOKEN_ADMIN secret is not configured, and the failure was buried in the script
# log where nobody noticed. These tests pin a preflight step that surfaces the missing
# secret loudly (GitHub error annotation + job summary) with actionable remediation, so
# the operational blocker is discoverable instead of silently recurring.
# Thin-caller-stub contract: the job delegates to the org reusable workflow and
# carries no inline steps of its own (settings/ruleset logic lives upstream).

@test "workflow has a preflight step that verifies GH_TOKEN_ADMIN before applying (issue #420)" {
run yq '[.jobs["apply-settings"].steps[] | select(.env.GH_TOKEN_ADMIN != null and ((.run // "") | test("GH_TOKEN_ADMIN")))] | length' "$WORKFLOW"
@test "apply job delegates to the org reusable workflow via uses:" {
run yq '.jobs.apply.uses' "$WORKFLOW"
[ "$status" -eq 0 ]
[ "$output" -ge 1 ]
[ "$output" != "null" ]
printf '%s\n' "$output" | grep -q 'petry-projects/.github/.github/workflows/apply-repo-settings-reusable.yml@'
}

@test "apply job carries no inline steps (logic lives in the reusable)" {
run yq '.jobs.apply | has("steps")' "$WORKFLOW"
[ "$status" -eq 0 ]
[ "$output" = "false" ]
}

@test "preflight step surfaces the missing secret loudly and actionably (issue #420)" {
run yq '.jobs["apply-settings"].steps[] | select(.env.GH_TOKEN_ADMIN != null and ((.run // "") | test("GH_TOKEN_ADMIN"))) | .run' "$WORKFLOW"
@test "apply job inherits secrets so the reusable gets the admin token" {
run yq '.jobs.apply.secrets' "$WORKFLOW"
[ "$status" -eq 0 ]
local preflight="$output"
# emits a GitHub error annotation so the failure is visible on the run page
printf '%s\n' "$preflight" | grep -q '::error'
# writes actionable remediation to the job summary
printf '%s\n' "$preflight" | grep -q 'GITHUB_STEP_SUMMARY'
# fails the job so the drift is not silently left unhealed
printf '%s\n' "$preflight" | grep -q 'exit 1'
[ "$output" = "inherit" ]
}

@test "preflight runs before the apply-repo-settings step (issue #420)" {
run yq '.jobs["apply-settings"].steps | to_entries | map(select(.value.env.GH_TOKEN_ADMIN != null and ((.value.run // "") | test("GH_TOKEN_ADMIN")))) | .[0].key' "$WORKFLOW"
@test "checkout_ref forward stays in lockstep with the uses: channel pin" {
run yq '.jobs.apply.with.checkout_ref' "$WORKFLOW"
[ "$status" -eq 0 ]
local preflight_idx="$output"
local checkout_ref="$output"
[ "$checkout_ref" != "null" ]
[ -n "$checkout_ref" ]

run yq '.jobs["apply-settings"].steps | to_entries | map(select((.value.run // "") | test("apply-repo-settings.sh"))) | .[0].key' "$WORKFLOW"
run yq '.jobs.apply.uses' "$WORKFLOW"
[ "$status" -eq 0 ]
local apply_idx="$output"
local uses="$output"

[ -n "$preflight_idx" ] && [ "$preflight_idx" != "null" ]
[ -n "$apply_idx" ] && [ "$apply_idx" != "null" ]
[ "$preflight_idx" -lt "$apply_idx" ]
# `uses:` ends with @<channel-tag>; checkout_ref must equal that same tag.
[ "${uses##*@}" = "$checkout_ref" ]
}
96 changes: 40 additions & 56 deletions .github/workflows/apply-repo-settings.yml
Original file line number Diff line number Diff line change
@@ -1,71 +1,55 @@
# Apply repository settings — disables check-suite auto-triggers (including
# Claude app 1236702) that permanently block auto-merge, and enforces all other
# required settings from standards/github-settings.md.
# ─────────────────────────────────────────────────────────────────────────────
# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/apply-repo-settings.yml
# Standard: petry-projects/.github/standards/github-settings.md
# petry-projects/.github/standards/ruleset-remediation-runbook.md
# Reusable: petry-projects/.github/.github/workflows/apply-repo-settings-reusable.yml
#
# Triggers:
# • workflow_dispatch — manual run to fix current state immediately
# • push to main on the settings script — re-applies on every future change
# • schedule — weekly re-apply so codified rulesets self-heal live drift.
# Without this, a ruleset that is manually changed in the GitHub UI (e.g.
# pr-quality's require_last_push_approval flipped to false) stays drifted
# until someone edits a script path, which is why compliance drift findings
# recurred (#325 → #403). The apply scripts are idempotent, so re-running is
# safe.
# AGENTS — READ BEFORE EDITING:
# • This file is a THIN CALLER STUB. All settings/ruleset logic lives in the
# reusable workflow above; consumer repos carry NO script copy.
# • You MAY change: the cron offset only (stagger org API load).
# • You MUST NOT change: the `uses:` line, `secrets: inherit`, the job-level
# `permissions:` block, or the `checkout_ref` forward — reusable workflows can
# be granted no more permissions than the calling job has, and forwarding an
# input the pinned channel does not declare breaks every run at startup (#1034).
# • If you need different behaviour, open a PR against the reusable in the
# central repo.
# ─────────────────────────────────────────────────────────────────────────────
#
# Required secret: GH_TOKEN_ADMIN — a classic PAT or fine-grained token with
# administration:write (repo admin) scope for petry-projects/markets.
# Apply repo settings — thin caller for the org-level compliance self-heal.
# To adopt: copy this file verbatim to .github/workflows/apply-repo-settings.yml.
# Required org secret (inherited):
# GH_PAT_DON_PETRY — classic PAT, repo scope, owner has admin on this repo.
name: Apply repo settings

on:
workflow_dispatch:
schedule:
- cron: "23 5 * * 1"
# Weekly self-heal. The cron offset MAY be changed to stagger org API load.
- cron: '17 6 * * 1'
workflow_dispatch:
inputs:
dry_run:
description: "Preview changes without applying"
type: boolean
required: false
default: false
push:
branches: [main]
paths:
- .github/scripts/apply-repo-settings.sh
- .github/scripts/apply-code-quality-ruleset.sh
- .github/scripts/apply-pr-quality-ruleset.sh
- .github/workflows/apply-repo-settings.yml
- '.github/workflows/apply-repo-settings.yml'
Comment thread
don-petry marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

permissions: {}

concurrency:
group: apply-repo-settings
cancel-in-progress: false
Comment thread
codeant-ai[bot] marked this conversation as resolved.

jobs:
apply-settings:
name: Apply repo settings
runs-on: ubuntu-latest
apply:
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

# Preflight — without GH_TOKEN_ADMIN the apply step dies with a generic
# "GH_TOKEN is required" buried in the script log, so the weekly self-heal
# fails unnoticed and ruleset drift recurs (#325 → #403 → #420). Surface the
# missing secret loudly (error annotation + job summary) so it gets fixed.
- name: Verify admin token is configured
env:
GH_TOKEN_ADMIN: ${{ secrets.GH_TOKEN_ADMIN }}
run: |
if [ -z "${GH_TOKEN_ADMIN}" ]; then
{
echo "## Apply repo settings cannot run"
echo ""
echo "The \`GH_TOKEN_ADMIN\` secret is not configured for this repository."
echo "Without it the pr-quality / code-quality rulesets cannot self-heal, so"
echo "compliance drift (e.g. \`require_last_push_approval\` flipping to false)"
echo "recurs unnoticed."
echo ""
echo "**Fix:** provision a repo/org admin token secret named \`GH_TOKEN_ADMIN\`"
echo "with \`administration:write\` scope (see standards/github-settings.md),"
echo "then re-run this workflow."
} >> "$GITHUB_STEP_SUMMARY"
echo "::error title=GH_TOKEN_ADMIN missing::Provision the GH_TOKEN_ADMIN secret so ruleset drift can self-heal (see job summary)."
exit 1
fi
echo "GH_TOKEN_ADMIN is configured."

- name: Apply repo settings
env:
GH_TOKEN: ${{ secrets.GH_TOKEN_ADMIN }}
run: bash .github/scripts/apply-repo-settings.sh
uses: petry-projects/.github/.github/workflows/apply-repo-settings-reusable.yml@apply-repo-settings/v1-ring1 # NOSONAR(githubactions:S7637) first-party channel ref
Comment thread
don-petry marked this conversation as resolved.
with:
dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run == true }}
checkout_ref: apply-repo-settings/v1-ring1 # keep in lockstep with the uses: channel pin
secrets: inherit # NOSONAR(githubactions:S7635) first-party trusted reusable
Loading