Update repository settings in bulk across multiple GitHub repositories.
- 🔧 Update pull request merge strategies (squash, merge, rebase)
- ✅ Configure auto-merge settings
- 🗑️ Enable automatic branch deletion after merge
- 🔄 Configure pull request branch update suggestions
- 📊 Enable default CodeQL code scanning
- 🔒 Enable or disable immutable releases to prevent release deletion and modification
- 🏷️ Manage repository topics
- 🔄 Sync dependabot.yml files across repositories via pull requests
- 📋 Sync repository rulesets across repositories
- 📝 Sync pull request templates across repositories via pull requests
- 📝 Support multiple repository input methods (comma-separated, YAML file, or all org repos)
- 🔍 Dry-run mode with change preview and intelligent change detection
- 📋 Per-repository overrides via YAML configuration
- 📊 Comprehensive logging showing before/after values for all changes
- name: Update Repository Settings
uses: joshjohanning/bulk-github-repo-settings-sync-action@v1
with:
github-token: ${{ steps.app-token.outputs.token }}
repositories: 'owner/repo1,owner/repo2,owner/repo3'
allow-squash-merge: true
allow-merge-commit: false
delete-branch-on-merge: true
enable-default-code-scanning: true
immutable-releases: true
dependabot-yml: './config/dependabot/npm-actions.yml'
rulesets-file: './config/rulesets/prod-ruleset.json'
pull-request-template: './config/templates/pull_request_template.md'
topics: 'javascript,github-actions,automation'
dry-run: ${{ github.event_name == 'pull_request' }} # dry run if PRCreate a repos.yml file:
repos:
- repo: owner/repo1
allow-squash-merge: false # Override global setting
topics: 'javascript,special-config'
- repo: owner/repo2 # Uses global defaults
- repo: owner/repo3
enable-default-code-scanning: falseUse in workflow:
- name: Update Repository Settings with Overrides
uses: joshjohanning/bulk-github-repo-settings-sync-action@v1
with:
github-token: ${{ steps.app-token.outputs.token }}
repositories-file: 'repos.yml'
# Global defaults (overridden per-repo in YAML)
allow-squash-merge: true
delete-branch-on-merge: true
enable-default-code-scanning: true
topics: 'javascript,github-actions'Sync a dependabot.yml file to .github/dependabot.yml in target repositories via pull requests:
- name: Sync Dependabot Config
uses: joshjohanning/bulk-github-repo-settings-sync-action@v1
with:
github-token: ${{ steps.app-token.outputs.token }}
repositories-file: 'repos.yml'
dependabot-yml: './config/dependabot/npm-actions.yml'
dependabot-pr-title: 'chore: update dependabot.yml'Or with repo-specific overrides in repos.yml:
repos:
- repo: owner/repo1
dependabot-yml: './config/dependabot/npm-actions.yml'
- repo: owner/repo2
dependabot-yml: './config/dependabot/python.yml'
- repo: owner/repo3
dependabot-yml: './.github/dependabot.yml' # use the same config that this repo is usingBehavior:
- If
.github/dependabot.ymldoesn't exist, it creates it and opens a PR - If it exists but differs, it updates it via PR
- If content is identical, no PR is created
- PRs are created/updated using the GitHub API so commits are verified
- Updates existing open PRs instead of creating duplicates
Sync repository rulesets across multiple repositories:
- name: Sync Repository Rulesets
uses: joshjohanning/bulk-github-repo-settings-sync-action@v1
with:
github-token: ${{ steps.app-token.outputs.token }}
repositories-file: 'repos.yml'
rulesets-file: './config/rulesets/ci-ruleset.json'Or with repo-specific overrides in repos.yml:
repos:
- repo: owner/repo1
rulesets-file: './config/rulesets/ci-ruleset.json'
- repo: owner/repo2
rulesets-file: './config/rulesets/prod-ruleset.json'
- repo: owner/repo3
# Skip ruleset sync for this repoBehavior:
- Creates the ruleset if it doesn't exist in the repository
- Updates the ruleset if a ruleset with the same name already exists
- Ruleset is identified by the
namefield in the JSON configuration - The JSON file should contain a valid ruleset configuration matching the GitHub Rulesets API schema
Example Ruleset JSON (ci-ruleset.json):
{
"name": "ci",
"target": "branch",
"enforcement": "active",
"bypass_actors": [
{
"actor_id": 5,
"actor_type": "RepositoryRole",
"bypass_mode": "always"
}
],
"conditions": {
"ref_name": {
"include": ["refs/heads/main"],
"exclude": []
}
},
"rules": [
{
"type": "deletion"
},
{
"type": "required_status_checks",
"parameters": {
"required_status_checks": [
{
"context": "test",
"integration_id": 15368
}
]
}
},
{
"type": "non_fast_forward"
}
]
}For more information on ruleset configuration, see the GitHub Rulesets documentation.
Sync a pull request template file to .github/pull_request_template.md in target repositories via pull requests:
- name: Sync Pull Request Template
uses: joshjohanning/bulk-github-repo-settings-sync-action@v1
with:
github-token: ${{ steps.app-token.outputs.token }}
repositories-file: 'repos.yml'
pull-request-template: './config/templates/pull_request_template.md'
pull-request-template-pr-title: 'chore: update pull request template'Or with repo-specific overrides in repos.yml:
repos:
- repo: owner/repo1
pull-request-template: './config/templates/standard-template.md'
- repo: owner/repo2
pull-request-template: './config/templates/feature-template.md'
- repo: owner/repo3
# Skip pull request template sync for this repoBehavior:
- If
.github/pull_request_template.mddoesn't exist, it creates it and opens a PR - If it exists but differs, it updates it via PR
- If content is identical, no PR is created
- PRs are created/updated using the GitHub API so commits are verified
- Updates existing open PRs instead of creating duplicates
For more information on pull request templates, see the GitHub documentation.
- name: Update All Org Repositories
uses: joshjohanning/bulk-github-repo-settings-sync-action@v1
with:
github-token: ${{ steps.app-token.outputs.token }}
repositories: 'all'
owner: 'my-organization'
allow-squash-merge: true
delete-branch-on-merge: truePreview changes without applying them:
- name: Preview Changes
uses: joshjohanning/bulk-github-repo-settings-sync-action@v1
with:
github-token: ${{ steps.app-token.outputs.token }}
repositories: 'owner/repo1,owner/repo2'
allow-squash-merge: true
dry-run: true
# dry-run true in PRs, false otherwise example:
# dry-run: ${{ github.event_name == 'pull_request' }}Output shows what would change:
🔍 Would update owner/repo1
📝 Settings changes:
allow-squash-merge: false → true
delete-branch-on-merge: false → true
📦 Would create .github/dependabot.yml via PR
| Input | Description | Required | Default |
|---|---|---|---|
github-token |
GitHub token for API access (requires repo scope or GitHub App with repository administration) |
Yes | - |
github-api-url |
GitHub API URL (e.g., https://api.github.com for GitHub.com or https://ghes.domain.com/api/v3 for GHES). Instance URL is auto-derived. |
No | ${{ github.api_url }} |
repositories |
Comma-separated list of repositories (owner/repo) or "all" for all org/user repos |
No* | - |
repositories-file |
Path to YAML file containing repository list | No* | - |
owner |
Owner (user or organization) name - required when using repositories: "all" |
No | - |
allow-squash-merge |
Allow squash merging pull requests | No | - |
allow-merge-commit |
Allow merge commits for pull requests | No | - |
allow-rebase-merge |
Allow rebase merging pull requests | No | - |
allow-auto-merge |
Allow auto-merge on pull requests | No | - |
delete-branch-on-merge |
Automatically delete head branches after pull requests are merged | No | - |
allow-update-branch |
Always suggest updating pull request branches | No | - |
immutable-releases |
Enable immutable releases to prevent release deletion and modification | No | - |
enable-default-code-scanning |
Enable default code scanning setup | No | - |
topics |
Comma-separated list of topics to set on repositories (replaces existing topics) | No | - |
dependabot-yml |
Path to a dependabot.yml file to sync to .github/dependabot.yml in target repositories |
No | - |
dependabot-pr-title |
Title for pull requests when updating dependabot.yml | No | chore: update dependabot.yml |
rulesets-file |
Path to a JSON file containing repository ruleset configuration to sync to target repositories | No | - |
pull-request-template |
Path to a pull request template file to sync to .github/pull_request_template.md in target repositories |
No | - |
pull-request-template-pr-title |
Title for pull requests when updating pull request template | No | chore: update pull request template |
dry-run |
Preview changes without applying them (logs what would be changed) | No | false |
* Either repositories or repositories-file must be provided
| Output | Description |
|---|---|
updated-repositories |
Number of repositories successfully updated |
failed-repositories |
Number of repositories that failed to update |
results |
JSON array of update results for each repository |
For better security and rate limits, use a GitHub App:
- Create a GitHub App with the following permissions:
- Repository Administration: Read and write (required for updating repository settings and rulesets)
- Contents: Read and write (required if syncing
dependabot.yml) - Pull Requests: Read and write (required if syncing
dependabot.yml)
- Install it to your organization/repositories
- Add
APP_IDandAPP_PRIVATE_KEYas repository secrets
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
- name: Update Repository Settings
uses: joshjohanning/bulk-github-repo-settings-sync-action@v1
with:
github-token: ${{ steps.app-token.outputs.token }}
# ... other inputsAlternatively, use a PAT with repo scope:
- name: Update Repository Settings
uses: joshjohanning/bulk-github-repo-settings-sync-action@v1
with:
github-token: ${{ secrets.PAT_TOKEN }}
# ... other inputsThe repositories-file supports both simple lists and per-repository overrides:
repos:
- owner/repo1
- owner/repo2
- owner/repo3repos:
- repo: owner/repo1
allow-squash-merge: false # Override global setting
topics: 'javascript,custom-topic'
- repo: owner/repo2 # Uses global defaults
- repo: owner/repo3
enable-default-code-scanning: false
dependabot-yml: './config/dependabot/npm-actions.yml'
rulesets-file: './config/rulesets/custom-ruleset.json'
pull-request-template: './config/templates/feature-template.md'Priority: Repository-specific settings override global defaults from action inputs.
- Settings not specified will remain unchanged
- Topics replace all existing repository topics
- Dependabot.yml syncing creates pull requests for review before merging
- Dependabot.yml PRs use the GitHub API ensuring verified commits
- Pull request template syncing creates pull requests for review before merging
- Pull request templates are synced to
.github/pull_request_template.md(standard location) - Failed updates are logged as warnings but don't fail the action
- Access denied repositories are skipped with warnings - ensure your GitHub App has:
- Repository Administration permissions
- Is installed on all target repositories
- CodeQL scanning may not be available for all languages
See CONTRIBUTING.md for development setup, testing, and contribution guidelines.