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
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"
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
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.
Context. Four repos have no
mainruleset andhomelab-deploy's livedeploy/productionbranch (the one Flux reconciles) is unprotected. The agent's App can read rulesets but cannot create them (needsadministration: write). You must run this as an org admin.Depends on. Nothing — do this first.
Steps
gh auth statusshows an admin identity (PAT withrepo+admin:org, or you as owner).migration/branch-protection-instructions.md). It is idempotent:Mainruleset:Acceptance
Mainruleset.homelab-deployshows aDeploy Productionruleset with the App (id 15368) as sole bypass actor.mainis rejected; release PRs still merge (0-approval +Pipeline Complete).Where to run: Run from anywhere on your machine (e.g. your home dir) — these are
ghcommands 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 isgh auth statusshowing an org-admin identity (PAT withrepo+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.