Skip to content

Installation & Configuration

Requirements

  • Python 3.13+
  • uv (recommended) or pip
  • A KeePass database holding your Jira credentials

Install

git clone <repository-url>
cd jira-transition-runner
uv sync

This installs all runtime dependencies declared in pyproject.toml.

Settings (settings.toml)

Settings are loaded with Dynaconf from settings.toml in the project root. The file has a [default] section plus one section per environment: [development], [staging] and [production]. An environment section only needs the keys that differ from [default].

[default]
# ── Required ─────────────────────────────────────────────────────────────────
JIRA_BASE_URL = "https://your-domain.atlassian.net"

# KeePass database that stores the Jira credentials
KEEPASS_DB = "C:/keepass/Database.kdbx"
KEEPASS_KEY = "C:/keepass/Database.key"
KEEPASS_ENTRY = "Jira Entry Name"

# ── Concurrency ──────────────────────────────────────────────────────────────
JIRA_MAX_CONCURRENCY = 10   # simultaneous API requests
PHASE2_WORKERS = 15         # concurrent issue transitions in Phase 2

# ── Retry ────────────────────────────────────────────────────────────────────
JIRA_MAX_RETRIES = 3
JIRA_RETRY_BACKOFF_MIN = 2.0    # seconds
JIRA_RETRY_BACKOFF_MAX = 30.0   # seconds

# ── Rate limiting (HTTP 429) ─────────────────────────────────────────────────
JIRA_RATE_LIMIT_MAX_RETRIES = 10   # retries per request for HTTP 429
JIRA_RATE_LIMIT_MAX_WAIT = 300     # seconds; fail if Jira asks to wait longer

# ── Status classification ────────────────────────────────────────────────────
DONE_STATUSES = ["Done", "Cancelled", "Closed"]
BUG_ISSUE_TYPES = ["Bug", "UAT Defect", "Production Defect"]   # case-insensitive
PRIORITY_STATUSES = ["Closed", "Done", "Cancelled"]            # smart-default order

# ── Mandatory field (Phase 2) ────────────────────────────────────────────────
# Set before transitioning these issue types if empty. Skipped in --dry-run.
# Use MANDATORY_FIELD_ID = "" to disable.
MANDATORY_FIELD_ID = "customfield_10000"
MANDATORY_FIELD_VALUE = "Some value"
MANDATORY_FIELD_ISSUE_TYPES = ["Bug", "UAT Defect"]

[development]
JIRA_BASE_URL = "https://your-sandbox.atlassian.net"
PHASE2_WORKERS = 2

[staging]
JIRA_BASE_URL = "https://your-sandbox.atlassian.net"

[production]
KEEPASS_ENTRY = "Jira (prod)"

Choosing the environment

The active environment comes from JIRA_ENV and defaults to development. Set it in a .env file in the project root (see .env.example) or in the shell:

# .env
JIRA_ENV=production
$env:JIRA_ENV = "staging"; jira-runner ...

The CLI logs the active environment and Jira URL at startup, and the GUI shows them under Global settings.

Overrides

Highest precedence wins:

  1. settings.toml, the [default] section, then the active environment's section
  2. .secrets.toml (optional, git-ignored, same layout), for values you don't want committed
  3. JTR_-prefixed environment variables, including those in .env, e.g. JTR_PHASE2_WORKERS=5 or JTR_DONE_STATUSES='["Done", "Closed"]'

settings.toml is read from the current directory if it has one, otherwise from the project root. Set JTR_SETTINGS_DIR to use another directory.

Workflow Configuration (transition.yaml)

Define per-project workflows. Each project maps issue types to workflows, and each workflow lists named transitions with from, to, and Jira transition id.

projects:
  PROJ:
    projectId: '10000'
    workflowSchemeId: 10001
    issueTypes:
      Story:
        workflow: Story_Workflow
        transitions:
          open_to_in_progress:
            id: '11'
            name: Start Progress
            from: Open
            to: In Progress
          in_progress_to_done:
            id: '21'
            name: Done
            from: In Progress
            to: Done

Tip

You can find transition IDs in Jira by navigating to Project Settings → Workflows and inspecting the transition links, or by calling GET /rest/api/3/issue/{key}/transitions.

Logging Configuration (logging_config.yaml)

The default config logs WARNING+ to the console and DEBUG+ to rotating files:

formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'

handlers:
  console:
    class: logging.StreamHandler
    level: WARNING
    formatter: simple
  file:
    class: logging.handlers.RotatingFileHandler
    level: DEBUG
    filename: jira_transition_runner.log
    maxBytes: 10485760   # 10 MB
    backupCount: 5
    formatter: simple

Pass a custom config at runtime:

jira-runner --project PROJ --yaml transition.yaml --log-config my_logging.yaml ...