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

Uploaded Python 3

File details

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

File metadata

  • Download URL: todo_jira_sync-1.2.0.tar.gz
  • Upload date:
  • Size: 41.7 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.2.0.tar.gz
Algorithm Hash digest
SHA256 001d92a69684fe360113cee346104b19468eda0ed810c014248b1d382d8645e8
MD5 12b9c82b34f9e2359a5d9e11f6405c0c
BLAKE2b-256 b8de070bc2345e3a4fe49c024588b137a67ffc08c00d9adcd73c8a002a71c464

See more details on using hashes here.

Provenance

The following attestation bundles were made for todo_jira_sync-1.2.0.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.2.0-py3-none-any.whl.

File metadata

  • Download URL: todo_jira_sync-1.2.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.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3ffd3c8264b94df99eb52b0cdf768ffb6b93ee629cd358495411b7b848229165
MD5 8b9bcaa1cceade3ff00cba7663098ad0
BLAKE2b-256 c0972aae8b6fe11c8d586e78f5892f40d16a90cf71f760336a84a9a2fe496f35

See more details on using hashes here.

Provenance

The following attestation bundles were made for todo_jira_sync-1.2.0-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