feat: add Jira export script (jsonl2jira.py)
Add a Python script to push bd issues to Jira. Features: - Create new Jira issues from bd issues without external_ref - Update existing Jira issues matched by external_ref - Handle Jira workflow transitions for status changes - Reverse field mappings (bd -> Jira) via config - Dry-run mode for previewing changes - Auto-update external_ref after creation (--update-refs) Also updates README to document bidirectional sync workflow. Closes bd-93d 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,32 @@
|
||||
# Jira Issues to bd Importer
|
||||
# Jira Integration for bd
|
||||
|
||||
Import issues from Jira Cloud or Jira Server/Data Center into `bd`.
|
||||
Two-way synchronization between Jira and bd (beads).
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `jira2jsonl.py` | **Import** - Fetch Jira issues into bd |
|
||||
| `jsonl2jira.py` | **Export** - Push bd issues to Jira |
|
||||
|
||||
## Overview
|
||||
|
||||
This tool converts Jira Issues to bd's JSONL format, supporting:
|
||||
These tools enable bidirectional sync between Jira and bd:
|
||||
|
||||
**Import (Jira → bd):**
|
||||
1. **Jira REST API** - Fetch issues directly from any Jira instance
|
||||
2. **JSON Export** - Parse exported Jira issues JSON
|
||||
3. **bd config integration** - Read credentials and mappings from `bd config`
|
||||
|
||||
**Export (bd → Jira):**
|
||||
1. **Create issues** - Push new bd issues to Jira
|
||||
2. **Update issues** - Sync changes to existing Jira issues
|
||||
3. **Status transitions** - Handle Jira workflow transitions automatically
|
||||
|
||||
## Features
|
||||
|
||||
### Import (jira2jsonl.py)
|
||||
|
||||
- Fetch from Jira Cloud or Server/Data Center
|
||||
- JQL query support for flexible filtering
|
||||
- Configurable field mappings (status, priority, type)
|
||||
@@ -19,6 +35,15 @@ This tool converts Jira Issues to bd's JSONL format, supporting:
|
||||
- Set `external_ref` for re-sync capability
|
||||
- Hash-based or sequential ID generation
|
||||
|
||||
### Export (jsonl2jira.py)
|
||||
|
||||
- Create new Jira issues from bd issues
|
||||
- Update existing Jira issues (matched by `external_ref`)
|
||||
- Handle Jira workflow transitions for status changes
|
||||
- Reverse field mappings (bd → Jira)
|
||||
- Dry-run mode for previewing changes
|
||||
- Auto-update `external_ref` after creation
|
||||
|
||||
## Installation
|
||||
|
||||
No dependencies required! Uses Python 3 standard library.
|
||||
@@ -351,6 +376,164 @@ Jira Cloud has rate limits. For large imports:
|
||||
|
||||
This script fetches 100 issues per request, so a 1000-issue project requires ~10 API calls.
|
||||
|
||||
---
|
||||
|
||||
# Export: jsonl2jira.py
|
||||
|
||||
Push bd issues to Jira.
|
||||
|
||||
## Export Quick Start
|
||||
|
||||
```bash
|
||||
# Export all issues (create new, update existing)
|
||||
bd export | python jsonl2jira.py --from-config
|
||||
|
||||
# Create only (don't update existing Jira issues)
|
||||
bd export | python jsonl2jira.py --from-config --create-only
|
||||
|
||||
# Dry run (preview what would happen)
|
||||
bd export | python jsonl2jira.py --from-config --dry-run
|
||||
|
||||
# Auto-update bd with new external_refs
|
||||
bd export | python jsonl2jira.py --from-config --update-refs
|
||||
```
|
||||
|
||||
## Export Modes
|
||||
|
||||
### Create Only
|
||||
|
||||
Only create new Jira issues for bd issues that don't have an `external_ref`:
|
||||
|
||||
```bash
|
||||
bd export | python jsonl2jira.py --from-config --create-only
|
||||
```
|
||||
|
||||
### Create and Update
|
||||
|
||||
Create new issues AND update existing ones (matched by `external_ref`):
|
||||
|
||||
```bash
|
||||
bd export | python jsonl2jira.py --from-config
|
||||
```
|
||||
|
||||
### Dry Run
|
||||
|
||||
Preview what would happen without making any changes:
|
||||
|
||||
```bash
|
||||
bd export | python jsonl2jira.py --from-config --dry-run
|
||||
```
|
||||
|
||||
## Workflow Transitions
|
||||
|
||||
Jira often requires workflow transitions to change issue status (you can't just set `status=Done`). The export script automatically:
|
||||
|
||||
1. Fetches available transitions for each issue
|
||||
2. Finds a transition that leads to the target status
|
||||
3. Executes the transition
|
||||
|
||||
If no valid transition is found, the status change is skipped with a warning.
|
||||
|
||||
## Reverse Field Mappings
|
||||
|
||||
For export, you need mappings from bd → Jira (reverse of import):
|
||||
|
||||
```bash
|
||||
# Status: bd status -> Jira status name
|
||||
bd config set jira.reverse_status_map.open "To Do"
|
||||
bd config set jira.reverse_status_map.in_progress "In Progress"
|
||||
bd config set jira.reverse_status_map.blocked "Blocked"
|
||||
bd config set jira.reverse_status_map.closed "Done"
|
||||
|
||||
# Type: bd type -> Jira issue type name
|
||||
bd config set jira.reverse_type_map.bug "Bug"
|
||||
bd config set jira.reverse_type_map.feature "Story"
|
||||
bd config set jira.reverse_type_map.task "Task"
|
||||
bd config set jira.reverse_type_map.epic "Epic"
|
||||
bd config set jira.reverse_type_map.chore "Task"
|
||||
|
||||
# Priority: bd priority (0-4) -> Jira priority name
|
||||
bd config set jira.reverse_priority_map.0 "Highest"
|
||||
bd config set jira.reverse_priority_map.1 "High"
|
||||
bd config set jira.reverse_priority_map.2 "Medium"
|
||||
bd config set jira.reverse_priority_map.3 "Low"
|
||||
bd config set jira.reverse_priority_map.4 "Lowest"
|
||||
```
|
||||
|
||||
If not configured, sensible defaults are used.
|
||||
|
||||
## Updating external_ref
|
||||
|
||||
After creating a Jira issue, you'll want to link it back to the bd issue:
|
||||
|
||||
```bash
|
||||
# Option 1: Auto-update with --update-refs flag
|
||||
bd export | python jsonl2jira.py --from-config --update-refs
|
||||
|
||||
# Option 2: Manual update from script output
|
||||
bd export | python jsonl2jira.py --from-config | while read line; do
|
||||
bd_id=$(echo "$line" | jq -r '.bd_id')
|
||||
ext_ref=$(echo "$line" | jq -r '.external_ref')
|
||||
bd update "$bd_id" --external-ref="$ext_ref"
|
||||
done
|
||||
```
|
||||
|
||||
## Export Examples
|
||||
|
||||
### Example 1: Initial Export to Jira
|
||||
|
||||
```bash
|
||||
# First, export all open issues
|
||||
bd list --status open --json | python jsonl2jira.py --from-config --update-refs
|
||||
|
||||
# Now those issues have external_ref set
|
||||
bd list --status open
|
||||
```
|
||||
|
||||
### Example 2: Sync Changes Back to Jira
|
||||
|
||||
```bash
|
||||
# Export issues modified today
|
||||
bd list --json | python jsonl2jira.py --from-config
|
||||
```
|
||||
|
||||
### Example 3: Preview Before Export
|
||||
|
||||
```bash
|
||||
# See what would happen
|
||||
bd export | python jsonl2jira.py --from-config --dry-run
|
||||
|
||||
# If it looks good, run for real
|
||||
bd export | python jsonl2jira.py --from-config --update-refs
|
||||
```
|
||||
|
||||
## Export Limitations
|
||||
|
||||
- **Assignee**: Not set (requires Jira account ID lookup)
|
||||
- **Dependencies**: Not synced to Jira issue links
|
||||
- **Comments**: Not exported
|
||||
- **Custom fields**: design, acceptance_criteria, notes not exported
|
||||
- **Attachments**: Not exported
|
||||
|
||||
## Bidirectional Sync Workflow
|
||||
|
||||
For ongoing synchronization between Jira and bd:
|
||||
|
||||
```bash
|
||||
# 1. Pull changes from Jira
|
||||
python jira2jsonl.py --from-config --jql "project=PROJ AND updated >= -1d" | bd import
|
||||
|
||||
# 2. Do local work in bd
|
||||
bd update bd-xxx --status in_progress
|
||||
# ... work ...
|
||||
bd close bd-xxx
|
||||
|
||||
# 3. Push changes to Jira
|
||||
bd export | python jsonl2jira.py --from-config
|
||||
|
||||
# 4. Repeat daily/weekly
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [bd README](../../README.md) - Main documentation
|
||||
|
||||
Reference in New Issue
Block a user