309 lines
8.1 KiB
Markdown
309 lines
8.1 KiB
Markdown
# 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
|
|
``` |