Easy Sync GitHub repository labels from a canonical source repository to one or more target repositories.
- Sync label name/color/description from the source repo to target repos
- Optional deletion of labels in targets that do not exist in the source
- Optional ordering via numeric-prefixed label names (renames labels safely to preserve assignments)
- Automatic descriptions for known base label names (with
Extratag*kept empty)
Support this project: Donate via Stripe 50% of the earnings goes to support Ukraine!
Recommended approach:
- Make this repo a template repository and click Use this template to create your own label-sync automation repo.
- If you do not see the template button, Fork also works.
If you are the owner of this repository and you want others to easily reuse it:
- GitHub ->
Settings->General-> enable Template repository
If you are using this project for your own automation:
- Click Use this template to create your own repo
- Update
label-sync.ymlin your copy (source/targets/options) - Add the
LABEL_SYNC_TOKENsecret in your copy
Steps:
-
Create your automation repo
- Click Use this template (recommended)
-
Add
LABEL_SYNC_TOKENsecret- Create a GitHub PAT that can access the source repo and all target repos
- In your automation repo:
Settings->Secrets and variables->Actions->New repository secret - Name it
LABEL_SYNC_TOKEN
-
Configure
label-sync.yml- Set
sourceandtargets - (Optional) set
ordering.namesto enforce GitHub UI ordering
- Set
-
Run the workflow
Actions->Label Sync->Run workflow- Set
apply=falsefor dry-run - Set
apply=trueto apply changes
- Set
The workflow file is at .github/workflows/label-sync.yml.
Prerequisites:
- Node.js (the included GitHub Action uses Node
20)
- Install dependencies
npm install- Create an env file
copy .env.example .envSet GITHUB_TOKEN for local runs.
If you run this from GitHub Actions and need to sync across multiple repositories, store a PAT in repository secret LABEL_SYNC_TOKEN (the built-in GITHUB_TOKEN is repo-scoped).
Token guidance:
- For private repos, the PAT typically needs
repo - For public repos only,
public_repois usually sufficient - The PAT must be able to read and write labels (Issues permissions)
- Edit
label-sync.yml
source: canonical labels repositorytargets: repositories to sync tooptions.deleteExtraLabels: whentrue, labels in targets that are not in the source repo are deleted
Example:
source:
owner: your-user-or-org
repo: your-canonical-labels-repo
targets:
- owner: your-user-or-org
repo: target-repo-1
- owner: your-user-or-org
repo: target-repo-2
ordering:
applyToSource: true
names:
- "01-important"
- "02-idea"
- "03-documentation"
- "04-improvement"
- "05-not Important"
- "06-Wordpress demand"
- "07-Pro-version"
- "08-Milestone"
- "09-bug"
- "10-help wanted"
- "11-question"
- "12-wontfix"
- "13-duplicate"
options:
deleteExtraLabels: true
allowEmptySource: falseRecommended first run:
- If
options.deleteExtraLabels: true, run dry-run first to confirm what would be created/updated/deleted.
Dry-run (no changes):
npm run sync -- --dry-runApply changes:
npm run sync -- --applyExport labels from the source repo:
npm run export -- --out labels-export.ymlThis repo includes a workflow at .github/workflows/label-sync.yml:
- Manual run:
Actions->Label Sync->Run workflow- Set
apply=falsefor dry-run - Set
apply=trueto apply changes
- Set
- Scheduled run: weekly (see workflow cron)
The workflow expects repository secret:
LABEL_SYNC_TOKEN: a GitHub PAT with access to the source repo and all targets
Note: the workflow uses LABEL_SYNC_TOKEN (not the built-in GITHUB_TOKEN) because syncing across multiple repositories requires a PAT.
- Do not commit tokens. Store PATs in GitHub repository secrets (like
LABEL_SYNC_TOKEN). - If you make this repo public, your
label-sync.ymlcontents (including your repo list) are public too. - Prefer least privilege for your PAT:
- If you only sync public repos,
public_repois usually sufficient. - If you sync private repos, you typically need
repo.
- If you only sync public repos,
- If you accept contributions:
- Avoid unsafe workflow patterns that could expose secrets.
- Consider enabling branch protection for
main.
If ordering.names is provided:
- Labels are synced in the exact order of
ordering.names(numeric prefixes provide stable GitHub UI ordering) - If
ordering.applyToSource: true, ordering renames are applied to the source repo too - Renames use GitHub's label rename API so existing issue/PR label assignments are preserved
- If a repo contains both a base label and the prefixed label (example: both
bugand09-bug), sync stops with an error to avoid splitting assignments ordering.namesdoes not create labels by itself; it only controls ordering/renames. If an entry is missing from the source repo, it is skipped.
To delete a label everywhere:
- Delete it from the source repo
- Run sync with
options.deleteExtraLabels: true(targets will delete labels not present in the source)
Descriptions:
- Descriptions are generated from the base name (the part after the
NN-prefix) Extratag*labels always get an empty description
Default label descriptions (built-in):
important: High priority.idea: New idea or proposal.documentation: Documentation updates.improvement: Enhancement/improvement.not important: Low priority.wordpress demand: WordPress-related request.pro-version: Pro/version-related work.milestone: Milestone tracking.bug: Bug report.help wanted: Help wanted.question: Question/support.wontfix: Won't fix.duplicate: Duplicate.ExtratagN(example:Extratag1): empty description.
- "Source repo has 0 labels"
- Add labels to the source repo, or pass
--allow-empty-sourceonly if you understand the delete risk.
- Add labels to the source repo, or pass
- "both 'X' and 'NN-X' exist"
- Delete/merge one of the two labels in that repo, then re-run.
- Auth / 404 / permission errors in Actions
- Confirm
LABEL_SYNC_TOKENexists and can access every repo inlabel-sync.yml.
- Confirm
If the source repository has zero labels, sync is aborted by default when deletion is enabled. Override with --allow-empty-source only if you are absolutely sure.