Label Sync is a standalone GitHub label management repository for keeping labels consistent across an organization.
It treats one configured source repository as the label source of truth, exports those labels into config files, validates the config, and syncs the resulting label set to the rest of the selected repositories in the organization.
Features:
- Sync labels from a source repository into
config/labels.jsonc - Track removed managed labels in
config/deleted-labels.jsonc - Validate JSONC config files automatically on config changes and pull requests
- Create and update labels across selected organization repositories
- Delete labels that were removed from the managed label set
- Optionally delete exact GitHub default labels
- Optionally delete unmanaged labels during org sync runs
- Rename labels across repositories while preserving issue and pull request assignments where possible
- Targeted soft label removal from issues and pull requests across selected repositories
- Support whitelist or blacklist repository selection
- Write changelogs for real workflow changes and fake changelogs for dry runs
- Fork or clone this repository into the GitHub organization you want to manage.
- Follow the authentication setup guide.
- Update
config/properties.jsoncwith your organization, source repository, and authentication settings. - Configure
config/repository-filter.jsoncto choose which repositories should be synced. - Set the labels on the source repository to the label set you want to manage.
- Run
Config-Label_Synconce to populateconfig/labels.jsonc. - Run
Org-Label-Syncto apply the labels across the selected repositories.
All project config lives in config/ and uses JSONC, so comments are allowed.
config/properties.jsonc: organization, source repository, and authentication settingsconfig/labels.jsonc: managed labels to create or update across the organizationconfig/deleted-labels.jsonc: labels that should be deleted from target repositoriesconfig/github-default-labels.jsonc: exact GitHub default label specs that can be prunedconfig/repository-filter.jsonc: whitelist or blacklist rules for repository selection
The configured source repository is always skipped by repository filtering. You do not need to add it to the whitelist or blacklist.
labels.jsoncstarts empty until labels are defined or synced from the source repositorydeleted-labels.jsoncstarts empty and is populated when managed labels are removed from the source repositoryrepository-filter.jsoncdefaults to blacklist mode, which targets all discovered org repositories except listed exclusions- GitHub default labels are pruned only when they exactly match
config/github-default-labels.jsonc - Unmanaged label deletion is disabled unless
delete_missingis enabled onOrg-Label-Sync
This repository includes five GitHub Actions workflows:
Config-Label_SyncValidate-ConfigsReverse-Config-Label-SyncOrg-Label-SyncRemove-Labels
Use this flow when the source repository labels are the source of truth.
- Edit labels directly on the configured source repository.
- Run
Config-Label_Sync, or runOrg-Label-Syncand let it callConfig-Label_Syncfirst. - Review the generated changes to
config/labels.jsoncandconfig/deleted-labels.jsonc. - Run
Org-Label-Syncto apply the managed label set to the selected repositories.
Config-Label_Sync reads the current labels on the source repository, rewrites config/labels.jsonc, moves removed managed labels into config/deleted-labels.jsonc, validates the default-label config, and commits the config update when something changed.
Use this flow when you want to edit the managed label config directly.
- Edit
config/labels.jsonc. - Push the change to the default branch.
- Let
Validate-Configsverify the config. - Let
Reverse-Config-Label-Syncupdate the source repository labels from the config.
Reverse-Config-Label-Sync ignores bot commits so automated config updates do not trigger a reverse sync loop.
Run Org-Label-Sync manually when you want to apply the managed label set across selected repositories.
Inputs:
dry_run: preview changes without applying themdelete_missing: delete labels that are not managed byconfig/labels.jsoncdelete_github_default_labels: delete exact GitHub default labels listed inconfig/github-default-labels.jsoncrepositories: comma-separated override for the target repository listlabel_replacements: comma-separated rename map inold=new, old2=new2format
label_replacements is meant for label renames. The old label must exist in config/deleted-labels.jsonc, and the new label must exist in config/labels.jsonc.
When changes are made, the workflow writes changelogs under changelogs/YYYY-MM-DD/. Dry runs write preview changelogs under fake-changelogs/YYYY-MM-DD/.
Run Remove-Labels manually when you want to remove one exact label from issues and pull requests across selected repositories.
Inputs:
dry_run: preview removals without applying themrun_on_issues: remove the label from matching issuestarget_only_closed_issues: only target closed issuesrun_on_pull_requests: remove the label from matching pull requeststarget_only_closed_pull_requests: only target closed pull requestslabel_name: exact label name to removerepositories: comma-separated override for the target repository list
Like Org-Label-Sync, real changes are written to changelogs/ and dry-run previews are written to fake-changelogs/.
Validate-Configs runs automatically when config files change.
Validation checks include:
- JSONC parsing
- Required fields
- Duplicate labels
- Invalid label colors
- Invalid repository names
- Repository filter shape
- GitHub default label shape
- Shared config used by
Org-Label-SyncandRemove-Labels
Label Sync is designed for organizations that want one consistent label system across many repositories without manually editing every repo.
This was initially based on a GHA workflow that I wrote in 2025 but was transitioned into a repository to account for increased complexity. The initial transition and all commits afterwards were aided by AI and underwent human review.
This project is licensed under the MIT License.