This is an automated email from the ASF dual-hosted git repository.
dongjoon-hyun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/spark-connect-swift.git
The following commit(s) were added to refs/heads/main by this push:
new eebe4d8 [SPARK-57009] Add `dev/spark_jira_utils.py`
eebe4d8 is described below
commit eebe4d846a1d32a2c58d0a87ed64bb5e7f4aaca2
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Fri May 22 13:26:06 2026 -0700
[SPARK-57009] Add `dev/spark_jira_utils.py`
### What changes were proposed in this pull request?
Extract JIRA helpers from `dev/create_jira_and_branch.py` into a new
`dev/spark_jira_utils.py`, ported from [`apache/spark`'s
`dev/spark_jira_utils.py`](https://github.com/apache/spark/blob/master/dev/spark_jira_utils.py)
with the version regex adapted to `connect-swift-\d+\.\d+\.\d+`.
### Why are the changes needed?
To remove duplication and give future JIRA-related dev scripts a single
shared entry point, matching the upstream Apache Spark layout.
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
Manual review. I used this newly revised script to prepare this PR via the
following.
```
$ dev/create_jira_and_branch.py -p SPARK-56376 'Add
`dev/spark_jira_utils.py`'
Creating a subtask of SPARK-56376 with title: Add `dev/spark_jira_utils.py`
Created JIRA issue: SPARK-57009
git checkout -b SPARK-57009
Switched to a new branch 'SPARK-57009'
Created and checked out branch: SPARK-57009
['git', 'commit', '-a', '-m', '[SPARK-57009] Add `dev/spark_jira_utils.py`']
Created a commit with message: [SPARK-57009] Add `dev/spark_jira_utils.py`
```
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (claude-opus-4-7)
Closes #375 from dongjoon-hyun/SPARK-57009.
Authored-by: Dongjoon Hyun <[email protected]>
Signed-off-by: Dongjoon Hyun <[email protected]>
---
dev/create_jira_and_branch.py | 70 ++--------------------------
dev/spark_jira_utils.py | 105 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 109 insertions(+), 66 deletions(-)
diff --git a/dev/create_jira_and_branch.py b/dev/create_jira_and_branch.py
index 9ad2b3d..1a28cd3 100755
--- a/dev/create_jira_and_branch.py
+++ b/dev/create_jira_and_branch.py
@@ -17,27 +17,12 @@
# limitations under the License.
#
-import os
-import re
+import argparse
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)
+from spark_jira_utils import create_jira_issue, fail, get_jira_client
def run_cmd(cmd):
@@ -48,48 +33,6 @@ def run_cmd(cmd):
return subprocess.check_output(cmd.split(" ")).decode("utf-8")
-import argparse
-
-def create_jira_issue(title, parent_jira_id=None, issue_type=None,
version=None):
- asf_jira = jira.client.JIRA(
- {"server": JIRA_API_BASE},
- token_auth=JIRA_ACCESS_TOKEN,
- timeout=(3.05, 60)
- )
-
- if version:
- affected_version = version
- else:
- versions = asf_jira.project_versions("SPARK")
- # Consider only connect-swift-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"connect-swift-\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': '',
- 'components': [{'name': 'Connect'}],
- 'versions': [{'name': affected_version}],
- }
-
- 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)
@@ -107,12 +50,6 @@ def create_commit(jira_id, title):
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", help="Title of the JIRA issue")
parser.add_argument("-p", "--parent", help="Parent JIRA ID for subtasks")
@@ -125,7 +62,8 @@ def main():
else:
print("Creating JIRA issue with title: %s" % args.title)
- jira_id = create_jira_issue(args.title, args.parent, args.type,
args.version)
+ asf_jira = get_jira_client()
+ jira_id = create_jira_issue(asf_jira, args.title, "Connect", args.parent,
args.type, args.version)
print("Created JIRA issue: %s" % jira_id)
create_and_checkout_branch(jira_id)
diff --git a/dev/spark_jira_utils.py b/dev/spark_jira_utils.py
new file mode 100644
index 0000000..4dcdea2
--- /dev/null
+++ b/dev/spark_jira_utils.py
@@ -0,0 +1,105 @@
+#
+# 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 os
+import re
+import sys
+
+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 get_jira_client():
+ """Create and return a JIRA client, or exit with a helpful message."""
+ 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 jira.client.JIRA(
+ {"server": JIRA_API_BASE}, token_auth=JIRA_ACCESS_TOKEN,
timeout=(3.05, 30)
+ )
+
+
+def detect_affected_version(asf_jira):
+ """Return the latest unreleased connect-swift-x.y.z version, or exit."""
+ versions = asf_jira.project_versions("SPARK")
+ versions = [
+ x
+ for x in versions
+ if not x.raw["released"]
+ and not x.raw["archived"]
+ and re.match(r"connect-swift-\d+\.\d+\.\d+", x.name)
+ ]
+ versions = sorted(versions, key=lambda x: x.name, reverse=True)
+ if not versions:
+ fail(
+ "Cannot detect affected version. "
+ "Please create the ticket manually at %s" % JIRA_API_BASE
+ )
+ return versions[0].name
+
+
+def list_components(asf_jira):
+ """Print all non-archived Spark JIRA components."""
+ 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 create_jira_issue(asf_jira, title, component, parent=None,
issue_type=None, version=None):
+ """Create a JIRA issue and return the issue key (e.g. SPARK-12345)."""
+ affected_version = version if version else
detect_affected_version(asf_jira)
+
+ issue_dict = {
+ "project": {"key": "SPARK"},
+ "summary": title,
+ "description": "",
+ "versions": [{"name": affected_version}],
+ "components": [{"name": component}],
+ }
+
+ if parent:
+ issue_dict["issuetype"] = {"name": "Sub-task"}
+ issue_dict["parent"] = {"key": parent}
+ 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)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]