This is an automated email from the ASF dual-hosted git repository.

dongjoon pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/master by this push:
     new 3ca6c0ffb686 [SPARK-55697][INFRA] Add `create_spark_jira.py` script
3ca6c0ffb686 is described below

commit 3ca6c0ffb686704091d34f8e0ce8d73171fb3a82
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Wed Feb 25 18:17:25 2026 -0800

    [SPARK-55697][INFRA] Add `create_spark_jira.py` script
    
    ### What changes were proposed in this pull request?
    
    This PR aims to add `create_spark_jira.py` script in addition to 
`merge_spark_pr.py`.
    
    ### Why are the changes needed?
    
    To help the developers via
    1. Create ASF JIRA Issue ID.
    2. Create a branch with the generated JIRA Issue ID.
    3. Help to choose a proper component for the PR (if not given via `-c` CLI 
argument)
    4. Create a commit with the Spark community style commit title at least: 
`[$JIRA_ID] JIRA_ISSUE_TITLE`.
    
    Currently, this script has been used in the `Apache Spark` sub-projects. 
It's greatly helpful.
    - 
https://github.com/apache/spark-kubernetes-operator/blob/main/dev/create_spark_jira.py
    - 
https://github.com/apache/spark-connect-swift/blob/main/dev/create_spark_jira.py
    
    ### How was this patch tested?
    
    Manually. I used this script to prepare this PR.
    
    ```
    $ dev/create_spark_jira.py -p SPARK-54137 'Add `create_spark_jira.py` 
script'
    1. Block Manager
    2. Build
    3. Connect
    4. Declarative Pipelines
    5. Deploy
    6. Documentation
    7. DStreams
    8. Examples
    9. Input/Output
    10. Java API
    11. Kubernetes
    12. ML
    13. MLlib
    14. Optimizer
    15. Pandas API on Spark
    16. Project Infra
    17. Protobuf
    18. PySpark
    19. Scheduler
    20. Security
    21. Shuffle
    22. Spark Core
    23. Spark Docker
    24. Spark Shell
    25. Spark Submit
    26. SQL
    27. Structured Streaming
    28. Tests
    29. Web UI
    30. Windows
    31. YARN
    Please choose a component by number: 16
    Creating a subtask of SPARK-54137 with title: Add `create_spark_jira.py` 
script
    Created JIRA issue: SPARK-55697
    git checkout -b SPARK-55697
    Switched to a new branch 'SPARK-55697'
    Created and checked out branch: SPARK-55697
    ['git', 'commit', '-a', '-m', '[SPARK-55697] Add `create_spark_jira.py` 
script']
    Created a commit with message: [SPARK-55697] Add `create_spark_jira.py` 
script
    ```
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: `Gemini 3.1 Pro (High)` on `Antigravity`
    
    Closes #54495 from dongjoon-hyun/SPARK-55697.
    
    Authored-by: Dongjoon Hyun <[email protected]>
    Signed-off-by: Dongjoon Hyun <[email protected]>
---
 dev/create_spark_jira.py | 180 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 180 insertions(+)

diff --git a/dev/create_spark_jira.py b/dev/create_spark_jira.py
new file mode 100755
index 000000000000..9259bf3adc81
--- /dev/null
+++ b/dev/create_spark_jira.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python3
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import argparse
+import os
+import re
+import subprocess
+import sys
+import traceback
+
+try:
+    import jira.client
+
+    JIRA_IMPORTED = True
+except ImportError:
+    JIRA_IMPORTED = False
+
+# ASF JIRA access token
+JIRA_ACCESS_TOKEN = os.environ.get("JIRA_ACCESS_TOKEN")
+JIRA_API_BASE = "https://issues.apache.org/jira";
+
+
+def fail(msg):
+    print(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 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)
+    )
+
+    if version:
+        affected_version = version
+    else:
+        versions = asf_jira.project_versions("SPARK")
+        # Consider only x.y.z, unreleased, unarchived versions
+        versions = [
+            x
+            for x in versions
+            if not x.raw["released"]
+            and not x.raw["archived"]
+            and re.match(r"\d+\.\d+\.\d+", x.name)
+        ]
+        versions = sorted(versions, key=lambda x: x.name, reverse=True)
+        affected_version = versions[0].name
+
+    issue_dict = {
+        "project": {"key": "SPARK"},
+        "summary": title,
+        "description": "",
+        "versions": [{"name": affected_version}],
+    }
+
+    if component:
+        issue_dict["components"] = [{"name": component}]
+
+    if parent_jira_id:
+        issue_dict["issuetype"] = {"name": "Sub-task"}
+        issue_dict["parent"] = {"key": parent_jira_id}
+    else:
+        issue_dict["issuetype"] = {"name": issue_type if issue_type else 
"Improvement"}
+
+    try:
+        new_issue = asf_jira.create_issue(fields=issue_dict)
+        return 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()
+    except Exception:
+        traceback.print_exc()
+        sys.exit(-1)


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to