|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | + |
| 4 | +""" |
| 5 | +This script finds SQS and SC integration tickets linked to a given release ticket, |
| 6 | +optionally updates the SQS ticket's fix versions, and returns both keys. |
| 7 | +""" |
| 8 | + |
| 9 | +import argparse |
| 10 | +import os |
| 11 | +import sys |
| 12 | +from jira import JIRA |
| 13 | +from jira.exceptions import JIRAError |
| 14 | + |
| 15 | +JIRA_SANDBOX_URL = "https://sonarsource-sandbox-608.atlassian.net/" |
| 16 | +JIRA_PROD_URL = "https://sonarsource.atlassian.net/" |
| 17 | + |
| 18 | + |
| 19 | +def eprint(*args, **kwargs): |
| 20 | + """Prints messages to the standard error stream.""" |
| 21 | + print(*args, file=sys.stderr, **kwargs) |
| 22 | + |
| 23 | + |
| 24 | +# noinspection DuplicatedCode |
| 25 | +def get_jira_instance(use_sandbox=False): |
| 26 | + """Initializes and returns a JIRA client instance.""" |
| 27 | + jira_user = os.environ.get('JIRA_USER') |
| 28 | + jira_token = os.environ.get('JIRA_TOKEN') |
| 29 | + |
| 30 | + if not jira_user or not jira_token: |
| 31 | + eprint("Error: JIRA_USER and JIRA_TOKEN environment variables must be set.") |
| 32 | + sys.exit(1) |
| 33 | + |
| 34 | + jira_url = JIRA_SANDBOX_URL if use_sandbox else JIRA_PROD_URL |
| 35 | + eprint(f"Connecting to JIRA server at: {jira_url}") |
| 36 | + |
| 37 | + try: |
| 38 | + jira_client = JIRA(jira_url, basic_auth=(jira_user, jira_token)) |
| 39 | + return jira_client |
| 40 | + except JIRAError as e: |
| 41 | + eprint(f"Error: JIRA authentication failed. Status: {e.status_code}, Response: {e.text}") |
| 42 | + sys.exit(1) |
| 43 | + |
| 44 | + |
| 45 | +def find_linked_ticket(release_issue, target_project_key): |
| 46 | + """Finds a linked ticket from a specific project.""" |
| 47 | + eprint(f"Searching for linked ticket in project '{target_project_key}'...") |
| 48 | + |
| 49 | + linked_tickets = [] |
| 50 | + for link in release_issue.fields.issuelinks: |
| 51 | + linked_issue = getattr(link, 'outwardIssue', getattr(link, 'inwardIssue', None)) |
| 52 | + if linked_issue and linked_issue.key.startswith(f"{target_project_key}-"): |
| 53 | + linked_tickets.append(linked_issue.key) |
| 54 | + |
| 55 | + if len(linked_tickets) == 0: |
| 56 | + eprint(f"Error: No linked ticket found in project '{target_project_key}' for ticket '{release_issue.key}'.") |
| 57 | + sys.exit(1) |
| 58 | + |
| 59 | + if len(linked_tickets) > 1: |
| 60 | + eprint( |
| 61 | + f"Error: Found multiple linked tickets in project '{target_project_key}': {', '.join(linked_tickets)}. Please ensure only one is linked.") |
| 62 | + sys.exit(1) |
| 63 | + |
| 64 | + found_key = linked_tickets[0] |
| 65 | + eprint(f"✅ Found linked ticket: {found_key}") |
| 66 | + return found_key |
| 67 | + |
| 68 | + |
| 69 | +def update_sqs_fix_versions(jira, ticket_key, fix_versions_str): |
| 70 | + """Attempts to update the fixVersions of the SQS ticket.""" |
| 71 | + if not fix_versions_str: |
| 72 | + return |
| 73 | + |
| 74 | + eprint(f"Attempting to set fix versions for SQS ticket {ticket_key} to: '{fix_versions_str}'") |
| 75 | + versions_list = [{'name': v.strip()} for v in fix_versions_str.split(',')] |
| 76 | + |
| 77 | + try: |
| 78 | + issue = jira.issue(ticket_key) |
| 79 | + issue.update(fields={'fixVersions': versions_list}) |
| 80 | + eprint("✅ Successfully updated fix versions for SQS ticket.") |
| 81 | + except JIRAError as e: |
| 82 | + eprint( |
| 83 | + f"##[warning]Could not update fix versions for {ticket_key}. Jira API failed with status {e.status_code}: {e.text}") |
| 84 | + |
| 85 | + |
| 86 | +def main(): |
| 87 | + parser = argparse.ArgumentParser(description="Finds linked Jira tickets and optionally updates one.") |
| 88 | + parser.add_argument("--release-ticket-key", required=True, help="The key of the release ticket.") |
| 89 | + parser.add_argument("--sqs-project-key", default="SONAR", help="The project key for the SQS ticket.") |
| 90 | + parser.add_argument("--sc-project-key", default="SC", help="The project key for the SC ticket.") |
| 91 | + parser.add_argument("--sqs-fix-versions", help="Comma-separated list of fix versions for the SQS ticket.") |
| 92 | + parser.add_argument('--use-sandbox', action='store_true', help="Use the sandbox Jira server.") |
| 93 | + |
| 94 | + args = parser.parse_args() |
| 95 | + |
| 96 | + jira = get_jira_instance(args.use_sandbox) |
| 97 | + |
| 98 | + try: |
| 99 | + eprint(f"Fetching release ticket '{args.release_ticket_key}' to find linked issues...") |
| 100 | + release_issue = jira.issue(args.release_ticket_key, fields='issuelinks') |
| 101 | + except JIRAError as e: |
| 102 | + eprint( |
| 103 | + f"Error: Could not retrieve issue '{args.release_ticket_key}'. Status: {e.status_code}, Response: {e.text}") |
| 104 | + sys.exit(1) |
| 105 | + |
| 106 | + sqs_ticket_key = find_linked_ticket(release_issue, args.sqs_project_key) |
| 107 | + sc_ticket_key = find_linked_ticket(release_issue, args.sc_project_key) |
| 108 | + |
| 109 | + update_sqs_fix_versions(jira, sqs_ticket_key, args.sqs_fix_versions) |
| 110 | + |
| 111 | + # Output for the GitHub Action |
| 112 | + print(f"sqs_ticket_key={sqs_ticket_key}") |
| 113 | + print(f"sc_ticket_key={sc_ticket_key}") |
| 114 | + |
| 115 | + |
| 116 | +if __name__ == "__main__": |
| 117 | + main() |
0 commit comments