# Jira ↔ Jotty Sync Bidirectional sync between [Jira](https://www.atlassian.com/software/jira) stories and [Jotty](https://github.com/fccview/jotty) task checklists. ## Features - **Jira → Jotty**: Every Jira story becomes a checklist item in Jotty - **Status sync**: Jira status maps to Jotty Kanban columns (To Do / In Progress / Completed / Paused) - **Idempotent**: Running the script multiple times won't create duplicates — it reconciles state each run - **Jira as source of truth**: Jira is the authoritative data source; Jotty items are updated/deleted to match ## Setup ### 1. Create a virtual environment and install dependencies ```bash python -m venv .venv source .venv/bin/activate pip install -r requirements.txt ``` ### 2. Configure credentials Copy the template and fill in your values: ```bash cp sync_config.json.template sync_config.json ``` Or set environment variables (recommended — keep `.env` out of version control): ```bash # .env file JIRA_BASE_URL=https://jira.your-domain.com JIRA_API_TOKEN=your_personal_access_token PROJECT_KEY=PROJ JOTTY_BASE_URL=https://jotty.your-domain.com JOTTY_API_KEY=ck_your_api_key_here ``` ### 3. Test the connection ```bash source .venv/bin/activate python jira_jotty_sync.py --direction=jira-to-jotty ``` ## Usage ```bash # Pull Jira stories into Jotty only python jira_jotty_sync.py --direction=jira-to-jotty # Push Jotty check/uncheck changes to Jira status only python jira_jotty_sync.py --direction=jotty-to-jira # Full two-way sync (default) python jira_jotty_sync.py --direction=both ``` ## Docker (Raspberry Pi 5 / ARM64) ### Build for Multiple Platforms Build a multi-architecture image supporting `linux/amd64`, `linux/arm/v7`, and `linux/arm64`: ```bash docker buildx create --use --name multi-arch-builder --driver docker-container docker buildx build -t "jotty-jira-sync:latest" --no-cache --platform linux/amd64,linux/arm/v7,linux/arm64 . ``` > **Note:** The `docker-container` driver stores the result in the build cache. To push to a registry, add `--push`: > ```bash > docker buildx build -t "your-registry/jotty-jira-sync:latest" --no-cache --platform linux/amd64,linux/arm/v7,linux/arm64 --push . > ``` ### Push to Registry Tag and push the image to your local registry (requires Docker insecure-registries config for HTTP): ```bash docker tag jotty-jira-sync:latest beere5:9444/jotty-jira-sync:latest docker push beere5:9444/jotty-jira-sync:latest ``` If you get a `http: server gave HTTP response to HTTPS client` error, configure Docker to allow the insecure registry: ```bash sudo mkdir -p /etc/docker echo '{"insecure-registries": ["beere5:9444"]}' | sudo tee /etc/docker/daemon.json sudo systemctl restart docker ``` ### Clean Up Unused Images Remove unused Docker images to free disk space: ```bash docker image prune -f ``` ### Build for Single Platform (local) On Raspberry Pi 5 (native ARM64): ```bash docker build -t jotty-jira-sync:arm64 . ``` From another machine using buildx (single platform only): ```bash docker buildx build \ --platform linux/arm64 \ -t jotty-jira-sync:arm64 \ . ``` ### Prepare runtime config files Create local config files in the project folder: ```bash cp sync_config.json.template sync_config.json # Optional if you prefer env-based auth instead of putting secrets in JSON touch .env ``` ### Run container The container runs `python jira_jotty_sync.py --direction both` by default. ```bash docker run --rm \ --name jotty-jira-sync \ -v "$(pwd)/sync_config.json:/app/sync_config.json:ro" \ -v "$(pwd)/.env:/app/.env:ro" \ -v "$(pwd)/sync_mapping.json:/app/sync_mapping.json" \ jotty-jira-sync:arm64 ``` Run a one-way sync instead: ```bash docker run --rm \ -v "$(pwd)/sync_config.json:/app/sync_config.json:ro" \ -v "$(pwd)/.env:/app/.env:ro" \ -v "$(pwd)/sync_mapping.json:/app/sync_mapping.json" \ jotty-jira-sync:arm64 --direction jira-to-jotty ``` Notes: - Keep `.env` and `sync_config.json` private because they contain credentials. - `sync_mapping.json` is mounted so state persists across container runs. - If you do not use `.env`, remove that `-v` mount and rely on `sync_config.json`. ### CLI overrides Override config values from the command line: ```bash python jira_jotty_sync.py \ --jira-base https://jira.example.com \ --jira-token your_token \ --project PROJ \ --jotty-base https://jotty.example.com \ --jotty-key ck_your_key ``` ## Status Mapping ### Jira → Jotty (Kanban columns) | Jira Status | Jotty Column | |-------------|--------------| | NEW, TO DO, WAITING, READY | To Do | | IN PROGRESS, TEST, PO REVIEW, READY FOR QA | In Progress | | DONE, DEPLOYED, LIVE, TEST COMPLETED | Completed | | IN QA, REJECTED | Paused | ### Jotty → Jira | Jotty Column | Jira Status | |--------------|-------------| | To Do | TO DO | | In Progress | IN PROGRESS | | Completed | DONE | | Paused | WAITING | ## How It Works 1. **Jira→Jotty sync**: - Fetches all stories matching the JQL query from Jira - Scans existing items in the Jotty checklist - Updates status and text for matching items - Creates new items for missing issues - Deletes duplicate/orphaned items 2. **Jotty→Jira sync**: - Reads current state of all checklist items - Compares with actual Jira issue statuses - Pushes status changes to Jira when they differ ## File Structure ``` jira_jotty_sync.py # Main sync script requirements.txt # Python dependencies sync_config.json.template # Configuration template .env # Credentials (git-ignored) sync_mapping.json # Auto-generated mapping file (Jira key → Jotty item index) tests/ # Unit tests ``` ## Notes - The `sync_mapping.json` file is auto-managed — don't edit it manually - `.env` and `sync_mapping.json` are git-ignored to protect credentials - Uses the official [`jira`](https://pypi.org/project/jira/) Python library for Jira API access - Personal Access Tokens (PAT) are used for self-hosted Jira authentication **Option A — `.env` file (recommended):** Copy and edit `.env.template`: ```bash cp .env.template .env ``` Fill in your Jira base URL, username, API token, project key, and Jotty base URL + API key. `.env` is gitignored so it won't be committed. **Option B — `sync_config.json`:** Copy and edit `sync_config.json.template`: ```bash cp sync_config.json.template sync_config.json ``` Fill in your credentials there instead. **Credential priority (highest → lowest):** 1. CLI flags (`--jira-base`, `--jira-user`, etc.) 2. Environment variables (`JIRA_BASE_URL`, `JOTTY_API_KEY`, …) 3. `.env` file (auto-loaded from the script's directory) 4. `sync_config.json` | Variable | Description | |---|---| | `JIRA_BASE_URL` | e.g. `https://your-org.atlassian.net` | | `JIRA_USERNAME` | Your Jira email / username | | `JIRA_API_TOKEN` | Jira API token (from atlassian.com) | | `PROJECT_KEY` | Jira project key, e.g. `PROJ` | | `JOTTY_BASE_URL` | e.g. `https://jotty.your-domain.com` | | `JOTTY_API_KEY` | Your Jotty API key (starts with `ck_`) | ### 3. Run the sync ```bash # Full two-way sync python jira_jotty_sync.py --direction both # Pull new/updated stories from Jira into Jotty only python jira_jotty_sync.py --direction jira-to-jotty # Push Jotty check/uncheck changes to Jira only python jira_jotty_sync.py --direction jotty-to-jira ``` ### 4. CLI overrides All config values can be overridden on the command line: ```bash python jira_jotty_sync.py \ --jira-base https://mycompany.atlassian.net \ --jira-user you@company.com \ --project PROJ \ --direction jira-to-jotty ``` ## Status Mapping Edit `JIRA_TO_JOTTY_STATUS` and `JOTTY_TO_JIRA_STATUS` in the script to match your workflow. | Jira Status | → Jotty Status | |---|---| | To Do | todo | | In Progress | in_progress | | Done | completed | | In Review | review | | Blocked | blocked | ## Mapping Persistence The file `sync_mapping.json` stores the mapping between Jira issue keys and Jotty checklist item indices. It is automatically created on first run. ## Automation Run periodically via cron: ```bash # Every 15 minutes */15 * * * * cd /path/to/jotty_jira && python jira_jotty_sync.py --direction both >> sync.log 2>&1 ```