From 1b37e89c475079cbbfb62fb2c98ef30c4c61004d Mon Sep 17 00:00:00 2001 From: Wenchen Fan Date: Thu, 9 Apr 2026 14:15:48 +0000 Subject: [PATCH 1/9] [SPARK-56415][INFRA] Simplify create_spark_jira.py for LLM-driven JIRA ticket creation Co-authored-by: Isaac --- AGENTS.md | 12 ++- dev/create_spark_jira.py | 161 +++++++++++++++------------------------ 2 files changed, 72 insertions(+), 101 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 62732904dd3a9..7d981e3e979b1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,7 +92,17 @@ Each annotation contains the test class, test name, and failure message. ## Pull Request Workflow -PR title requires a JIRA ticket ID (e.g., `[SPARK-xxxx][SQL] Title`). Ask the user to create a new ticket or provide an existing one if not given. Before writing the PR description, read `.github/PULL_REQUEST_TEMPLATE` and fill in every section from that file. +PR title requires a JIRA ticket ID (e.g., `[SPARK-xxxx][SQL] Title`). If no ticket ID is given, create one using `dev/create_spark_jira.py`: + + python3 dev/create_spark_jira.py "Title" -c -t + +Infer the arguments from the changes: + +- **Title**: a concise summary of the change (without the JIRA ID or component tag). +- **Issue type** (`-t`): "Bug", "Improvement", "New Feature", "Test", "Documentation", or "Dependency upgrade". +- **Component** (`-c`): e.g. "SQL", "Spark Core", "PySpark", "Connect". Run `python3 dev/create_spark_jira.py --list-components` for the full list. + +Before writing the PR description, read `.github/PULL_REQUEST_TEMPLATE` and fill in every section from that file. DO NOT push to the upstream repo. Always push to the personal fork. Open PRs against `master` on the upstream repo. diff --git a/dev/create_spark_jira.py b/dev/create_spark_jira.py index 9259bf3adc819..11c13cd80fc73 100755 --- a/dev/create_spark_jira.py +++ b/dev/create_spark_jira.py @@ -20,7 +20,6 @@ import argparse import os import re -import subprocess import sys import traceback @@ -41,21 +40,63 @@ def fail(msg): sys.exit(-1) -def run_cmd(cmd): - print(cmd) - if isinstance(cmd, list): - return subprocess.check_output(cmd).decode("utf-8") - else: - return subprocess.check_output(cmd.split(" ")).decode("utf-8") +def get_jira_client(): + return jira.client.JIRA( + {"server": JIRA_API_BASE}, token_auth=JIRA_ACCESS_TOKEN, timeout=(3.05, 30) + ) -def create_jira_issue(title, parent_jira_id=None, issue_type=None, version=None, component=None): - asf_jira = jira.client.JIRA( - {"server": JIRA_API_BASE}, token_auth=JIRA_ACCESS_TOKEN, timeout=(3.05, 30) +def list_components(): + asf_jira = get_jira_client() + components = asf_jira.project_components("SPARK") + components = [c for c in components if not c.raw.get("archived", False)] + for c in sorted(components, key=lambda x: x.name): + print(c.name) + + +def main(): + parser = argparse.ArgumentParser(description="Create a Spark JIRA issue.") + parser.add_argument("title", nargs="?", help="Title of the JIRA issue") + parser.add_argument("-p", "--parent", help="Parent JIRA ID for subtasks") + parser.add_argument( + "-t", + "--type", + help="Issue type (e.g. Bug, Improvement). Defaults to Improvement.", + ) + parser.add_argument("-v", "--version", help="Version to use for the issue") + parser.add_argument("-c", "--component", help="Component for the issue") + parser.add_argument( + "--list-components", action="store_true", help="List available components and exit" ) + args = parser.parse_args() + + def check_jira_access(): + if not JIRA_IMPORTED or not JIRA_ACCESS_TOKEN: + msg = "Cannot create JIRA ticket automatically" + if not JIRA_IMPORTED: + msg += " (jira-python library not installed, run 'pip install jira')" + else: + msg += " (JIRA_ACCESS_TOKEN env-var not set)" + msg += ". Please create the ticket manually at %s" % JIRA_API_BASE + fail(msg) + + if args.list_components: + check_jira_access() + list_components() + return + + if not args.title: + parser.error("the following arguments are required: title") + + if not args.component: + parser.error("the following arguments are required: -c/--component") + + check_jira_access() + + asf_jira = get_jira_client() - if version: - affected_version = version + if args.version: + affected_version = args.version else: versions = asf_jira.project_versions("SPARK") # Consider only x.y.z, unreleased, unarchived versions @@ -67,111 +108,31 @@ def create_jira_issue(title, parent_jira_id=None, issue_type=None, version=None, and re.match(r"\d+\.\d+\.\d+", x.name) ] versions = sorted(versions, key=lambda x: x.name, reverse=True) + if not versions: + fail("No unreleased versions found for SPARK project.") affected_version = versions[0].name issue_dict = { "project": {"key": "SPARK"}, - "summary": title, + "summary": args.title, "description": "", "versions": [{"name": affected_version}], + "components": [{"name": args.component}], } - if component: - issue_dict["components"] = [{"name": component}] - - if parent_jira_id: + if args.parent: issue_dict["issuetype"] = {"name": "Sub-task"} - issue_dict["parent"] = {"key": parent_jira_id} + issue_dict["parent"] = {"key": args.parent} else: - issue_dict["issuetype"] = {"name": issue_type if issue_type else "Improvement"} + issue_dict["issuetype"] = {"name": args.type if args.type else "Improvement"} try: new_issue = asf_jira.create_issue(fields=issue_dict) - return new_issue.key + print(new_issue.key) except Exception as e: fail("Failed to create JIRA issue: %s" % e) -def create_and_checkout_branch(jira_id): - try: - run_cmd("git checkout -b %s" % jira_id) - print("Created and checked out branch: %s" % jira_id) - except subprocess.CalledProcessError as e: - fail("Failed to create branch %s: %s" % (jira_id, e)) - - -def create_commit(jira_id, title): - try: - run_cmd(["git", "commit", "-a", "-m", "[%s] %s" % (jira_id, title)]) - print("Created a commit with message: [%s] %s" % (jira_id, title)) - except subprocess.CalledProcessError as e: - fail("Failed to create commit: %s" % e) - - -def choose_components(): - asf_jira = jira.client.JIRA( - {"server": JIRA_API_BASE}, token_auth=JIRA_ACCESS_TOKEN, timeout=(3.05, 30) - ) - components = asf_jira.project_components("SPARK") - components = [c for c in components if not c.raw.get("archived", False)] - for i, c in enumerate(components): - print("%d. %s" % (i + 1, c.name)) - - while True: - try: - choice = input("Please choose a component by number: ") - idx = int(choice) - 1 - if 0 <= idx < len(components): - return components[idx].name - else: - print("Invalid number. Please try again.") - except ValueError: - print("Invalid input. Please enter a number.") - - -def main(): - if not JIRA_IMPORTED: - fail("Could not find jira-python library. Run 'sudo pip3 install jira' to install.") - - if not JIRA_ACCESS_TOKEN: - fail("The env-var JIRA_ACCESS_TOKEN is not set.") - - parser = argparse.ArgumentParser(description="Create a Spark JIRA issue.") - parser.add_argument("title", nargs="?", help="Title of the JIRA issue") - parser.add_argument("-p", "--parent", help="Parent JIRA ID for subtasks") - parser.add_argument( - "-t", - "--type", - help="Issue type to create when no parent is specified (e.g. Bug). Defaults to Improvement.", - ) - parser.add_argument("-v", "--version", help="Version to use for the issue") - parser.add_argument("-c", "--component", help="Component for the issue") - args = parser.parse_args() - - if args.parent: - asf_jira = jira.client.JIRA( - {"server": JIRA_API_BASE}, token_auth=JIRA_ACCESS_TOKEN, timeout=(3.05, 30) - ) - parent_issue = asf_jira.issue(args.parent) - print("Parent issue title: %s" % parent_issue.fields.summary) - print("Creating a subtask of %s with title: %s" % (args.parent, args.title)) - else: - print("Creating JIRA issue with title: %s" % args.title) - - if not args.title: - parser.error("the following arguments are required: title") - - if not args.component: - args.component = choose_components() - - jira_id = create_jira_issue(args.title, args.parent, args.type, args.version, args.component) - print("Created JIRA issue: %s" % jira_id) - - create_and_checkout_branch(jira_id) - - create_commit(jira_id, args.title) - - if __name__ == "__main__": try: main() From b0097221698040046833f3334e9153702d6390cd Mon Sep 17 00:00:00 2001 From: Wenchen Fan Date: Thu, 9 Apr 2026 15:11:19 +0000 Subject: [PATCH 2/9] [SPARK-56415][INFRA] Refine version handling and pre-flight checks - Remove CLI version flag; auto-detect latest unreleased version instead - Split preflight into check_jira_access() and detect_affected_version() - Hint in AGENTS.md to review versions after ticket creation Co-authored-by: Isaac --- AGENTS.md | 2 ++ dev/create_spark_jira.py | 63 +++++++++++++++++++--------------------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7d981e3e979b1..8dec73eb7ac64 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -102,6 +102,8 @@ Infer the arguments from the changes: - **Issue type** (`-t`): "Bug", "Improvement", "New Feature", "Test", "Documentation", or "Dependency upgrade". - **Component** (`-c`): e.g. "SQL", "Spark Core", "PySpark", "Connect". Run `python3 dev/create_spark_jira.py --list-components` for the full list. +The script sets the latest unreleased version as the default affected version. Ask the user to review and adjust versions on the JIRA ticket after creation. + Before writing the PR description, read `.github/PULL_REQUEST_TEMPLATE` and fill in every section from that file. DO NOT push to the upstream repo. Always push to the personal fork. Open PRs against `master` on the upstream repo. diff --git a/dev/create_spark_jira.py b/dev/create_spark_jira.py index 11c13cd80fc73..2f12fc0fce154 100755 --- a/dev/create_spark_jira.py +++ b/dev/create_spark_jira.py @@ -46,8 +46,7 @@ def get_jira_client(): ) -def list_components(): - asf_jira = get_jira_client() +def list_components(asf_jira): components = asf_jira.project_components("SPARK") components = [c for c in components if not c.raw.get("archived", False)] for c in sorted(components, key=lambda x: x.name): @@ -63,7 +62,6 @@ def main(): "--type", help="Issue type (e.g. Bug, Improvement). Defaults to Improvement.", ) - parser.add_argument("-v", "--version", help="Version to use for the issue") parser.add_argument("-c", "--component", help="Component for the issue") parser.add_argument( "--list-components", action="store_true", help="List available components and exit" @@ -71,35 +69,19 @@ def main(): args = parser.parse_args() def check_jira_access(): - if not JIRA_IMPORTED or not JIRA_ACCESS_TOKEN: - msg = "Cannot create JIRA ticket automatically" - if not JIRA_IMPORTED: - msg += " (jira-python library not installed, run 'pip install jira')" - else: - msg += " (JIRA_ACCESS_TOKEN env-var not set)" - msg += ". Please create the ticket manually at %s" % JIRA_API_BASE - fail(msg) - - if args.list_components: - check_jira_access() - list_components() - return - - if not args.title: - parser.error("the following arguments are required: title") - - if not args.component: - parser.error("the following arguments are required: -c/--component") - - check_jira_access() - - asf_jira = get_jira_client() - - if args.version: - affected_version = args.version - else: + errors = [] + if not JIRA_IMPORTED: + errors.append("jira-python library not installed, run 'pip install jira'") + if not JIRA_ACCESS_TOKEN: + errors.append("JIRA_ACCESS_TOKEN env-var not set") + if errors: + fail("Cannot create JIRA ticket automatically (%s). " + "Please create the ticket manually at %s" + % ("; ".join(errors), JIRA_API_BASE)) + return get_jira_client() + + def detect_affected_version(asf_jira): versions = asf_jira.project_versions("SPARK") - # Consider only x.y.z, unreleased, unarchived versions versions = [ x for x in versions @@ -109,8 +91,23 @@ def check_jira_access(): ] versions = sorted(versions, key=lambda x: x.name, reverse=True) if not versions: - fail("No unreleased versions found for SPARK project.") - affected_version = versions[0].name + fail("Cannot detect affected version. " + "Please create the ticket manually at %s" % JIRA_API_BASE) + return versions[0].name + + if args.list_components: + asf_jira = check_jira_access() + list_components(asf_jira) + return + + if not args.title: + parser.error("the following arguments are required: title") + + if not args.component: + parser.error("the following arguments are required: -c/--component") + + asf_jira = check_jira_access() + affected_version = detect_affected_version(asf_jira) issue_dict = { "project": {"key": "SPARK"}, From f6e589e77f48788625f56146718191c800b50d69 Mon Sep 17 00:00:00 2001 From: Wenchen Fan Date: Thu, 9 Apr 2026 15:13:29 +0000 Subject: [PATCH 3/9] [SPARK-56415][INFRA] Remove --parent flag and simplify to top-level issues only Co-authored-by: Isaac --- AGENTS.md | 2 +- dev/create_spark_jira.py | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8dec73eb7ac64..a1e8c20a5c577 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -102,7 +102,7 @@ Infer the arguments from the changes: - **Issue type** (`-t`): "Bug", "Improvement", "New Feature", "Test", "Documentation", or "Dependency upgrade". - **Component** (`-c`): e.g. "SQL", "Spark Core", "PySpark", "Connect". Run `python3 dev/create_spark_jira.py --list-components` for the full list. -The script sets the latest unreleased version as the default affected version. Ask the user to review and adjust versions on the JIRA ticket after creation. +The script sets the latest unreleased version as the default affected version. Ask the user to review and adjust versions, subtask relationships, and other fields on the JIRA ticket after creation. Before writing the PR description, read `.github/PULL_REQUEST_TEMPLATE` and fill in every section from that file. diff --git a/dev/create_spark_jira.py b/dev/create_spark_jira.py index 2f12fc0fce154..252b11f4ea470 100755 --- a/dev/create_spark_jira.py +++ b/dev/create_spark_jira.py @@ -56,7 +56,6 @@ def list_components(asf_jira): def main(): parser = argparse.ArgumentParser(description="Create a Spark JIRA issue.") parser.add_argument("title", nargs="?", help="Title of the JIRA issue") - parser.add_argument("-p", "--parent", help="Parent JIRA ID for subtasks") parser.add_argument( "-t", "--type", @@ -117,11 +116,7 @@ def detect_affected_version(asf_jira): "components": [{"name": args.component}], } - if args.parent: - issue_dict["issuetype"] = {"name": "Sub-task"} - issue_dict["parent"] = {"key": args.parent} - else: - issue_dict["issuetype"] = {"name": args.type if args.type else "Improvement"} + issue_dict["issuetype"] = {"name": args.type if args.type else "Improvement"} try: new_issue = asf_jira.create_issue(fields=issue_dict) From e58deff3364b6dcaa7de9b4d28447308db73a9b5 Mon Sep 17 00:00:00 2001 From: Wenchen Fan Date: Thu, 9 Apr 2026 15:23:58 +0000 Subject: [PATCH 4/9] [SPARK-56415][INFRA] Revert PR template wording to original Co-authored-by: Isaac --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index a1e8c20a5c577..50ea7a46f3a87 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -104,7 +104,7 @@ Infer the arguments from the changes: The script sets the latest unreleased version as the default affected version. Ask the user to review and adjust versions, subtask relationships, and other fields on the JIRA ticket after creation. -Before writing the PR description, read `.github/PULL_REQUEST_TEMPLATE` and fill in every section from that file. +Follow the template in `.github/PULL_REQUEST_TEMPLATE` for the PR description. DO NOT push to the upstream repo. Always push to the personal fork. Open PRs against `master` on the upstream repo. From 53cfe2195871fea57a8ad07f63c712b48ab9b28d Mon Sep 17 00:00:00 2001 From: Wenchen Fan Date: Thu, 9 Apr 2026 15:26:25 +0000 Subject: [PATCH 5/9] [SPARK-56415][INFRA] Use more explicit PR template instruction Co-authored-by: Isaac --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 50ea7a46f3a87..a1e8c20a5c577 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -104,7 +104,7 @@ Infer the arguments from the changes: The script sets the latest unreleased version as the default affected version. Ask the user to review and adjust versions, subtask relationships, and other fields on the JIRA ticket after creation. -Follow the template in `.github/PULL_REQUEST_TEMPLATE` for the PR description. +Before writing the PR description, read `.github/PULL_REQUEST_TEMPLATE` and fill in every section from that file. DO NOT push to the upstream repo. Always push to the personal fork. Open PRs against `master` on the upstream repo. From 974bd4606efbb8a10e4590691d343c395df17edf Mon Sep 17 00:00:00 2001 From: Wenchen Fan Date: Thu, 9 Apr 2026 15:28:10 +0000 Subject: [PATCH 6/9] [SPARK-56415][INFRA] Use placeholder syntax for title in example command Co-authored-by: Isaac --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index a1e8c20a5c577..ee526adc7a296 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -94,7 +94,7 @@ Each annotation contains the test class, test name, and failure message. PR title requires a JIRA ticket ID (e.g., `[SPARK-xxxx][SQL] Title`). If no ticket ID is given, create one using `dev/create_spark_jira.py`: - python3 dev/create_spark_jira.py "Title" -c -t + python3 dev/create_spark_jira.py "" -c <component> -t <type> Infer the arguments from the changes: From 753c63e17c7a32c3946d1d21abd677787153d6f5 Mon Sep 17 00:00:00 2001 From: Wenchen Fan <wenchen@databricks.com> Date: Thu, 9 Apr 2026 16:31:24 +0000 Subject: [PATCH 7/9] [SPARK-56415][INFRA] Add back --parent flag for creating subtasks Co-authored-by: Isaac --- AGENTS.md | 12 +++++------- dev/create_spark_jira.py | 21 +++++++++++++++++---- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ee526adc7a296..e697bee61722c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,17 +92,15 @@ Each annotation contains the test class, test name, and failure message. ## Pull Request Workflow -PR title requires a JIRA ticket ID (e.g., `[SPARK-xxxx][SQL] Title`). If no ticket ID is given, create one using `dev/create_spark_jira.py`: +PR title requires a JIRA ticket ID (e.g., `[SPARK-xxxx][SQL] Title`). If no ticket ID is given, create one using `dev/create_spark_jira.py`. Infer the title from the changes — a concise summary without the JIRA ID or component tag. - python3 dev/create_spark_jira.py "<title>" -c <component> -t <type> + python3 dev/create_spark_jira.py "<title>" -c <component> { -t <type> | -p <parent-jira-id> } -Infer the arguments from the changes: - -- **Title**: a concise summary of the change (without the JIRA ID or component tag). -- **Issue type** (`-t`): "Bug", "Improvement", "New Feature", "Test", "Documentation", or "Dependency upgrade". - **Component** (`-c`): e.g. "SQL", "Spark Core", "PySpark", "Connect". Run `python3 dev/create_spark_jira.py --list-components` for the full list. +- **Issue type** (`-t`): "Bug", "Improvement", "New Feature", "Test", "Documentation", or "Dependency upgrade". +- **Parent** (`-p`): if the user mentions a parent JIRA ticket (e.g., "this is a subtask of SPARK-12345"), pass it instead of `-t`. The issue type is automatically "Sub-task". -The script sets the latest unreleased version as the default affected version. Ask the user to review and adjust versions, subtask relationships, and other fields on the JIRA ticket after creation. +The script sets the latest unreleased version as the default affected version. Ask the user to review and adjust versions and other fields on the JIRA ticket after creation. Before writing the PR description, read `.github/PULL_REQUEST_TEMPLATE` and fill in every section from that file. diff --git a/dev/create_spark_jira.py b/dev/create_spark_jira.py index 252b11f4ea470..abdaaf862144a 100755 --- a/dev/create_spark_jira.py +++ b/dev/create_spark_jira.py @@ -56,10 +56,15 @@ def list_components(asf_jira): def main(): parser = argparse.ArgumentParser(description="Create a Spark JIRA issue.") parser.add_argument("title", nargs="?", help="Title of the JIRA issue") + parser.add_argument( + "-p", + "--parent", + help="Parent JIRA ID to create a subtask (e.g. SPARK-12345).", + ) parser.add_argument( "-t", "--type", - help="Issue type (e.g. Bug, Improvement). Defaults to Improvement.", + help="Issue type (e.g. Bug, Improvement)", ) parser.add_argument("-c", "--component", help="Component for the issue") parser.add_argument( @@ -103,7 +108,10 @@ def detect_affected_version(asf_jira): parser.error("the following arguments are required: title") if not args.component: - parser.error("the following arguments are required: -c/--component") + parser.error("-c/--component is required") + + if not args.parent and not args.type: + parser.error("-t/--type is required when not creating a subtask") asf_jira = check_jira_access() affected_version = detect_affected_version(asf_jira) @@ -113,10 +121,15 @@ def detect_affected_version(asf_jira): "summary": args.title, "description": "", "versions": [{"name": affected_version}], - "components": [{"name": args.component}], } - issue_dict["issuetype"] = {"name": args.type if args.type else "Improvement"} + issue_dict["components"] = [{"name": args.component}] + + if args.parent: + issue_dict["issuetype"] = {"name": "Sub-task"} + issue_dict["parent"] = {"key": args.parent} + else: + issue_dict["issuetype"] = {"name": args.type} try: new_issue = asf_jira.create_issue(fields=issue_dict) From 0158dadbf7a659f1f9ebefc1e2ae26040d764d25 Mon Sep 17 00:00:00 2001 From: Wenchen Fan <wenchen@databricks.com> Date: Thu, 9 Apr 2026 16:32:27 +0000 Subject: [PATCH 8/9] [SPARK-56415][INFRA] Validate that --parent and --type are mutually exclusive Co-authored-by: Isaac --- dev/create_spark_jira.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/create_spark_jira.py b/dev/create_spark_jira.py index abdaaf862144a..31bdfc599dc66 100755 --- a/dev/create_spark_jira.py +++ b/dev/create_spark_jira.py @@ -110,6 +110,9 @@ def detect_affected_version(asf_jira): if not args.component: parser.error("-c/--component is required") + if args.parent and args.type: + parser.error("--parent and --type cannot be used together") + if not args.parent and not args.type: parser.error("-t/--type is required when not creating a subtask") From 0cc325099107dcb37a0fd2e54d887874d28fb176 Mon Sep 17 00:00:00 2001 From: Wenchen Fan <wenchen@databricks.com> Date: Thu, 9 Apr 2026 16:36:12 +0000 Subject: [PATCH 9/9] [SPARK-56415][INFRA] Simplify JIRA ticket title instruction in CLAUDE.md Co-authored-by: Isaac --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index e697bee61722c..fd20f33248d9e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,7 +92,7 @@ Each annotation contains the test class, test name, and failure message. ## Pull Request Workflow -PR title requires a JIRA ticket ID (e.g., `[SPARK-xxxx][SQL] Title`). If no ticket ID is given, create one using `dev/create_spark_jira.py`. Infer the title from the changes — a concise summary without the JIRA ID or component tag. +PR title format is `[SPARK-xxxx][Component] Title`. Infer the PR title from the changes. If no ticket ID is given, create one using `dev/create_spark_jira.py`, using the PR title (without the JIRA ID and component tag) as the ticket title. python3 dev/create_spark_jira.py "<title>" -c <component> { -t <type> | -p <parent-jira-id> }