Repo settings as code: rulesets, settings JSON and setup-repo.py - #2
Merged
Merged
Conversation
Scaffolds .github/repo-settings.json, .github/rulesets/main.json and scripts/setup-repo.sh so a new repo's default branch gets protected from files in the repo rather than by hand in the GitHub UI. Ported from the worker template's open PR and adapted to this template's CI check names; the template repo carries and applies its own copies. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
gh's default-repo guess picked a fork on a checkout with two remotes and reconfigured it silently. Resolve the 'origin' URL instead, and accept an OWNER/REPO argument for cases like the template repo applying its own copies. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Off by default so copier update --defaults never lands the files, or the post-copy invitation to run them, in a downstream repo that didn't ask. Directory names carry the condition so declining leaves no empty dirs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The bash version had grown embedded jq programs and parallel arrays, and a jq input() misuse made every ruleset look new. The stdlib Python script fetches the repo's current settings and each ruleset, prints what would change (settings keys, and a unified diff of each ruleset projected onto the keys the file sets), and applies only after confirmation or --yes; --dry-run only shows. Unit-tested against a stubbed gh in template CI. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The smoke-test job runs pre-commit over every tracked file with ruff's defaults (line length 88), while the scaffold formats at 100. Format so both are satisfied. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The smoke-test job runs pre-commit over every tracked file, and without a root config ruff formatted the template's own Python at its default line length, disagreeing with the scaffold's 100. Mirror the scaffold settings so the same file formats identically in both places. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ports the approach from Generality-Labs/cloudflare-worker-template#15 into this template and adapts it to the Python scaffold's CI check names.
Opt-in. A new
use_repo_settingsquestion, default no. Declining renders none of the files and no mention in the post-copy message, socopier update --defaults(the weekly template-update workflow) cannot land them in a downstream repo that didn't ask. An existing project opts in by settinguse_repo_settings: truein.copier-answers.ymland runningcopier update. Nothing touches GitHub until someone runs the script.Scaffold additions (when opted in)
.github/repo-settings.json: keys forPATCH /repos/{owner}/{repo}(merge methods,delete_branch_on_merge: true, wiki/projects off)..github/rulesets/main.json(templated): protect the default branch. Changes via PR with 0 required approvals, no force-push or deletion,ci / Lint, type-check, and testrequired, plusfrontend / Type-check and buildwhenuse_frontend. Repository Admins bypass.scripts/setup_repo.py: standard-library Python applier using the developer'sghauth. It fetches the repo's current settings and each existing ruleset first and prints what would change: settings keys with current and desired values, and a unified diff per ruleset projected onto the keys the file sets (so ids, timestamps and GitHub's filled-in defaults never appear). It applies only after confirmation or--yes;--dry-runonly shows; a non-terminal run without--yesrefuses. Targets theoriginremote or an explicitOWNER/REPO, never gh's default-repo guess (which picked the wrong repo on a two-remote checkout during testing). Only changed settings keys are sent; rulesets are created or updated in place by name.tests/test_setup_repo.pyin this repo exercises the script against a stubbedgh: no-op when everything matches, first-run create, drift diff and in-place update, dry-run, prompt yes/no, non-interactive refusal, plan-gate handling, missing settings file, explicit repo. Template CI runs it, plus render assertions for default, opt-in and frontend variants.This repo carries its own copies and applies them with the scaffolded script (
python3 'template/{% if use_repo_settings %}scripts{% endif %}/setup_repo.py' Generality-Labs/python-project-template).Verified against the live API (the org is on Team, so no plan gate):
protect-mainexists on this repo (id 22861394) and on Generality-Labs/inspect-evals-lint (id 22861417); a dry run against either reports "Nothing to apply", and a dry run with deliberately drifted local files shows exactly the drifted keys and ruleset lines.🤖 Generated with Claude Code
Posted by Claude Code on Matt's behalf.