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; run one command; Jira and the file converge.

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.0.0.tar.gz (32.0 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.0.0-py3-none-any.whl (23.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: todo_jira_sync-1.0.0.tar.gz
  • Upload date:
  • Size: 32.0 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.0.0.tar.gz
Algorithm Hash digest
SHA256 b6d7c7c3822df59f0577a686268a51ab5694f3a34b5196ac66ea241b0a747fb9
MD5 23f9c5933e8314fcf62cab04914011ba
BLAKE2b-256 3c67e1fc6b8aa04b5d5c03f58f229b4a4c093917ffabae070d215d5a7ad8b894

See more details on using hashes here.

Provenance

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

Publisher: publish-pypi.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.0.0-py3-none-any.whl.

File metadata

  • Download URL: todo_jira_sync-1.0.0-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.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ee5c6bb444fd6678afe5a0a82eb63a350d5756daf451b6f1a448a34979482f0d
MD5 28ee6b24b032ed9e540a60aec8a324cf
BLAKE2b-256 f26700e9e95813410e8036f2a97fedd23e7f52a8117c1498dcb2be1cfa6720e8

See more details on using hashes here.

Provenance

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

Publisher: publish-pypi.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