JIRA Integration Skill
Overview
JIRA integration skill for automating ticket creation from conversations, bug reporting, task management, and synchronizing development workflows with project management.
Capabilities
1. Ticket Management
- Create issues from conversations
- Update issue status
- Add comments
- Link related issues
- Track time
2. Automation
- Auto-create bugs from errors
- Link commits to issues
- Update status from PR merges
- Generate reports
3. Query & Search
- Search issues (JQL)
- Filter by project/status
- Get sprint information
- Track velocity
4. Reporting
- Sprint reports
- Burndown charts
- Team velocity
- Issue statistics
JIRA REST API Setup
Authentication
from jira import JIRA
import os
# Basic auth
jira = JIRA(
server='https://your-company.atlassian.net',
basic_auth=(
os.getenv('JIRA_EMAIL'),
os.getenv('JIRA_API_TOKEN')
)
)
# Or OAuth
jira = JIRA(
server='https://your-company.atlassian.net',
oauth={
'access_token': os.getenv('JIRA_ACCESS_TOKEN'),
'access_token_secret': os.getenv('JIRA_ACCESS_TOKEN_SECRET'),
'consumer_key': os.getenv('JIRA_CONSUMER_KEY'),
'key_cert': open('jira.pem').read()
}
)
Environment Variables
# .env file
JIRA_SERVER=https://your-company.atlassian.net
JIRA_EMAIL=your-email@example.com
JIRA_API_TOKEN=your-api-token
JIRA_PROJECT_KEY=PROJ # Your project key
Common Operations
1. Create Issue
def create_bug_ticket(summary, description, priority='Medium'):
"""Create a bug ticket in JIRA"""
issue_dict = {
'project': {'key': 'PROJ'},
'summary': summary,
'description': description,
'issuetype': {'name': 'Bug'},
'priority': {'name': priority},
'labels': ['auto-generated', 'from-monitoring']
}
new_issue = jira.create_issue(fields=issue_dict)
return new_issue.key
# Example usage
ticket_key = create_bug_ticket(
summary='N+1 query detected in AthleteService',
description='''
## Issue
N+1 query pattern detected in AthleteService.findAll()
## Details
- Location: src/main/java/com/siae/sport/service/AthleteService.java:45
- Queries executed: 1 + 100 (N+1)
- Performance impact: 2.3s → 0.15s after fix
## Recommendation
Use @EntityGraph or JOIN FETCH to eager load licenses
## Priority
High - affects production performance
''',
priority='High'
)
print(f"Created ticket: {ticket_key}") # e.g., PROJ-1234
2. Search Issues
def find_open_bugs(project_key='PROJ'):
"""Find all open bugs in project"""
jql = f'project = {project_key} AND issuetype = Bug AND status != Done'
issues = jira.search_issues(jql, maxResults=50)
for issue in issues:
print(f"{issue.key}: {issue.fields.summary}")
print(f" Status: {issue.fields.status}")
print(f" Assignee: {issue.fields.assignee}")
print(f" Priority: {issue.fields.priority}\n")
return issues
def find_my_issues():
"""Find issues assigned to me"""
jql = 'assignee = currentUser() AND status != Done ORDER BY priority DESC'
return jira.search_issues(jql)
3. Update Issue
def update_issue_status(issue_key, new_status):
"""Transition issue to new status"""
issue = jira.issue(issue_key)
# Get available transitions
transitions = jira.transitions(issue)
for t in transitions:
if t['name'] == new_status:
jira.transition_issue(issue, t['id'])
print(f"Updated {issue_key} to {new_status}")
return True
print(f"Transition '{new_status}' not available for {issue_key}")
return False
# Example
update_issue_status('PROJ-1234', 'In Progress')
4. Add Comment
def add_comment(issue_key, comment):
"""Add comment to issue"""
jira.add_comment(issue_key, comment)
# Example
add_comment('PROJ-1234', '''
Fix implemented:
- Added @EntityGraph annotation
- Query count reduced from 101 to 1
- Response time improved from 2.3s to 0.15s
Ready for review.
''')
5. Link Commit to Issue
def link_commit_to_issue(issue_key, commit_sha, commit_message, repo_url):
"""Link Git commit to JIRA issue"""
commit_url = f"{repo_url}/commit/{commit_sha}"
comment = f'''
Commit linked: [{commit_sha[:7]}|{commit_url}]
{commit_message}
'''
jira.add_comment(issue_key, comment)
Automation Workflows
1. Auto-Create Bug from Error Log
#!/usr/bin/env python3
import re
from jira import JIRA
def parse_error_log(log_file):
"""Parse error log and create JIRA tickets"""
errors = []
with open(log_file) as f:
for line in f:
if 'ERROR' in line or 'Exception' in line:
# Extract error details
match = re.search(r'(\w+Exception): (.+)', line)
if match:
exception_type = match.group(1)
message = match.group(2)
errors.append({
'type': exception_type,
'message': message
})
return errors
def create_tickets_from_errors(errors, jira):
"""Create JIRA tickets for unique errors"""
# Deduplicate errors
unique_errors = {}
for error in errors:
key = error['type']
if key not in unique_errors:
unique_errors[key] = error
# Create tickets
created_tickets = []
for error_type, error in unique_errors.items():
ticket = jira.create_issue(fields={
'project': {'key': 'PROJ'},
'summary': f"{error_type} in production",
'description': f"Error message: {error['message']}\n\nSee logs for stack trace.",
'issuetype': {'name': 'Bug'},
'priority': {'name': 'High'},
'labels': ['production', 'auto-generated']
})
created_tickets.append(ticket.key)
print(f"Created ticket: {ticket.key}")
return created_tickets
2. Link PR to JIRA Issue
#!/bin/bash
# Git hook: Extract JIRA issue from branch name and link PR
BRANCH_NAME=$(git branch --show-current)
# Extract issue key (e.g., feature/PROJ-1234-add-auth → PROJ-1234)
ISSUE_KEY=$(echo $BRANCH_NAME | grep -oE '[A-Z]+-[0-9]+')
if [ -z "$ISSUE_KEY" ]; then
echo "No JIRA issue key found in branch name"
exit 0
fi
echo "Found JIRA issue: $ISSUE_KEY"
# Add issue key to commit message if not present
COMMIT_MSG=$(cat .git/COMMIT_EDITMSG)
if ! echo "$COMMIT_MSG" | grep -q "$ISSUE_KEY"; then
echo "$COMMIT_MSG" | sed "1s/$/\n\nRelates to: $ISSUE_KEY/" > .git/COMMIT_EDITMSG
fi
3. Update Status on PR Merge
# GitHub Action or GitLab CI script
def update_jira_on_merge(pr_title, pr_body, merged_by):
"""Update JIRA issue status when PR is merged"""
# Extract JIRA issue key from PR
match = re.search(r'([A-Z]+-\d+)', pr_title + pr_body)
if not match:
print("No JIRA issue found in PR")
return
issue_key = match.group(1)
# Add comment
jira.add_comment(issue_key, f'''
Pull request merged by {merged_by}
Changes deployed to staging. Ready for QA testing.
''')
# Transition to "Ready for QA"
update_issue_status(issue_key, 'Ready for QA')
Integration Scripts
jira_cli.py
Command-line JIRA interface:
#!/usr/bin/env python3
import sys
import os
from jira import JIRA
jira = JIRA(
server=os.getenv('JIRA_SERVER'),
basic_auth=(os.getenv('JIRA_EMAIL'), os.getenv('JIRA_API_TOKEN'))
)
def create_issue(args):
"""Create new issue"""
project = args[0] if args else 'PROJ'
issue_type = args[1] if len(args) > 1 else 'Task'
summary = input("Summary: ")
description = input("Description: ")
priority = input("Priority (Low/Medium/High): ") or 'Medium'
issue = jira.create_issue(fields={
'project': {'key': project},
'summary': summary,
'description': description,
'issuetype': {'name': issue_type},
'priority': {'name': priority}
})
print(f"\n✅ Created: {issue.key}")
print(f" URL: {jira.server}/browse/{issue.key}")
def list_issues(args):
"""List issues"""
project = args[0] if args else 'PROJ'
jql = f'project = {project} AND status != Done ORDER BY priority DESC, created DESC'
issues = jira.search_issues(jql, maxResults=20)
print(f"\n📋 Issues in {project}:\n")
for issue in issues:
print(f"{issue.key}: {issue.fields.summary}")
print(f" Status: {issue.fields.status.name} | Priority: {issue.fields.priority.name}")
assignee = issue.fields.assignee.displayName if issue.fields.assignee else 'Unassigned'
print(f" Assignee: {assignee}\n")
def show_issue(args):
"""Show issue details"""
if not args:
print("Usage: jira_cli.py show ISSUE-KEY")
sys.exit(1)
issue_key = args[0]
issue = jira.issue(issue_key)
print(f"\n{issue.key}: {issue.fields.summary}")
print(f"Status: {issue.fields.status.name}")
print(f"Priority: {issue.fields.priority.name}")
print(f"Assignee: {issue.fields.assignee.displayName if issue.fields.assignee else 'Unassigned'}")
print(f"\nDescription:\n{issue.fields.description}")
# Comments
comments = jira.comments(issue)
if comments:
print(f"\nComments ({len(comments)}):")
for comment in comments[-3:]: # Last 3 comments
print(f"\n {comment.author.displayName} ({comment.created}):")
print(f" {comment.body[:100]}...")
def main():
if len(sys.argv) < 2:
print("Usage: jira_cli.py {create|list|show} [args]")
sys.exit(1)
command = sys.argv[1]
args = sys.argv[2:]
commands = {
'create': create_issue,
'list': list_issues,
'show': show_issue,
}
if command in commands:
commands[command](args)
else:
print(f"Unknown command: {command}")
sys.exit(1)
if __name__ == '__main__':
main()
sprint_report.py
Generate sprint report:
#!/usr/bin/env python3
from jira import JIRA
import os
from collections import defaultdict
jira = JIRA(
server=os.getenv('JIRA_SERVER'),
basic_auth=(os.getenv('JIRA_EMAIL'), os.getenv('JIRA_API_TOKEN'))
)
def get_sprint_report(board_id):
"""Generate sprint report"""
# Get active sprint
sprints = jira.sprints(board_id, state='active')
if not sprints:
print("No active sprint")
return
sprint = sprints[0]
print(f"=== Sprint Report: {sprint.name} ===\n")
# Get sprint issues
jql = f'sprint = {sprint.id}'
issues = jira.search_issues(jql, maxResults=100)
# Group by status
by_status = defaultdict(list)
by_type = defaultdict(int)
story_points = defaultdict(int)
for issue in issues:
status = issue.fields.status.name
issue_type = issue.fields.issuetype.name
by_status[status].append(issue)
by_type[issue_type] += 1
# Story points (if custom field configured)
# points = getattr(issue.fields, 'customfield_10016', 0) or 0
# story_points[status] += points
# Print summary
print(f"Total Issues: {len(issues)}\n")
print("By Status:")
for status, issues_list in by_status.items():
print(f" {status}: {len(issues_list)}")
print("\nBy Type:")
for issue_type, count in by_type.items():
print(f" {issue_type}: {count}")
# Completion rate
done = len(by_status.get('Done', []))
completion_rate = (done / len(issues) * 100) if issues else 0
print(f"\nCompletion Rate: {completion_rate:.1f}%")
# List incomplete items
print("\n⚠️ Incomplete Items:")
for status, issues_list in by_status.items():
if status != 'Done':
for issue in issues_list:
print(f" {issue.key}: {issue.fields.summary}")
if __name__ == '__main__':
import sys
board_id = sys.argv[1] if len(sys.argv) > 1 else 1
get_sprint_report(board_id)
JQL (JIRA Query Language) Examples
# My open issues
assignee = currentUser() AND status != Done
# High priority bugs
project = PROJ AND issuetype = Bug AND priority = High AND status != Done
# Issues updated this week
project = PROJ AND updated >= -7d
# Issues in current sprint
sprint in openSprints()
# Overdue issues
duedate < now() AND status != Done
# Issues without assignee
project = PROJ AND assignee is EMPTY
# Issues created by me
reporter = currentUser() AND created >= -30d
# Performance-related issues
project = PROJ AND (summary ~ "performance" OR labels = performance)
GitHub Actions Integration
# .github/workflows/jira-integration.yml
name: JIRA Integration
on:
pull_request:
types: [opened, closed]
issues:
types: [opened]
jobs:
jira:
runs-on: ubuntu-latest
steps:
- name: Extract JIRA Issue
id: extract
run: |
ISSUE_KEY=$(echo "${{ github.event.pull_request.title || github.event.issue.title }}" | grep -oE '[A-Z]+-[0-9]+' | head -1)
echo "issue_key=$ISSUE_KEY" >> $GITHUB_OUTPUT
- name: Update JIRA Issue
if: steps.extract.outputs.issue_key != ''
uses: atlassian/gajira-transition@v3
with:
issue: ${{ steps.extract.outputs.issue_key }}
transition: "In Progress"
env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
Best Practices
- Use Branch Naming: Include JIRA key (feature/PROJ-1234-description)
- Link Commits: Reference JIRA in commit messages
- Automate Status: Update on PR merge/deploy
- Consistent Labels: Use standard labels (performance, bug, enhancement)
- Track Time: Log work time on issues
- Sprint Planning: Move issues to sprint during planning
- Comment Updates: Add progress comments regularly
- Acceptance Criteria: Define clear completion criteria
- Link Related Issues: Use "blocks", "relates to"
- Close on Deploy: Auto-close when deployed to production
Project Configuration
Issue Types
- Epic: Large features
- Story: User stories
- Task: Development tasks
- Bug: Defects
- Improvement: Enhancements
Workflow
To Do → In Progress → Code Review → QA → Done
Custom Fields (Examples)
- Story Points: Effort estimation
- Sprint: Current sprint
- Environment: Dev/Staging/Production
- Component: Frontend/Backend/Database
Requirements
# Python JIRA library
pip install jira
# Environment variables
export JIRA_SERVER=https://siae.atlassian.net
export JIRA_EMAIL=your-email@siae.it
export JIRA_API_TOKEN=your-token
# GitHub CLI (for Actions)
brew install gh
Metrics to Track
- Sprint velocity: Story points completed per sprint
- Cycle time: Days from "To Do" to "Done"
- Lead time: Days from created to done
- Bug rate: Bugs per sprint
- Completion rate: % of sprint goals achieved
- WIP limit: Issues in "In Progress"