Manage repository settings as code with YAML + GitHub CLI.
A GitHub CLI extension to manage repository settings via YAML configuration. Inspired by Terraform's workflow - define your desired state in code, see planned changes, and apply them.
GitHub repository settings are hard to manage consistently:
- Clicking through the Settings UI doesn't scale
- Repository admins often drift from the desired configuration
- Terraform's GitHub provider is powerful, but requires:
- A separate backend (state management)
- GitHub provider authentication setup
- Managing HCL files separately from your repository
gh-repo-settings provides:
- Zero backend - No state files to manage
- Zero external dependencies - Works entirely through GitHub CLI
- YAML configuration - Lives alongside your code in
.github/ - Terraform-like workflow - Familiar
plan/applycommands - Workflow validation - Detects mismatches between
status_checksand actual workflow job names
- Infrastructure as Code: Define repository settings in YAML files
- Terraform-like workflow:
planto preview,applyto execute - Export existing settings: Generate YAML from current repository configuration
- Schema validation: Validate configuration before applying
- Multiple config formats: Single file or directory-based configuration
- Secrets/Env check: Verify required secrets and environment variables exist
- Actions permissions: Configure GitHub Actions permissions and workflow settings
gh extension install myzkey/gh-repo-settingsgh extension upgrade myzkey/gh-repo-settingsDownload the latest binary from Releases and add it to your PATH.
# Initialize config interactively
gh repo-settings init
# Preview changes (like terraform plan)
gh repo-settings plan
# Apply changes
gh repo-settings applyDefault config paths (in priority order):
.github/repo-settings/(directory).github/repo-settings.yaml(single file)
Create a configuration file interactively.
# Create .github/repo-settings.yaml interactively
gh repo-settings init
# Specify output path
gh repo-settings init -o config.yaml
# Overwrite existing file
gh repo-settings init -fExport current GitHub repository settings to YAML format.
# Export to stdout
gh repo-settings export
# Export to single file
gh repo-settings export -s .github/repo-settings.yaml
# Export to directory (multiple files)
gh repo-settings export -d .github/repo-settings/
# Include secret names
gh repo-settings export -s settings.yaml --include-secrets
# Export from specific repository
gh repo-settings export -r owner/repo -s settings.yamlValidate configuration and show planned changes without applying them.
Repository: owner/my-repo
repo:
~ description: "Old description" → "New description"
labels:
+ feature (color: 0e8a16)
~ bug: color ff0000 → d73a4a
- old-label
branch_protection (main):
~ required_reviews: 1 → 2
Plan: 2 to add, 2 to change, 1 to delete# Preview all changes (uses default config path)
gh repo-settings plan
# Specify config file
gh repo-settings plan -c custom-config.yaml
# Specify config directory
gh repo-settings plan -d .github/repo-settings/
# Show current GitHub settings (useful for debugging)
gh repo-settings plan --show-current
# Check secrets
gh repo-settings plan --secrets
# Check environment variables
gh repo-settings plan --env
# Show variables/secrets to delete (not in config)
gh repo-settings plan --env --secrets --syncThe --show-current option displays the current GitHub repository settings, which is useful for:
- Debugging configuration issues
- Finding settings that exist on GitHub but are not in your config file
- Verifying what's actually configured on the repository
Status Check Validation: When running plan, the tool automatically validates that status_checks names in your branch protection rules match the job names defined in your .github/workflows/ files. If a mismatch is found, you'll see a warning:
⚠ status check lint not found in workflows
⚠ status check test not found in workflows
Available checks: build, golangci-lint, Run tests
Apply YAML configuration to the GitHub repository.
# Apply changes (uses default config path)
gh repo-settings apply
# Auto-approve without confirmation
gh repo-settings apply -y
# Specify config file
gh repo-settings apply -c custom-config.yaml
# Apply from directory
gh repo-settings apply -d .github/repo-settings/
# Apply variables and secrets
gh repo-settings apply --env --secrets
# Sync mode: delete variables/secrets not in config
gh repo-settings apply --env --secrets --syncThe --sync flag enables destructive operations:
- Deletes labels not defined in your config (when
labels.replace_default: true) - Deletes variables not defined in your config
- Deletes secrets not defined in your config
Always run plan --sync first to preview what will be deleted:
# Preview deletions BEFORE applying
gh repo-settings plan --env --secrets --sync
# Only then apply if the plan looks correct
gh repo-settings apply --env --secrets --syncTip: Avoid using
--syncin CI without human review.
Create .github/repo-settings.yaml:
repo:
description: "My awesome project"
homepage: "https://example.com"
visibility: public
allow_merge_commit: false
allow_rebase_merge: true
allow_squash_merge: true
delete_branch_on_merge: true
topics:
- typescript
- cli
- github
labels:
replace_default: true
items:
- name: bug
color: ff0000
description: Something isn't working
- name: feature
color: 0e8a16
description: New feature request
branch_protection:
main:
required_reviews: 1
dismiss_stale_reviews: true
require_status_checks: true
status_checks:
- ci/test
- ci/lint
enforce_admins: false
env:
variables:
NODE_ENV: production
API_URL: https://api.example.com
secrets:
- API_TOKEN
- DEPLOY_KEY
actions:
enabled: true
allowed_actions: selected
selected_actions:
github_owned_allowed: true
verified_allowed: true
patterns_allowed:
- "actions/*"
default_workflow_permissions: read
can_approve_pull_request_reviews: falseAlternatively, split configuration into multiple files:
.github/repo-settings/
├── repo.yaml
├── topics.yaml
├── labels.yaml
├── branch-protection.yaml
├── env.yaml
└── actions.yaml
| Field | Type | Description |
|---|---|---|
description |
string | Repository description |
homepage |
string | Homepage URL |
visibility |
public | private | internal |
Repository visibility |
allow_merge_commit |
boolean | Allow merge commits |
allow_rebase_merge |
boolean | Allow rebase merging |
allow_squash_merge |
boolean | Allow squash merging |
delete_branch_on_merge |
boolean | Auto-delete head branches |
allow_update_branch |
boolean | Allow updating PR branches |
Array of topic strings:
topics:
- javascript
- nodejs
- cli| Field | Type | Description |
|---|---|---|
replace_default |
boolean | Delete labels not in config |
items |
array | List of label definitions |
items[].name |
string | Label name |
items[].color |
string | Hex color (without #) |
items[].description |
string | Label description |
branch_protection:
<branch_name>:
# Pull request reviews
required_reviews: 1 # Number of required approvals
dismiss_stale_reviews: true # Dismiss approvals on new commits
require_code_owner: false # Require CODEOWNERS review
# Status checks
require_status_checks: true # Require status checks
status_checks: # Required status check names
- ci/test
strict_status_checks: false # Require up-to-date branches
# Deployments
required_deployments: # Required deployment environments
- production
# Commit requirements
require_signed_commits: false # Require signed commits
require_linear_history: false # Prevent merge commits
# Push/merge restrictions
enforce_admins: false # Include administrators
restrict_creations: false # Restrict branch creation
restrict_pushes: false # Restrict who can push
allow_force_pushes: false # Allow force pushes
allow_deletions: false # Allow branch deletionManage repository variables and secrets:
env:
# Variables with default values (can be overridden by .env file)
variables:
NODE_ENV: production
API_URL: https://api.example.com
# Secret names (values come from .env file or interactive prompt)
secrets:
- API_TOKEN
- DEPLOY_KEY| Field | Type | Description |
|---|---|---|
variables |
map | Key-value pairs for repository variables |
secrets |
array | List of secret names to manage |
Create a .github/.env file (gitignored) to store actual values:
# .github/.env
NODE_ENV=staging
API_URL=https://staging-api.example.com
API_TOKEN=your-secret-token
DEPLOY_KEY=your-deploy-keyPriority: .env file values override YAML defaults for variables.
# Preview variable/secret changes
gh repo-settings plan --env --secrets
# Apply variables and secrets
gh repo-settings apply --env --secrets
# Delete variables/secrets not in config (sync mode)
gh repo-settings apply --env --secrets --syncIf a secret value is not found in .env, you'll be prompted to enter it interactively during apply.
You can automatically load secrets from AWS Secrets Manager and write them to .env:
env:
provider:
name: secretsmanager
secret: /myapp/prod/secrets # Secret name in AWS Secrets Manager
region: ap-northeast-1This fetches all keys from the secret JSON and writes them to .github/.env.
Example: If your AWS secret contains:
{
"API_TOKEN": "secret-value",
"DB_PASSWORD": "db-secret"
}Running gh repo-settings plan --secrets will write to .github/.env:
# Added by provider: 2024-01-15 10:30:00
API_TOKEN=secret-value
DB_PASSWORD=db-secret
Filter specific keys: To load only specific keys, specify them in secrets:
env:
provider:
name: secretsmanager
secret: /myapp/prod/secrets
region: ap-northeast-1
secrets:
- API_TOKEN # Only load this keyMemory mode: To load secrets without writing to file (in-memory only):
env:
provider:
name: secretsmanager
secret: /myapp/prod/secrets
region: ap-northeast-1
output: memory| Field | Type | Description |
|---|---|---|
provider.name |
secretsmanager |
Provider type |
provider.secret |
string | Secret name/path in AWS Secrets Manager |
provider.region |
string | AWS region |
provider.output |
file | memory |
Output mode (default: file) |
Note: Requires AWS CLI configured with appropriate credentials.
Configure GitHub Actions permissions for the repository:
actions:
# Enable/disable GitHub Actions
enabled: true
# Which actions can be used: "all", "local_only", "selected"
allowed_actions: selected
# When allowed_actions is "selected"
selected_actions:
github_owned_allowed: true # Allow actions from GitHub
verified_allowed: true # Allow actions from verified creators
patterns_allowed: # Allow specific action patterns
- "actions/*"
- "github/codeql-action/*"
# Default GITHUB_TOKEN permissions: "read" or "write"
default_workflow_permissions: read
# Allow GitHub Actions to create/approve pull requests
can_approve_pull_request_reviews: false| Field | Type | Description |
|---|---|---|
enabled |
boolean | Enable GitHub Actions for this repository |
allowed_actions |
all | local_only | selected |
Which actions are allowed |
selected_actions.github_owned_allowed |
boolean | Allow actions created by GitHub |
selected_actions.verified_allowed |
boolean | Allow actions from verified creators |
selected_actions.patterns_allowed |
array | Patterns for allowed actions |
default_workflow_permissions |
read | write |
Default GITHUB_TOKEN permissions |
can_approve_pull_request_reviews |
boolean | Allow Actions to approve PRs |
Configure GitHub Pages for the repository:
pages:
# Build type: "workflow" (GitHub Actions) or "legacy" (branch-based)
build_type: workflow
# Source configuration (only for legacy build type)
source:
branch: main
path: /docs # "/" or "/docs"| Field | Type | Description |
|---|---|---|
build_type |
workflow | legacy |
How Pages is built |
source.branch |
string | Branch for legacy builds |
source.path |
/ | /docs |
Path within the branch |
This project provides a JSON Schema for YAML validation and auto-completion in VSCode.
-
Install the YAML extension
-
Add to your
.vscode/settings.json:
{
"yaml.schemas": {
"https://raw.githubusercontent.com/myzkey/gh-repo-settings/main/schema.json": [
".github/repo-settings.yaml",
".github/repo-settings/*.yaml"
]
}
}- Auto-completion for all fields
- Hover documentation
- Enum suggestions (
public/private/internal,read/write, etc.) - Unknown field detection
- Type validation
name: Repo Settings Check
on:
pull_request:
paths:
- ".github/repo-settings.yaml"
- ".github/repo-settings/**"
jobs:
check:
runs-on: ubuntu-latest
permissions:
contents: read
actions: read
steps:
- uses: actions/checkout@v4
- name: Install gh-repo-settings
run: gh extension install myzkey/gh-repo-settings
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check drift
run: gh repo-settings plan
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}This tool applies settings to one repository per execution. To manage multiple repositories with the same configuration, use GitHub Actions matrix strategy:
name: Sync Settings Across Repos
on:
workflow_dispatch:
push:
branches: [main]
paths:
- ".github/repo-settings.yaml"
jobs:
sync:
runs-on: ubuntu-latest
strategy:
matrix:
repo:
- myorg/service-a
- myorg/service-b
- myorg/service-c
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Install gh-repo-settings
run: gh extension install myzkey/gh-repo-settings
env:
GH_TOKEN: ${{ secrets.ADMIN_TOKEN }}
- name: Apply settings to ${{ matrix.repo }}
run: gh repo-settings apply -y -r ${{ matrix.repo }}
env:
GH_TOKEN: ${{ secrets.ADMIN_TOKEN }}Note: Requires a PAT (
ADMIN_TOKEN) with admin access to all target repositories.
| Option | Description |
|---|---|
-v, --verbose |
Show debug output |
-q, --quiet |
Only show errors |
-r, --repo <owner/name> |
Target repository (default: current) |
This extension uses the GitHub CLI (gh) for authentication. Make sure you're logged in:
gh auth login| Feature | Required Scopes |
|---|---|
| Repository settings | repo |
| Branch protection | repo (admin access to repository) |
| Secrets check | repo, admin:repo_hook |
| Environment variables | repo |
| Actions permissions | repo, admin:repo_hook |
GITHUB_TOKEN(GitHub Actions): Works for most operations within the same repository. However, branch protection rules require admin access, whichGITHUB_TOKENmay not have by default.- Personal Access Token (PAT): Required for cross-repository operations or when
GITHUB_TOKENlacks sufficient permissions. Use a fine-grained PAT withRepository administrationpermission for full functionality.
Not directly. This tool manages one repository per execution.
To apply the same configuration across multiple repositories:
- Place the same YAML config in each repository, or
- Use GitHub Actions matrix strategy (see Managing Multiple Repositories)
Not natively. The env block manages one set of variables/secrets per repository.
For environment-specific values, you can:
- Use different
.envfiles (.env.dev,.env.staging,.env.prod) and switch them in CI - Use GitHub Environments (not yet supported by this tool)
The tool will show you the planned changes and ask for confirmation before applying. Use -y flag to skip confirmation (not recommended for first-time use).
No. This tool only manages repository-level settings. Organization settings require different API permissions and are out of scope.
# Build
make build
# Run tests
make test
# Lint (requires golangci-lint)
make lint
# Build for all platforms
make build-all
# Clean build artifacts
make cleanMIT