blob: 9259bf3adc8193105b0b23b1140f05e9721de131 [file] [log] [blame]
#!/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)