-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #652 from softlayer/jiraAuto
Jira auto / 20240301 release notes
- Loading branch information
Showing
2 changed files
with
168 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
#!python | ||
|
||
import click | ||
import os | ||
import logging | ||
import requests | ||
import json | ||
import re | ||
from datetime import date | ||
from pprint import pprint as pp | ||
# from http.client import HTTPConnection | ||
from rich.console import Console | ||
import concurrent.futures as cf | ||
|
||
|
||
|
||
class JiraAPI(): | ||
def __init__(self, apitoken, debug=False): | ||
self.apitoken = apitoken | ||
self.jira_url = 'https://jira.softlayer.local/rest/api/2' | ||
self.headers = {"Authorization": f"Bearer {self.apitoken}", "Content-Type": "application/json"} | ||
self.console = Console(highlight=None) | ||
self.releases = [] | ||
self.internal = re.compile(r"internal", re.I) | ||
self.debug = debug | ||
|
||
def getReleaseJiras(self, days=7): | ||
jql_project = """project = JIRA""" | ||
jql_type = """type = "Production Release" """ | ||
jql_start = f""" "Release Estimated Start Time" >= startOfDay(-{days}d) """ | ||
jql_orderby = """ORDER BY "Release Estimated Start Time" DESC""" | ||
jql_status = """status = Resolved""" | ||
jql = f"{jql_project} AND {jql_type} AND {jql_start} AND {jql_status} {jql_orderby}" | ||
apirequest = f"{self.jira_url}/search?jql={jql}" | ||
self.printDebug(f"Calling {apirequest}") | ||
result = requests.get(apirequest, headers=self.headers, verify=True) | ||
return self.jiraToJson(result) | ||
|
||
def getNotes(self, jira_issue): | ||
self.printDebug(f"Getting JIRA: {jira_issue}") | ||
result = requests.get(jira_issue, headers=self.headers, verify=True) | ||
json = self.jiraToJson(result) | ||
return json.get('fields', {}).get('customfield_10113', 'NONE') | ||
|
||
|
||
def processReleases(self, releases): | ||
r_issues = releases.get('issues', []) | ||
for issue in r_issues: | ||
fileds = issue.get('fields', {}) | ||
this_issue = { | ||
"key": issue.get('key'), | ||
"resolution": fileds.get('resolution', {}).get('name', 'None'), | ||
"summary": fileds.get('summary'), | ||
"issues": [] | ||
} | ||
for sub_issue in fileds.get('issuelinks', []): | ||
released = self.processSubIssue(sub_issue) | ||
this_issue['issues'].append(released) | ||
|
||
self.releases.append(this_issue) | ||
return self.releases | ||
|
||
def processReleasesWithThreads(self, releases): | ||
"""does processReleases, but uses threads. for some reason it seems slower than doing 1 at a time""" | ||
r_issues = releases.get('issues', []) | ||
for issue in r_issues: | ||
fileds = issue.get('fields', {}) | ||
this_issue = { | ||
"key": issue.get('key'), | ||
"resolution": fileds.get('resolution', {}).get('name', 'None'), | ||
"summary": fileds.get('summary'), | ||
"issues": [] | ||
} | ||
with cf.ThreadPoolExecutor(max_workers=10) as executor: | ||
issueLinks = fileds.get('issuelinks', []) | ||
self.printDebug(f"{this_issue.get('key')} has {len(issueLinks)} sub issues") | ||
futures = {executor.submit(self.processSubIssue, sub_issue): sub_issue for sub_issue in issueLinks} | ||
for future in cf.as_completed(futures): | ||
this_issue['issues'].append(future.result()) | ||
|
||
self.releases.append(this_issue) | ||
return self.releases | ||
|
||
def processSubIssue(self, sub_issue): | ||
outward_issue = sub_issue.get('outwardIssue') | ||
released = { | ||
"key": outward_issue.get('key'), | ||
"self": outward_issue.get('self'), | ||
"summary": outward_issue.get('fields').get('summary'), | ||
"notes": self.getNotes(outward_issue.get('self')) | ||
} | ||
return released | ||
|
||
def jiraToJson(self, result): | ||
if result.status_code == 200: | ||
raw_json = result.json() | ||
# self.console.print_json(data=raw_json) | ||
return raw_json | ||
else: | ||
raise Exception(f"ERROR {result.status_code}: {result.text}") | ||
|
||
def printIssues(self, issues): | ||
for issue in issues: | ||
self.console.print(f"[orange3]{issue['key']}, {issue['summary']}, {issue['resolution']}") | ||
for sub_issue in issue.get('issues'): | ||
notes = sub_issue.get('notes') | ||
color = "green" | ||
isInternal = self.internal.search(notes) | ||
if isInternal: | ||
color = "red" | ||
self.console.print(f"\t[{color}]{sub_issue.get('key')}, {notes}, {sub_issue.get('summary')}") | ||
|
||
def printReleaseNotes(self, issues): | ||
today = date.today() | ||
template = f"""--- | ||
title: "Release notes: {today.strftime('%B %d, %Y')}" | ||
date: "{today.isoformat()}" | ||
tags: | ||
- "release notes" | ||
--- | ||
#### API | ||
""" | ||
self.console.print(template, highlight=False) | ||
for issue in issues: | ||
for sub_issue in issue.get('issues'): | ||
notes = sub_issue.get('notes') | ||
isInternal = self.internal.search(notes) | ||
if isInternal is None: | ||
self.console.print(f"- {notes}. {sub_issue.get('summary')} {sub_issue.get('key')}") | ||
|
||
def printDebug(self, message): | ||
if self.debug: | ||
self.console.print(f"[gold3][[DEBUG]] {message}", highlight=False) | ||
|
||
|
||
# Color Chart: https://rich.readthedocs.io/en/stable/appendix/colors.html#appendix-colors | ||
@click.command() | ||
@click.option('--debug', default=False, is_flag=True, help="Show debug level output") | ||
@click.option('--days', default=7, help="How many days in the past to search for issues.") | ||
def cli(debug, days): | ||
"""CLI entrypoint""" | ||
|
||
apitoken = os.getenv('SL_JIRA_TOKEN') | ||
if not apitoken: | ||
raise click.UsageError("APITOKEN env variable is not set") | ||
|
||
j_api = JiraAPI(apitoken, debug) | ||
releases = j_api.getReleaseJiras(days) | ||
parsed = j_api.processReleasesWithThreads(releases) | ||
j_api.printIssues(parsed) | ||
j_api.printReleaseNotes(parsed) | ||
|
||
|
||
|
||
if __name__ == "__main__": | ||
cli() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
--- | ||
title: "Release notes: March 01, 2024" | ||
date: "2024-03-01" | ||
tags: | ||
- "release notes" | ||
--- | ||
|
||
#### API | ||
|
||
- Removed code which checks if guest is powered off before detaching a vlan. | ||
- Added new guest attribute type for skipping PV driver checks. |