Skip to content

Apply branch-protection rulesets to every repo #16

Description

@jorisjonkers-dev-agents

Context. Four repos have no main ruleset and homelab-deploy's live deploy/production branch (the one Flux reconciles) is unprotected. The agent's App can read rulesets but cannot create them (needs administration: write). You must run this as an org admin.

Depends on. Nothing — do this first.

Steps

  • Open a shell where gh auth status shows an admin identity (PAT with repo+admin:org, or you as owner).
  • Run the rollout script (full version in migration/branch-protection-instructions.md). It is idempotent:
ORG=JorisJonkers-dev; APP=15368
main_rs(){ cat <<JSON
{"name":"Main","target":"branch","enforcement":"active",
 "conditions":{"ref_name":{"include":["refs/heads/main"],"exclude":[]}},"bypass_actors":[],
 "rules":[{"type":"deletion"},{"type":"non_fast_forward"},{"type":"required_linear_history"},
  {"type":"pull_request","parameters":{"required_approving_review_count":0,"dismiss_stale_reviews_on_push":false,"required_reviewers":[],"require_code_owner_review":false,"require_last_push_approval":false,"required_review_thread_resolution":false,"allowed_merge_methods":["squash"]}},
  {"type":"required_status_checks","parameters":{"strict_required_status_checks_policy":false,"do_not_enforce_on_create":false,"required_status_checks":[{"context":"Pipeline Complete","integration_id":$APP}]}}]}
JSON
}
deployprod_rs(){ cat <<JSON
{"name":"Deploy Production","target":"branch","enforcement":"active",
 "conditions":{"ref_name":{"include":["refs/heads/deploy/production"],"exclude":[]}},
 "bypass_actors":[],
 "rules":[{"type":"deletion"},{"type":"non_fast_forward"}]}
JSON
}
has(){ gh api repos/$ORG/$1/rulesets --jq ".[]|select(.name==\"$2\")|.id" 2>/dev/null|head -1; }
for r in renovate-config vue-web-commons stack-integration-tests homelab-deploy; do
  [ -z "$(has $r Main)" ] && main_rs|gh api -X POST repos/$ORG/$r/rulesets --input - && echo "Main->$r"; done
gh api repos/$ORG/homelab-deploy/branches/deploy/production >/dev/null 2>&1 &&   [ -z "$(has homelab-deploy 'Deploy Production')" ] && deployprod_rs|gh api -X POST repos/$ORG/homelab-deploy/rulesets --input - && echo "DeployProd->homelab-deploy"
  • Verify every repo lists a Main ruleset:
for r in $(gh repo list $ORG --limit 100 --json name --jq '.[].name'); do printf "%-26s " $r; gh api repos/$ORG/$r/rulesets --jq '[.[].name]|join(",")'; done

Acceptance

  • All repos show a Main ruleset.
  • homelab-deploy shows a Deploy Production ruleset with the App (id 15368) as sole bypass actor.
  • A test direct-push to main is rejected; release PRs still merge (0-approval + Pipeline Complete).

Note (deploy/production): No bypass actor is needed — the promote workflow updates deploy/production with a fast-forward push (git merge --ff-only), which the non_fast_forward rule allows. The rule only blocks force-pushes/rewrites and deletion, for everyone. (An earlier version added the App as an Integration bypass actor, which GitHub rejects via this API with a 422; it's unnecessary, so it was removed.)


Where to run: Run from anywhere on your machine (e.g. your home dir) — these are gh commands that call the GitHub API and name each repo explicitly (repos/$ORG/<repo> / $ORG/<repo>), so you do not need to be inside a cloned repo. The only requirement is gh auth status showing an org-admin identity (PAT with repo+admin:org, or your owner login). Steps that scan files (e.g. gitleaks) are the exception and need a local checkout — clone a throwaway copy first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    governanceRepo protection, permissions, visibilityowner-actionRequires owner/admin access — not automatable by the agent

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions