jps-jira-utils 0.22.0
Collection of Python scripts for interacting with Jira tickets.
License: MIT
Requires Python: >=3.10
Keywords: jira, comment, automation

# jps-jira-utils

![Build & Test](https://github.com/jai-python3/jps-jira-utils/actions/workflows/test.yml/badge.svg)
![Publish to PyPI](https://github.com/jai-python3/jps-jira-utils/actions/workflows/publish-to-pypi.yml/badge.svg)
[![codecov](https://codecov.io/gh/jai-python3/jps-jira-utils/branch/main/graph/badge.svg)](https://codecov.io/gh/jai-python3/jps-jira-utils)
[![Python Version](https://img.shields.io/badge/python-3.11%2B-blue)](#)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](#)

> **A clean, modular, production-grade toolkit for Jira power users**  
> Built with Typer, rich logging, and full test coverage.

## Features

- **Unified CLI** — `jps-jira-utils` command with subcommands for all operations
- **Create Issues** — `create_issue.py` — Create new Jira issues with all required fields
  - Interactive prompts for issue type, summary, and description
  - Automatic reporter field handling (uses authenticated user)
  - Support for all required fields: Space (project), Work Type, Reporter, Title
  - Full validation and confirmation before creation
- **Update Issues** — `update_issue.py` — Bulk update issue fields from YAML configuration
  - Update custom fields (Software Component, PROGRAM, TICKET TYPE, Project Label, Team)
  - Smart sprint assignment (latest sprint matching project key)
  - Automatic date calculations (Due Date, Dev Complete, QA Complete, UAT Start)
  - Business day calculations with configurable defaults
  - Only updates empty fields (preserves existing values)
  - Dry-run mode for validation
- **Add Comments** — `add_comment.py` — Add rich comments (with optional file attachments) to any Jira issue
  - **Interactive Mode**: Choose between branch, PR, or general comments
  - **Branch Comments**: Parse Azure DevOps branch URLs and create hyperlinked comments (e.g., "Created this bugfix branch [name](url) from source branch [name](url)")
  - **PR Comments**: Parse Azure DevOps PR URLs and create hyperlinked comments (e.g., "Created this pull-request: [PR-12345](url)")
  - **General Comments**: Interactive multiline input (ends on two blank lines)
  - **File Mode**: Load comment from file (`--comment-file`)
  - Attach files with pre-canned or custom notes (STDOUT, STDERR, log file, README, etc.)
  - Automatic timestamping for README.md attachments
  - Final confirmation table before posting
  - Full logging with structured output
  - Optional custom config file (`--config-file`)
- Secure credential loading via `~/.config/jps-jira-utils/.jira.env` (never in code or CLI)
- Clean, maintainable, fully tested codebase using modern Python best practices
- Zero external runtime dependencies beyond `requests`, `typer`, and `python-dotenv`

## Installation

### From source (recommended for development)

```bash
git clone https://github.com/jai-python3/jps-jira-utils.git
cd jps-jira-utils
make install    # creates .venv and installs in editable mode
```


From PyPI (coming soon)

```bash
pip install jps-jira-utils
```

## Configuration

### Setting up credentials

The tool requires Jira Cloud credentials to authenticate API requests. By default, credentials are loaded from:

```
~/.config/jps-jira-utils/.jira.env
```

#### Auto-create the configuration file

You can automatically create the configuration directory and file with placeholder values:

```bash
mkdir -p ~/.config/jps-jira-utils
cat > ~/.config/jps-jira-utils/.jira.env << 'EOF'
JIRA_BASE_URL=https://your-company.atlassian.net
JIRA_EMAIL=your.email@company.com
JIRA_API_TOKEN=your_api_token_here
EOF
```

Then edit the file with your actual credentials:

```bash
nano ~/.config/jps-jira-utils/.jira.env
# or use your preferred editor
```

#### Manual configuration

Create the configuration directory and file manually:

```bash
mkdir -p ~/.config/jps-jira-utils
```

Then create `~/.config/jps-jira-utils/.jira.env` with your Jira Cloud credentials:

```env
JIRA_BASE_URL=https://your-company.atlassian.net
JIRA_EMAIL=your.email@company.com
JIRA_API_TOKEN=your_api_token_here
```

**Generate your API token at:** https://id.atlassian.com/manage-profile/security/api-tokens

#### Using a custom config file

If you need to use a different location for your configuration file, you can specify it with the `--config-file` option:

```bash
jps-add-comment JPS-1234 --config-file /path/to/custom/.jira.env
```

## Prerequisites

Python 3.11 or higher is required.

## Usage

### Unified CLI

View all available commands:

```bash
jps-jira-utils --help
```

### Create a new issue

Create an issue interactively:

```bash
jps-jira-utils create-issue JPS
```

Or using the standalone command:

```bash
jps-jira-utils-create-issue JPS
```

Create with all details provided:

```bash
jps-jira-utils create-issue JPS \
  --summary "Fix login bug" \
  --issue-type Bug \
  --description "Users cannot log in with SSO"
```

**Required fields for issue creation:**
- **Space** (Project key): e.g., JPS
- **Work Type** (Issue type): e.g., Task, Bug, Story
- **Reporter**: Automatically set to authenticated user's email
- **Title** (Summary): Brief description of the issue

### Update issue fields

Update an issue with values from config file:

```bash
jps-jira-utils update-issue COMPBIO-1234
```

Or using the standalone command:

```bash
jps-jira-utils-update-issue COMPBIO-1234
```

With custom config file:

```bash
jps-jira-utils update-issue COMPBIO-1234 --config-file ~/my-config.yaml
```

Dry run to preview changes:

```bash
jps-jira-utils update-issue COMPBIO-1234 --dry-run
```

#### Configuration File Setup

The `update-issue` command requires a YAML configuration file with project-specific settings.

**Default location:** `~/.config/jps-jira-utils/config.yaml`

**Auto-generate the configuration file:**

```bash
mkdir -p ~/.config/jps-jira-utils
cat > ~/.config/jps-jira-utils/config.yaml << 'EOF'
# Jira Issue Update Configuration
# Customize these values for your project

# Project settings
project_key: "COMPBIO"  # Your Jira project key (used for sprint matching)
board_id: 123        # Your Jira board ID for sprint lookup (find this in board settings)

# Custom field values (these will populate empty custom fields)
software_component: "MyComponent"    # Software Component dropdown value
program: "MyProgram"                 # PROGRAM field value
ticket_type: "Development"           # TICKET TYPE field value
project_label: "MyProject"           # Project Label field value
team: "Engineering Team"             # Team field value

# Date calculation settings (in business days)
# These control automatic due date calculations
default_due_date_business_days: 10                      # Due Date: Friday after N business days from today
default_dev_complete_due_date_business_days: 5          # Dev Complete: Friday after N business days from today
default_qa_complete_due_date_business_days: 2           # QA Complete: N business days after Dev Complete
default_uat_start_date_business_days: 1                 # UAT Start: N business days after QA Complete
EOF
```

Then edit the file with your actual values:

```bash
nano ~/.config/jps-jira-utils/config.yaml
# or use your preferred editor
```

**Configuration File Explanation:**

| Setting | Description | Example |
|---------|-------------|---------|
| `project_key` | Your Jira project key. Used to find the latest sprint matching this prefix (e.g., "COMPBIO FY25 Sprint 6"). | `"COMPBIO"` |
| `board_id` | Jira board ID for sprint lookups. Find this in your board's URL or settings. Optional if provided via `--board-id` CLI option. | `123` |
| `software_components` | **(Recommended)** List of available software components for interactive selection. When provided, the program will prompt you to select from this list with fuzzy search and auto-completion. | `["Backend API", "Frontend UI", "Database"]` |
| `program` | Value for the PROGRAM custom field. | `"Platform"` |
| `ticket_type` | Value for the TICKET TYPE custom field. | `"Development"` |
| `project_label` | Value for the Project Label custom field. | `"Q1 Initiative"` |
| `team` | Value for the Team custom field. | `"Backend Team"` |
| `default_due_date_business_days` | Number of business days from today to calculate Due Date. Result will be the next Friday after N business days. | `10` |
| `default_dev_complete_due_date_business_days` | Number of business days from today to calculate Dev Complete Due Date. Result will be the next Friday after N business days. | `5` |
| `default_qa_complete_due_date_business_days` | Number of business days after Dev Complete Due Date to set QA Complete Due Date. | `2` |
| `default_uat_start_date_business_days` | Number of business days after QA Complete Due Date to set UAT Start Date. | `1` |

**Interactive Software Component Selection:**

When `software_components` is defined in your config.yaml as a list, the program will display an interactive picker:
- Enter a number to select by position (e.g., `1` for first component)
- Start typing to filter the list with fuzzy search (e.g., type `back` to filter to "Backend API")
- Press Enter to confirm selection
- After each selection, choose whether to add another component (default: Yes)
- Press Ctrl+C or Ctrl+D to cancel

**Finding your Board ID:**

1. Go to your Jira board
2. Look at the URL: `https://your-company.atlassian.net/jira/software/c/projects/COMPBIO/boards/123`
3. The number after `/boards/` is your board ID (e.g., `123`)

**Example Configuration:**

```yaml
# Project settings
project_key: "COMPBIO"
board_id: 123

# Custom field values
software_component: "Backend API"
program: "Platform Modernization"
ticket_type: "Development"
project_label: "Q1 2025 Initiative"
team: "Backend Engineering"

# Date calculation settings (in business days)
default_due_date_business_days: 10
default_dev_complete_due_date_business_days: 5
default_qa_complete_due_date_business_days: 2
default_uat_start_date_business_days: 1
```

**What gets updated:**
- Software Component (custom field)
- Sprint (latest sprint matching project key)
- PROGRAM, TICKET TYPE, Project Label, Team (custom fields)
- Due Date (Friday after N business days)
- Dev Complete Due Date (Friday after N business days)
- QA Complete Due Date (N business days after Dev Complete)
- UAT Start Date (N business days after QA Complete)
- Assignee (current user)

**Note:** Only updates fields that are currently empty.

### Add a comment to an issue

The `add-comment` command supports three modes of operation:

#### 1. Interactive Mode (Default)

When neither `--comment-file` nor `--attach-file` are specified, you'll be prompted to choose:

```bash
jps-jira-utils add-comment JPS-1234
```

Or using the standalone command:

```bash
jps-jira-utils-add-comment JPS-1234
```

You'll see:
```
What type of comment would you like to add?
  1. Add comment about a new branch
  2. Add comment about a PR
  3. Add a general comment
Select option (1-3):
```

**Option 1: Branch Comment**

Creates a formatted comment with hyperlinked branch names:

```
Provide the new branch URL: https://dev.azure.com/compbiosystems/Comp%20Biosystems/_git/sysbio?version=GBbugfix/COMPBIO-1272-fix
Provide the source branch URL: https://dev.azure.com/compbiosystems/Comp%20Biosystems/_git/sysbio?version=GBdevelop
```

Result: "Created this bugfix branch [bugfix/COMPBIO-1272-fix](url) from source branch [develop](url)."

**Option 2: PR Comment**

Creates a formatted comment with a hyperlinked PR reference:

```
Provide the pull-request URL: https://dev.azure.com/compbiosystems/Comp%20Biosystems/_git/sysbio/pullrequest/33081
```

Result: "Created this pull-request: [PR-33081](url)."

**Option 3: General Comment**

Prompts for multiline text input (ends on two blank lines):

```
Enter your comment (end with two blank lines):
```

#### 2. File-Based Mode

Add a comment from a file:

```bash
jps-jira-utils add-comment JPS-1234 --comment-file results.txt
```

Add a comment with an attachment:

```bash
jps-jira-utils add-comment JPS-1234 \
  --comment-file results.txt \
  --attach-file test-run.log
```

#### 3. Attachment-Only Mode

Attach a file without a comment:

```bash
jps-jira-utils add-comment JPS-1234 --attach-file test-run.log
```

**Attachment Features:**
- Supports pre-canned notes (STDOUT, STDERR, log file, README, etc.)
- Automatic timestamping for README.md files
- Final confirmation table before posting

### Get help

Full help for any command:

```bash
jps-jira-utils --help
jps-jira-utils create-issue --help
jps-jira-utils update-issue --help
jps-jira-utils add-comment --help
```


## Development
```bash
# Format, lint, and fix everything
make fix
make format
make lint
```

### Run tests with coverage

```bash
make test
```

### Run pre-commit hooks (recommended)

```bash
pre-commit run --all-files
```


## Project Structure
```text
src/jps_jira_utils/
├── cli.py                # Main CLI entry point with subcommands
├── create_issue.py       # Create new Jira issues
├── update_issue.py       # Update issue fields from YAML config
├── add_comment.py        # Add comments to Jira issues
├── jira_helper.py        # Credential loading
├── logging_helper.py     # Structured logging setup
└── prompt_helper.py      # Interactive input utilities

tests/                    # Full test coverage with pytest
├── test_cli.py           # CLI tests
├── test_create_issue.py  # Issue creation tests
├── test_update_issue.py  # Issue update tests
├── test_add_comment.py   # Comment addition tests
└── ...

config.yaml.example       # Example YAML configuration file
```

## Troubleshooting

### Field Compatibility Issues

If you see errors like `"Field 'customfield_XXXXX' cannot be set. It is not on the appropriate screen, or unknown."`, this means:

- The field doesn't exist in your Jira instance
- The field exists but is not available on this issue's edit screen
- You don't have permissions to edit that field

**Solution:** The `update-issue` command will automatically skip incompatible fields and update the remaining fields. Check your config.yaml to ensure field names match your Jira setup.

### Sprint Lookup Failures

If you see `"Failed to fetch sprints for board XXX: 404"`:

- Verify your `board_id` is correct (see "Finding your Board ID" above)
- Ensure you have view permissions for that board
- The board may not exist or may have been deleted

**Solution:** The sprint field will be skipped, but all other fields will update normally. To fix, update your `board_id` in config.yaml or use `--board-id` option.

### UAT Start Date Warnings

If you see `"Cannot calculate UAT Start Date without QA Complete Date"`:

- The UAT Start Date field doesn't exist in your Jira instance
- Or the QA Complete Date field hasn't been set/calculated

**Solution:** This is informational only. The program will skip the UAT Start Date calculation and continue with other updates.

##  Contributing

Contributions are welcome! Please:

Use pre-commit hooks
Write tests for new features
Follow Google-style docstrings
Keep the codebase clean and modular

## License

MIT License © Jaideep Sundaram