Files
beads/examples/jira-import
Steve Yegge 3f9c9bdfd4 feat: add Jira import script (jira2jsonl.py)
Add a Python script to import Jira issues into beads JSONL format.

Features:
- Fetch issues from Jira Cloud or Server/Data Center REST API
- JQL query support for flexible filtering
- Configurable field mappings via bd config
- Hash-based or sequential ID generation
- Issue links converted to dependencies
- external_ref set for re-sync capability

Configuration options:
- jira.url, jira.project, jira.api_token
- jira.status_map.*, jira.type_map.*, jira.priority_map.*

Closes bd-tjn

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 15:12:14 -08:00
..

Jira Issues to bd Importer

Import issues from Jira Cloud or Jira Server/Data Center into bd.

Overview

This tool converts Jira Issues to bd's JSONL format, supporting:

  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

Features

  • Fetch from Jira Cloud or Server/Data Center
  • JQL query support for flexible filtering
  • Configurable field mappings (status, priority, type)
  • Preserve timestamps, assignees, labels
  • Extract issue links as dependencies
  • Set external_ref for re-sync capability
  • Hash-based or sequential ID generation

Installation

No dependencies required! Uses Python 3 standard library.

Quick Start

Set up your Jira credentials once:

# Required settings
bd config set jira.url "https://company.atlassian.net"
bd config set jira.project "PROJ"
bd config set jira.api_token "YOUR_API_TOKEN"

# For Jira Cloud, also set username (your email)
bd config set jira.username "you@company.com"

Then import:

python jira2jsonl.py --from-config | bd import

Option 2: Using environment variables

export JIRA_API_TOKEN=your_token
export JIRA_USERNAME=you@company.com  # For Jira Cloud

python jira2jsonl.py \
  --url https://company.atlassian.net \
  --project PROJ \
  | bd import

Option 3: Command-line arguments

python jira2jsonl.py \
  --url https://company.atlassian.net \
  --project PROJ \
  --username you@company.com \
  --api-token YOUR_TOKEN \
  | bd import

Authentication

Jira Cloud

Jira Cloud requires:

  1. Username: Your email address
  2. API Token: Create at https://id.atlassian.com/manage-profile/security/api-tokens
bd config set jira.username "you@company.com"
bd config set jira.api_token "your_api_token"

Jira Server/Data Center

Jira Server/DC can use:

  • Personal Access Token (PAT) - Just set the token, no username needed
  • Username + Password - Set both username and password as the token
# Using PAT (recommended)
bd config set jira.api_token "your_pat_token"

# Using username/password
bd config set jira.username "your_username"
bd config set jira.api_token "your_password"

Usage

Basic Usage

# Fetch all issues from a project
python jira2jsonl.py --from-config | bd import

# Save to file first (recommended for large projects)
python jira2jsonl.py --from-config > issues.jsonl
bd import -i issues.jsonl --dry-run  # Preview
bd import -i issues.jsonl             # Import

Filtering Issues

# Only open issues
python jira2jsonl.py --from-config --state open

# Only closed issues
python jira2jsonl.py --from-config --state closed

# Custom JQL query
python jira2jsonl.py --url https://company.atlassian.net \
  --jql "project = PROJ AND priority = High AND status != Done"

ID Generation Modes

# Sequential IDs (bd-1, bd-2, ...) - default
python jira2jsonl.py --from-config

# Hash-based IDs (bd-a3f2dd, ...) - matches bd create
python jira2jsonl.py --from-config --id-mode hash

# Custom hash length (3-8 chars)
python jira2jsonl.py --from-config --id-mode hash --hash-length 4

# Custom prefix
python jira2jsonl.py --from-config --prefix myproject

From JSON File

If you have an exported JSON file:

python jira2jsonl.py --file issues.json | bd import

Field Mapping

Default Mappings

Jira Field bd Field Notes
key (internal) Used for dependency resolution
summary title Direct copy
description description Direct copy
status.name status Mapped via status_map
priority.name priority Mapped via priority_map
issuetype.name issue_type Mapped via type_map
assignee assignee Display name or username
labels labels Direct copy
created created_at ISO 8601 timestamp
updated updated_at ISO 8601 timestamp
resolutiondate closed_at ISO 8601 timestamp
(computed) external_ref URL to Jira issue
issuelinks dependencies Mapped to blocks/related
parent dependencies Mapped to parent-child

Status Mapping

Default status mappings (Jira status -> bd status):

Jira Status bd Status
To Do, Open, Backlog, New open
In Progress, In Development, In Review in_progress
Blocked, On Hold blocked
Done, Closed, Resolved, Complete closed

Custom mappings via bd config:

bd config set jira.status_map.backlog "open"
bd config set jira.status_map.in_review "in_progress"
bd config set jira.status_map.on_hold "blocked"

Priority Mapping

Default priority mappings (Jira priority -> bd priority 0-4):

Jira Priority bd Priority
Highest, Critical, Blocker 0 (Critical)
High, Major 1 (High)
Medium, Normal 2 (Medium)
Low, Minor 3 (Low)
Lowest, Trivial 4 (Backlog)

Custom mappings:

bd config set jira.priority_map.urgent "0"
bd config set jira.priority_map.nice_to_have "4"

Issue Type Mapping

Default type mappings (Jira type -> bd type):

Jira Type bd Type
Bug, Defect bug
Story, Feature, Enhancement feature
Task, Sub-task task
Epic, Initiative epic
Technical Task, Maintenance chore

Custom mappings:

bd config set jira.type_map.story "feature"
bd config set jira.type_map.spike "task"
bd config set jira.type_map.tech_debt "chore"

Jira issue links are converted to bd dependencies:

Jira Link Type bd Dependency Type
Blocks/Is blocked by blocks
Parent (Epic/Story) parent-child
All others related

Note: Only links to issues included in the import are preserved. Links to issues outside the query results are ignored.

Re-syncing from Jira

Each imported issue has an external_ref field containing the Jira issue URL. On subsequent imports:

  1. Issues are matched by external_ref first
  2. If matched, the existing bd issue is updated (if Jira is newer)
  3. If not matched, a new bd issue is created

This enables incremental sync:

# Initial import
python jira2jsonl.py --from-config | bd import

# Later: import only recent changes
python jira2jsonl.py --from-config \
  --jql "project = PROJ AND updated >= -7d" \
  | bd import

Examples

Example 1: Import Active Sprint

python jira2jsonl.py --url https://company.atlassian.net \
  --jql "project = PROJ AND sprint in openSprints()" \
  | bd import

bd ready  # See what's ready to work on

Example 2: Full Project Migration

# Export all issues
python jira2jsonl.py --from-config > all-issues.jsonl

# Preview import
bd import -i all-issues.jsonl --dry-run

# Import
bd import -i all-issues.jsonl

# View stats
bd stats

Example 3: Sync High Priority Bugs

python jira2jsonl.py --from-config \
  --jql "project = PROJ AND type = Bug AND priority in (Highest, High)" \
  | bd import

Example 4: Import with Hash IDs

# Use hash IDs for collision-free distributed work
python jira2jsonl.py --from-config --id-mode hash | bd import

Limitations

  • Single assignee: Jira supports multiple assignees (watchers), bd supports one
  • Custom fields: Only standard fields are mapped; custom fields are ignored
  • Attachments: Not imported
  • Comments: Not imported (only description)
  • Worklogs: Not imported
  • Sprints: Sprint metadata not preserved (use labels or JQL filtering)
  • Components/Versions: Not mapped to bd (consider using labels)

Troubleshooting

"Authentication failed"

Jira Cloud:

Jira Server/DC:

  • Try using a Personal Access Token instead of password
  • Check that your account has permission to access the project

"403 Forbidden"

  • Check project permissions in Jira
  • Verify API token has correct scopes
  • Some Jira instances restrict API access by IP

"400 Bad Request"

  • Check JQL syntax
  • Verify project key exists
  • Check for special characters in JQL (escape with backslash)

Rate Limits

Jira Cloud has rate limits. For large imports:

  • Add delays between requests (not implemented yet)
  • Import in batches using JQL date ranges
  • Use the --file option with a manual export

API Rate Limits

  • Jira Cloud: ~100 requests/minute (varies by plan)
  • Jira Server/DC: Depends on configuration

This script fetches 100 issues per request, so a 1000-issue project requires ~10 API calls.

See Also