Sync GitLab issues with local markdown files - bidirectional synchronization for offline issue management
Project description
GitLab Issue Sync
Sync GitLab issues with local markdown files for offline management, AI workflows, and seamless collaboration.
gl-issue-sync enables bidirectional synchronization between GitLab issues and local markdown files, allowing you to:
- Work with issues offline using your favorite text editor
- Leverage AI assistants for intelligent issue management
- Version control your issue workflows
- Manage Kanban boards from the command line
- Bulk edit issues using standard text tools
Key Features
- 📥 Bidirectional Sync - Pull issues from GitLab, edit locally, push changes back
- 🎯 Kanban Board Management - Move issues between columns, track progress
- 🔗 Parent-Child Hierarchy - Organize work items with task dependencies
- 💬 Comments & Metadata - Add comments, labels, assignees, milestones
- 📝 Wiki Integration - Clone and manage project wikis locally
- ⚡ Offline-First - Work without network access, sync when ready
- 🤖 AI-Ready - Perfect for AI-assisted workflows and automation
- 🔒 Self-Hosted Support - Works with gitlab.com and self-hosted instances
Installation
Prerequisites
- Python 3.12 or higher
- Git repository with GitLab remote
- GitLab personal access token with
apiscope
Install from PyPI
# Using pip
pip install gitlab-issue-sync
# Using uv (recommended)
uv pip install gitlab-issue-sync
Verify Installation
gl-issue-sync --version
Quick Start
1. Initialize Your Repository
Navigate to your GitLab project and run:
gl-issue-sync init
This will:
- Detect your GitLab instance from git remote
- Prompt for your personal access token
- Configure Kanban board columns
- Create
.issues/directory structure
2. Sync Your First Issues
# Pull all open issues
gl-issue-sync pull
# View issues by status
gl-issue-sync status
# Display a specific issue
gl-issue-sync show 42
3. Work with Issues Locally
Issues are stored as markdown files in .issues/opened/:
.issues/
├── opened/
│ ├── 1.md # Issue #1
│ ├── 42.md # Issue #42
│ └── ...
├── closed/
│ └── ...
└── .sync/
└── originals/ # Snapshots for conflict detection
4. Make Changes and Push
# Create a new issue
gl-issue-sync new "Fix login bug" --label bug --column "ToDo"
# Add a comment
gl-issue-sync comment 42 "Started working on this"
# Move issue on Kanban board
gl-issue-sync board move 42 "In Progress"
# Push changes to GitLab
gl-issue-sync push
Common Workflows
Kanban Board Management
# View board columns
gl-issue-sync board columns
# Move to next column
gl-issue-sync board move 42
# Move to specific column
gl-issue-sync board move 42 "Done"
# Move backward
gl-issue-sync board move 42 --back
Labels and Metadata
# List available labels
gl-issue-sync label list
# Add labels to issue
gl-issue-sync label 42 add bug urgent
# Set assignees
gl-issue-sync assignees 42 add alice bob
# Set milestone
gl-issue-sync milestone 42 set "v1.0"
Parent-Child Hierarchy
# Create child issue
gl-issue-sync new "Implement API endpoint" --parent 42
# Set parent on existing issue
gl-issue-sync parent 43 set 42
# View hierarchy
gl-issue-sync show 42 # Shows children
gl-issue-sync show 43 # Shows parent
Conflict Resolution
# List conflicts
gl-issue-sync conflicts list
# View conflict details
gl-issue-sync conflicts show 42
# After manually editing .issues/opened/42.md
gl-issue-sync conflicts resolve 42
gl-issue-sync push
Wiki Management
# Clone project wiki
gl-issue-sync wiki clone
# Edit wiki files
$EDITOR wiki/PRD.md
# Commit and push changes
gl-issue-sync wiki commit -m "Update PRD"
gl-issue-sync wiki push
Issue File Format
Issues are markdown files with YAML frontmatter:
---
iid: 42
title: "Fix login bug"
state: "opened"
labels:
- "bug"
- "ToDo" # Kanban column
assignees:
- "alice"
milestone: "v1.0"
parent_iid: 1 # Parent work item (optional)
child_iids: # Child work items (auto-managed)
- 43
- 44
---
# Fix login bug
Users cannot login using OAuth providers.
## Acceptance Criteria
- [ ] Identify root cause
- [ ] Implement fix
- [ ] Add tests
- [ ] Deploy to staging
## Comments
### alice - 2026-01-08 10:30:00
Started investigating this issue...
Configuration
Configuration is stored in ~/.config/gitlab-issue-sync/config.toml:
[project."gitlab.example.com/owner/project"]
token = "glpat-xxxxxxxxxxxxxxxxxxxx"
instance_url = "https://gitlab.example.com"
namespace = "owner"
project = "project"
[project."gitlab.example.com/owner/project".board]
columns = ["ToDo", "In Progress", "Needs Validation"]
Security: Config file permissions are automatically set to 0600 (owner read/write only).
Environment Variables
# Override config file token
export GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx
gl-issue-sync pull
Self-Hosted GitLab
Works seamlessly with self-hosted instances:
# Auto-detected from git remote
gl-issue-sync init
# Or specify manually
gl-issue-sync init --url https://gitlab.company.internal
Command Reference
| Command | Description |
|---|---|
init |
Initialize repository for issue sync |
pull |
Fetch issues from GitLab |
push |
Push local changes to GitLab |
sync |
Bidirectional sync (pull + push) |
status |
Show sync status and issue counts |
list |
List issues with filters |
show |
Display detailed issue information |
new |
Create new issue locally |
comment |
Add comment to issue |
board |
Manage Kanban board operations |
close |
Close issue |
label |
Manage labels (project and issue-level) |
milestone |
Manage milestones (project and issue-level) |
assignees |
Manage issue assignees |
linked |
Manage linked issues |
parent |
Manage parent-child hierarchy |
conflicts |
List and resolve conflicts |
wiki |
Manage project wiki repository |
Run gl-issue-sync COMMAND --help for detailed options on any command.
Exit Codes
The tool uses distinct exit codes for scripting and automation:
| Code | Meaning | Description |
|---|---|---|
| 0 | Success | Command completed successfully |
| 1 | General error | Validation errors, storage errors |
| 2 | Configuration error | Missing config, no git remote |
| 3 | Authentication error | Invalid token, insufficient permissions |
| 4 | API error | GitLab API failures, network errors |
Example: Retry on Network Errors
#!/bin/bash
gl-issue-sync pull
EXIT_CODE=$?
if [ $EXIT_CODE -eq 4 ]; then
echo "⚠ Network error, retrying..."
sleep 5
gl-issue-sync pull
elif [ $EXIT_CODE -eq 2 ] || [ $EXIT_CODE -eq 3 ]; then
echo "✗ Configuration or authentication error"
exit 1
fi
Troubleshooting
Authentication Issues
# Verify token has 'api' scope
gl-issue-sync init --token glpat-xxxxxxxxxxxxxxxxxxxx
# Check token hasn't expired in GitLab Settings → Access Tokens
Repository Not Found
# Verify git remote points to GitLab
git remote -v
# Manually specify instance URL
gl-issue-sync init --url https://gitlab.example.com
Board Columns Not Detected
# Refresh column configuration
gl-issue-sync board columns --sync
# Verify board exists: GitLab project → Issues → Boards
License
MIT License - see LICENSE file for details.
Credits
Inspired by mitsuhiko/gh-issue-sync - the GitHub equivalent of this tool.
Links
- Project: GitLab Repository
- Wiki: Project Wiki
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gitlab_issue_sync-0.1.1.tar.gz.
File metadata
- Download URL: gitlab_issue_sync-0.1.1.tar.gz
- Upload date:
- Size: 132.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30318c75b174e5671f3b054f7b8072226ff7e87940097257ab8622bfa8ddb53a
|
|
| MD5 |
72b4b1ec2bc8c90d6abaa9e6777d42f4
|
|
| BLAKE2b-256 |
d6692bed74f7a42ca87f5f8529fbf0e047f0569f7a139a03a862d6da85b117d9
|
File details
Details for the file gitlab_issue_sync-0.1.1-py3-none-any.whl.
File metadata
- Download URL: gitlab_issue_sync-0.1.1-py3-none-any.whl
- Upload date:
- Size: 96.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2170b145579bc96ca713dc597ffac88bc6d5dc9a58dc7cf8f7ebb24312bfa5b8
|
|
| MD5 |
9c95104a1bce6c9c3622de661a9f5d25
|
|
| BLAKE2b-256 |
f1917cf29fc7a24d9d336e8355bd4788ad16bf59d12372c1bf93341670d00250
|