Jira ↔ Jotty Sync
Bidirectional sync between Jira stories and 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
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
2. Configure credentials
Copy the template and fill in your values:
cp sync_config.json.template sync_config.json
Or set environment variables (recommended — keep .env out of version control):
# .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
source .venv/bin/activate
python jira_jotty_sync.py --direction=jira-to-jotty
Usage
# 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:
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-containerdriver stores the result in the build cache. To push to a registry, add--push: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):
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:
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:
docker image prune -f
Build for Single Platform (local)
On Raspberry Pi 5 (native ARM64):
docker build -t jotty-jira-sync:arm64 .
From another machine using buildx (single platform only):
docker buildx build \
--platform linux/arm64 \
-t jotty-jira-sync:arm64 \
.
Prepare runtime config files
Create local config files in the project folder:
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.
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:
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
.envandsync_config.jsonprivate because they contain credentials. sync_mapping.jsonis mounted so state persists across container runs.- If you do not use
.env, remove that-vmount and rely onsync_config.json.
CLI overrides
Override config values from the command line:
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
-
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
-
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.jsonfile is auto-managed — don't edit it manually .envandsync_mapping.jsonare git-ignored to protect credentials- Uses the official
jiraPython 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:
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:
cp sync_config.json.template sync_config.json
Fill in your credentials there instead.
Credential priority (highest → lowest):
- CLI flags (
--jira-base,--jira-user, etc.) - Environment variables (
JIRA_BASE_URL,JOTTY_API_KEY, …) .envfile (auto-loaded from the script's directory)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
# 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:
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:
# Every 15 minutes
*/15 * * * * cd /path/to/jotty_jira && python jira_jotty_sync.py --direction both >> sync.log 2>&1