请求协助:实现分支合并/禁用时关闭关联JIRA工单的Python脚本
Got it, let's break down how to build this solution step by step. We'll create two core components: one that closes linked JIRA tickets when a branch is merged into master, and another that checks for disabled branches during the pre-receive hook and closes their associated tickets.
First, let's set up the dependencies and core JIRA integration.
Prerequisites
- Install the JIRA Python library to handle API interactions:
pip install jira - You'll need a JIRA API token (generate this from your JIRA account settings), your JIRA email, and your team's JIRA server URL.
Step 1: Core JIRA Utility Functions
We'll start with a reusable module to handle JIRA connections and ticket closures. This keeps our code clean and easy to reuse across hooks.
from jira import JIRA from jira.exceptions import JIRAError import re # JIRA Configuration - replace these with your team's details JIRA_SERVER = "https://your-jira-instance.atlassian.net" JIRA_EMAIL = "your-work-email@company.com" JIRA_API_TOKEN = "your-generated-api-token" def get_jira_connection(): """Establish and return a valid JIRA connection.""" try: return JIRA( server=JIRA_SERVER, basic_auth=(JIRA_EMAIL, JIRA_API_TOKEN) ) except JIRAError as e: print(f"Failed to connect to JIRA: {e.response.text}") raise def close_jira_ticket(ticket_key): """Close a JIRA ticket by transitioning it to your team's 'Done' status (adjust as needed).""" jira = get_jira_connection() try: ticket = jira.issue(ticket_key) # Find the correct transition ID for closing tickets (match your workflow status) transitions = jira.transitions(ticket) close_transition = next(t for t in transitions if t['name'] == 'Done') jira.transition_issue( ticket, close_transition['id'], comment="Ticket closed automatically: Branch merged to master or marked as disabled." ) print(f"Successfully closed JIRA ticket {ticket_key}") except JIRAError as e: print(f"Failed to close ticket {ticket_key}: {e}") except StopIteration: print(f"No 'Done' transition found for {ticket_key} - check your JIRA workflow settings.") def extract_jira_keys(text): """Pull JIRA ticket keys (e.g., PROJ-123) from branch names or commit messages.""" pattern = r'([A-Z]{2,}-\d+)' # Adjust regex to match your team's ticket format return re.findall(pattern, text)
Step 2: Pre-Receive Hook for Disabled Branches
The pre-receive hook runs on the Git server before accepting any pushes. We'll use this to check if the pushed branch is disabled, then close its linked JIRA tickets.
Create a file named pre-receive in your Git repo's .git/hooks directory, and add this code:
#!/usr/bin/env python3 import sys from jira_utils import close_jira_ticket, extract_jira_keys # Define your team's disabled branches (or load from a config file for scalability) DISABLED_BRANCHES = ["feature/legacy-module", "bugfix/deprecated-api"] def handle_pre_receive(): """Process incoming pushes and close tickets for disabled branches.""" # Git passes push details via stdin: each line is <old-ref> <new-ref> <branch-name> for line in sys.stdin: old_ref, new_ref, branch_name = line.strip().split() # Check if the branch is in our disabled list if branch_name in DISABLED_BRANCHES: print(f"Detected push to disabled branch: {branch_name}") # Extract JIRA keys from the branch name jira_keys = extract_jira_keys(branch_name) if jira_keys: for key in jira_keys: close_jira_ticket(key) else: print(f"No JIRA keys found in branch name {branch_name}") # Optional: Reject the push to prevent changes to disabled branches # print(f"Push rejected: {branch_name} is marked as disabled") # sys.exit(1) if __name__ == "__main__": handle_pre_receive()
Don't forget to make the script executable:
chmod +x .git/hooks/pre-receive
Step 3: Close Tickets When Merging to Master
For this, we'll use a post-receive hook (runs after the push is accepted) to detect when master is updated, extract JIRA keys from commit messages, and close those tickets.
Create .git/hooks/post-receive with this code:
#!/usr/bin/env python3 import sys import subprocess from jira_utils import close_jira_ticket, extract_jira_keys def handle_master_merge(): """Check for master branch updates and close linked JIRA tickets.""" # Read push details from stdin for line in sys.stdin: old_ref, new_ref, branch_name = line.strip().split() # Only process pushes to the master branch if branch_name != "refs/heads/master": continue # Fetch all commit messages between the old and new master ref commit_range = f"{old_ref}..{new_ref}" try: result = subprocess.run( ["git", "log", "--oneline", commit_range], capture_output=True, text=True, check=True ) commit_messages = result.stdout.splitlines() # Collect all unique JIRA keys from commit messages all_jira_keys = set() for msg in commit_messages: keys = extract_jira_keys(msg) all_jira_keys.update(keys) # Close each linked ticket if all_jira_keys: print(f"Closing JIRA tickets linked to master merge: {', '.join(all_jira_keys)}") for key in all_jira_keys: close_jira_ticket(key) else: print("No JIRA keys found in commit messages for this master merge.") except subprocess.CalledProcessError as e: print(f"Failed to fetch commit logs: {e}") if __name__ == "__main__": handle_master_merge()
Make this script executable too:
chmod +x .git/hooks/post-receive
Customization Tips
- JIRA Workflow: Update the transition name (
'Done') inclose_jira_ticketto match your team's actual ticket-closing status (e.g., 'Resolved', 'Closed'). - Disabled Branches: Replace the hardcoded
DISABLED_BRANCHESlist with a config file or database lookup for easier management. - Security: Store JIRA credentials in environment variables instead of hardcoding them for production environments.
- Branch/Commit Format: Adjust the regex in
extract_jira_keysif your team uses a different ticket naming pattern.
内容的提问来源于stack exchange,提问作者Pavan

