Table of Contents
Click to expand
A system that keeps your ROADMAP.md in sync with GitHub Issues automatically using a Python script and a GitHub Actions workflow.
| File | Description |
|---|---|
.github/scripts/sync_roadmap.py |
Python script that parses ROADMAP.md, synchronizes checkbox states with GitHub Issues, auto-creates missing issues, and commits changes back. |
.github/workflows/roadmap-sync.yml |
GitHub Actions workflow that triggers the script. |
Your ROADMAP.md is the single source of truth. Edit checkboxes, push to main, and the GitHub Action handles the rest:
| Checkbox | Effect on GitHub Issue |
|---|---|
- [ ] |
Open (or reopen if closed); remove in-progress label if present |
- [/] |
Open (or reopen if closed); add in-progress label |
- [x] |
Close issue; remove in-progress label if present |
No (#N) |
Auto-creates the issue, writes the number back to the file, and commits |
| Modify text | Renames the GitHub issue to match the updated text in the roadmap |
Click to expand
The sync engine runs in two phases:
Tasks in the roadmap that don't have an issue number ((#N)) are processed first:
- A search is performed to avoid duplicates.
- New issues are created via
gh issue createwith the phase label applied automatically. - Tasks marked
[x]without an issue are created and immediately closed. - After creation, the script updates the ROADMAP.md in-place with the assigned issue number and auto-commits and pushes.
- The commit message includes
[roadmap-sync]to prevent infinite workflow loops.
All tasks with issue numbers are synced:
[ ](todo): Reopens the issue if closed; removes thein-progresslabel if present.[/](in-progress): Reopens the issue if closed; adds thein-progresslabel if missing.[x](done): Closes the issue if open; removes thein-progresslabel if present.- Titles: If the text of the task in
ROADMAP.mddiffers from the GitHub issue title, the issue is renamed.ROADMAP.mdis the single source of truth for titles.
Labels (in-progress and phase labels) are auto-created if they don't exist on the repository.
| Flag | Default | Description |
|---|---|---|
--roadmap PATH |
./ROADMAP.md |
Path to the roadmap file |
--repo OWNER/NAME |
Auto-detected by gh |
Target GitHub repository |
--dry-run |
Off | Preview all changes without applying them |
Click to expand
on:
push:
paths: ['ROADMAP.md']
branches: [main]
workflow_dispatch:- Triggers: Push to
mainthat modifiesROADMAP.md, or manual dispatch. - Permissions:
issues: writeandcontents: write. - Python version: 3.12
- Infinite-loop guard: Skipped if commit message contains
[roadmap-sync].
Click to expand
## Phase Name <!-- phase:label-name -->
- [ ] Task that already has an issue (#1)
- [ ] New task that needs an issue
- [/] Task currently in progress (#2)
- [x] Completed task (#3)Rules:
- Each phase heading must include
<!-- phase:label-name -->— the value becomes both the issue label and the phase identifier. - Tasks with
(#N)sync their state with that issue. - Tasks without
(#N)get a new issue created automatically.
Click to expand
Copy these two files into your repository:
your-repo/
├── .github/
│ ├── scripts/
│ │ └── sync_roadmap.py ← the sync engine
│ └── workflows/
│ └── roadmap-sync.yml ← the GitHub Action trigger
└── ROADMAP.md ← your roadmap
No configuration needed — the script auto-detects the repo from the gh CLI.
- GitHub CLI (
gh) authenticated (gh auth login) - Python 3.12+
- The repository's
GITHUB_TOKENmust haveissues: writeandcontents: writepermissions
Click to expand
# Preview what would change (no side effects)
python3 .github/scripts/sync_roadmap.py --dry-run
# Apply changes
python3 .github/scripts/sync_roadmap.py
# Use a different roadmap file
python3 .github/scripts/sync_roadmap.py --roadmap docs/ROADMAP.md
# Target a specific repo
python3 .github/scripts/sync_roadmap.py --repo owner/repoClick to expand
-
Add a new task to
ROADMAP.md:- [ ] Add retry logic to download manager
-
Push to
main. -
The Action auto-creates issue
#46, updates your file, and commits:- [ ] Add retry logic to download manager (#46)
-
Later, mark it in progress:
-- [ ] Add retry logic to download manager (#46) +- [/] Add retry logic to download manager (#46)
-
Push → issue
#46gets thein-progresslabel. -
When done:
-- [/] Add retry logic to download manager (#46) +- [x] Add retry logic to download manager (#46)
-
Push → issue
#46is closed andin-progresslabel is removed. -
Rename the task:
-- [x] Add retry logic to download manager (#46) +- [x] Add exponential backoff retry logic (#46)
-
Push → issue
#46title is updated in GitHub to match the new text.