Skip to main content

Bidirectionally sync a Jira project with a Todo+ (vscode-todo-plus) text file.

Project description

todo-jira-sync

Bidirectionally sync a Jira project with a Todo+ plain-text file — the format used by the vscode-todo-plus extension. Edit your backlog as a flat, version-controllable text file in your editor while having things sync in Jira.

Scaffolded from tedivm/robs_awesome_python_template conventions (uv, pyproject.toml, Typer, Pydantic Settings, Ruff, mypy, pytest, GitHub Actions).

Mapping rules

Each non-blank, non-comment line maps to one Jira issue, by indentation and the trailing colon:

Todo+ line Jira issue type
ends with :, no leading indent Epic
ends with :, indented User Story
a task ( / / …) Task
a task nested under another task Sub-task
Authentication:                 ← Epic
  Login flow:                   ← Story    (parent: Authentication)
    ☐ Build the login form      ← Task     (parent: Login flow)
    ☐ Add OAuth providers       ← Task     (parent: Login flow)
      ☐ Google provider         ← Sub-task (parent: Add OAuth providers)
  ☐ Password reset email        ← Task     (parent: Authentication)

A node's Jira parent is its nearest enclosing container: a Task takes the Story it sits under, or the Epic if there is no enclosing Story; a Story takes its Epic; a Sub-task takes the Task it sits under.

Status mapping

Todo+ symbol Status Jira category
[ ] To Do new
@started(...) In Progress indeterminate
[x] Done done
[-] @cancelled Cancelled done + cancelled status name

Jira has no "cancelled" status category; cancelled is detected by status name (configurable via CANCELLED_STATUS_NAMES).

How identity works

Each synced line gets a @jira(KEY) tag written back into the file (inserted before the trailing colon for projects, so it still reads as a project):

Authentication @jira(WEB-1):
  ☐ Build the login form @jira(WEB-3)

That tag is the stable anchor — rename a line freely and the link survives.

Install

Requires Python ≥ 3.10. Using uv:

uv venv
uv pip install -e ".[dev]"

or with pip:

python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"

Configure

Copy .env.example to .env and fill in your details:

cp .env.example .env

For Jira Cloud, create an API token at https://id.atlassian.com/manage-profile/security/api-tokens and set JIRA_EMAIL + JIRA_API_TOKEN with JIRA_AUTH=basic. For Server/Data Center, use a personal access token with JIRA_AUTH=bearer.

Use

# See what would happen — touches nothing:
todo-jira-sync status --todo todo.todo --project WEB

# Full bidirectional sync:
todo-jira-sync sync --todo todo.todo --project WEB

# One-way only:
todo-jira-sync push    # local file -> Jira (never edits the file)
todo-jira-sync pull    # Jira -> local file (never creates/edits Jira)

# Conflict policy when both sides changed the same field:
todo-jira-sync sync --conflict jira   # jira | todo | skip

A JSON sidecar todo.todo.todojira.json is written next to your file. It is the 3-way-merge baseline (the common ancestor) — keep it, but it need not be committed (it is git-ignored by default).

How sync decides (3-way merge)

For each issue the engine compares the live file and live Jira against the baseline from the last run:

  • only the file changed → push to Jira
  • only Jira changed → pull into the file
  • both changed the same field → conflict, resolved by --conflict
  • new in file → created in Jira (parents first)
  • new in Jira → pulled into the file (under the right parent)
  • gone from Jira but known → kept locally and reported (never silently lost)

The engine never deletes Jira issues and never deletes local lines.

Docker

A multi-stage, uv-based image is included. It builds the package into a slim runtime image whose entrypoint is the CLI, running as a non-root user.

# Build (VERSION is only needed because versioning comes from git tags):
docker build --build-arg VERSION=0.1.0 -t todo-jira-sync .

# Run against a working directory that holds your todo file and .env:
docker run --rm --env-file .env -v "$PWD:/work" todo-jira-sync \
  sync --todo todo.todo --project WEB

Or via Compose (put your todo file and .env in ./work):

docker compose run --rm sync status
docker compose run --rm sync sync --todo todo.todo --project WEB

Prebuilt multi-arch images (amd64 + arm64) are published to GitHub Container Registry by CI on version tags (and on demand via the workflow's manual trigger).

Releasing

Versioning is derived from git tags via setuptools-scm. Pushing a tag like v0.1.0 triggers two workflows: one builds the sdist/wheel and publishes to PyPI (via OIDC Trusted Publishing — no API token stored), the other builds and pushes the multi-arch container image. CI (ci.yaml) runs ruff, mypy and pytest on a Python 3.10–3.14 matrix for every push and PR.

Develop

make check     # ruff + mypy + pytest
make test

The sync core (config, models, todo_format, state, sync) is pure standard library, so the tests run against an in-memory fake Jira with no network and no live credentials. The two test files also run standalone:

python tests/test_todo_format.py
python tests/test_sync.py

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

todo_jira_sync-1.1.2.tar.gz (38.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

todo_jira_sync-1.1.2-py3-none-any.whl (23.2 kB view details)

Uploaded Python 3

File details

Details for the file todo_jira_sync-1.1.2.tar.gz.

File metadata

  • Download URL: todo_jira_sync-1.1.2.tar.gz
  • Upload date:
  • Size: 38.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for todo_jira_sync-1.1.2.tar.gz
Algorithm Hash digest
SHA256 00fd45dd3d6e7ae32c2e97746de0a880947e0ff8fe195bf38e38e50beba9cf95
MD5 ea61e5a7994054fa50ee70953348d208
BLAKE2b-256 5d27c7d52589c131060e31285508d8f3caf94b1e972ee3621c582f7d30ae2b5e

See more details on using hashes here.

Provenance

The following attestation bundles were made for todo_jira_sync-1.1.2.tar.gz:

Publisher: release.yaml on kalw/todo-jira-sync

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file todo_jira_sync-1.1.2-py3-none-any.whl.

File metadata

  • Download URL: todo_jira_sync-1.1.2-py3-none-any.whl
  • Upload date:
  • Size: 23.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for todo_jira_sync-1.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 27c4f4ce6edce93bdc454294fbb4201a05982eed7418af80b6a10010757d8cc0
MD5 b6e36a69bf3c093aa4292ad6cada3437
BLAKE2b-256 1d09eb60021e5e2b1080b5bf7cfad2539ea61ee90bc2f626aa481fab3339bfa6

See more details on using hashes here.

Provenance

The following attestation bundles were made for todo_jira_sync-1.1.2-py3-none-any.whl:

Publisher: release.yaml on kalw/todo-jira-sync

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page