Skip to content

Commit a8967a0

Browse files
ci: Sync .github (#3611)
1 parent 8171887 commit a8967a0

File tree

15 files changed

+512
-78
lines changed

15 files changed

+512
-78
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import re
2+
import subprocess
3+
import os
4+
from jira import JIRA
5+
from jira.exceptions import JIRAError
6+
import json
7+
8+
commit_pattern = re.compile(r'^(\w*):\s*(.*?)?:\s*(.*?)\s*(\(#(\d+)\))?$')
9+
10+
PREVIOUS_REF = os.getenv("PREVIOUS_REF").strip('\"')
11+
BASE_BRANCH = os.getenv("BASE_BRANCH", "origin/develop").strip('\"')
12+
13+
JIRA_USERNAME = os.getenv("JIRA_USERNAME").strip('\"') # in email format, e.g [email protected]
14+
JIRA_TOKEN = os.getenv("JIRA_TOKEN").strip('\"') # https://id.atlassian.com/manage-profile/security/api-tokens
15+
JIRA_SERVER = os.getenv("JIRA_SERVER", "https://heartex.atlassian.net").strip('\"')
16+
JIRA_RN_FIELD = os.getenv("JIRA_RN_FIELD", "customfield_10064").strip('\"')
17+
OUTPUT_FILE_MD = os.getenv("OUTPUT_FILE_MD", None)
18+
OUTPUT_FILE_JSON = os.getenv("OUTPUT_FILE_JSON", None)
19+
20+
jira = JIRA(JIRA_SERVER, basic_auth=(JIRA_USERNAME, JIRA_TOKEN))
21+
22+
title_map = {
23+
'feat': 'New Features',
24+
'fix': 'Bug Fixes',
25+
'perf': 'Performance Improvements',
26+
'refactor': 'Code Refactoring',
27+
'docs': 'Documentation'
28+
}
29+
30+
31+
def execute_git_cmd(cmd):
32+
git_command = " ".join(["git", cmd])
33+
return execute_cmd(git_command)
34+
35+
36+
def execute_cmd(cmd):
37+
try:
38+
out = subprocess.check_output(cmd, shell=True, universal_newlines=True, stderr=subprocess.STDOUT)
39+
except subprocess.CalledProcessError as err:
40+
print(f"Aborting: Got error while calling \"{cmd}\"")
41+
print(f"Details: {err}")
42+
print(f"Details: {err.output}")
43+
exit(1)
44+
return out.strip()
45+
46+
47+
def execute_cmd_visible(cmd):
48+
with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
49+
universal_newlines=True, stderr=subprocess.STDOUT) as proc:
50+
for line in proc.stdout:
51+
print(line, end='')
52+
if proc.returncode != 0:
53+
print(f"Aborting: Got error while calling \"{cmd}\"")
54+
exit(1)
55+
56+
57+
def get_head_sha():
58+
return execute_git_cmd("rev-parse HEAD")
59+
60+
61+
def generate_changelog(tag_new, previous_release_id):
62+
out = execute_git_cmd(f"log --reverse --pretty=format:%s {tag_new}..{previous_release_id}")
63+
return out
64+
65+
66+
def get_branch_diverge_commit(branch, base_branch):
67+
out = execute_git_cmd(f"rev-list --boundary {branch}...{base_branch} | grep '^-' | cut -c2- | tail -n1")
68+
return out
69+
70+
71+
def changelog_list(raw_changelog) -> list:
72+
changelog_list = []
73+
for line in raw_changelog.splitlines():
74+
if (match := re.match(commit_pattern, line)) is not None:
75+
if match.group(1) in title_map.keys():
76+
title = match.group(1)
77+
jira = match.group(2)
78+
changelog_list.append(title + jira)
79+
return list(set(changelog_list))
80+
81+
82+
def jira_release_note(ticket):
83+
try:
84+
issue = jira.issue(ticket)
85+
return issue.get_field(JIRA_RN_FIELD)
86+
except JIRAError:
87+
return ''
88+
89+
90+
def changelog_map(raw_changelog, tasks_list):
91+
changelog_dict = {}
92+
changelog_dup_dict = {}
93+
for line in raw_changelog.splitlines():
94+
if (match := re.match(commit_pattern, line)) is not None:
95+
if match.group(1) in title_map.keys():
96+
title = match.group(1)
97+
ticket = match.group(2)
98+
msg = match.group(3)
99+
pr_id = match.group(5) if match.group(5) else ''
100+
jira_rn = jira_release_note(ticket)
101+
desc = jira_rn.replace("\n", " ") if jira_rn else msg
102+
list_key = title + ticket
103+
entry = {'desc': desc, 'pr': pr_id}
104+
if list_key in tasks_list:
105+
if title in changelog_dup_dict:
106+
changelog_dup_dict[title].update({ticket: entry})
107+
else:
108+
changelog_dup_dict[title] = {ticket: entry}
109+
elif title in changelog_dict:
110+
changelog_dict[title].update({ticket: entry})
111+
else:
112+
changelog_dict[title] = {ticket: entry}
113+
return changelog_dict, changelog_dup_dict
114+
115+
116+
def format_changelog(changelog) -> str:
117+
result_lines = []
118+
for key in title_map.keys():
119+
if key in changelog.keys():
120+
result_lines.append(f"### {title_map[key]}")
121+
for jira, line in changelog[key].items():
122+
result_lines.append(f"- {line['desc']} [{jira.upper()}]")
123+
return '\n'.join(result_lines)
124+
125+
126+
def main():
127+
new_tag = os.getenv("NEW_TAG", get_head_sha())
128+
129+
previous_tag_base_commit = get_branch_diverge_commit(PREVIOUS_REF, BASE_BRANCH)
130+
branch_diff_changelog = generate_changelog(previous_tag_base_commit, PREVIOUS_REF)
131+
previous_tag_tasks_list = changelog_list(branch_diff_changelog)
132+
133+
new_changelog = generate_changelog(previous_tag_base_commit, new_tag)
134+
changelog_new, changelog_dup = changelog_map(new_changelog, previous_tag_tasks_list)
135+
136+
if OUTPUT_FILE_MD:
137+
with open(OUTPUT_FILE_MD, 'w') as f:
138+
print(f"Creating a markdown output file: '{OUTPUT_FILE_MD}'")
139+
f.write(format_changelog(changelog_new))
140+
141+
if OUTPUT_FILE_JSON:
142+
with open(OUTPUT_FILE_JSON, 'w') as f:
143+
print(f"Creating a json output file: '{OUTPUT_FILE_JSON}'")
144+
json.dump(changelog_new, f)
145+
146+
147+
if __name__ == "__main__":
148+
main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
jira==3.2.0
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from github import Github
2+
from jira import JIRA
3+
import re
4+
import os
5+
import urllib.parse
6+
7+
RELEASE_VERSION = os.getenv("RELEASE_VERSION", "2.2.8").strip('\"')
8+
CHANGELOG_SOURCE = os.getenv("CHANGELOG_SOURCE", "github").strip('\"') # /tmp/changelog_md
9+
10+
JIRA_USERNAME = os.getenv("JIRA_USERNAME").strip('\"') # in email format, e.g [email protected]
11+
JIRA_TOKEN = os.getenv("JIRA_TOKEN").strip('\"') # https://id.atlassian.com/manage-profile/security/api-tokens
12+
JIRA_SERVER = os.getenv("JIRA_SERVER", "https://heartex.atlassian.net").strip('\"')
13+
JIRA_PROJECT = os.getenv("JIRA_PROJECT", "DEV").strip('\"')
14+
15+
GH_TOKEN = os.getenv("GH_TOKEN").strip('\"') # https://github.com/settings/tokens/new
16+
GH_REPO = os.getenv("GH_REPO", "heartexlabs/label-studio").strip('\"')
17+
18+
jira = JIRA(JIRA_SERVER, basic_auth=(JIRA_USERNAME, JIRA_TOKEN))
19+
20+
jira_release_issues = []
21+
versions = jira.project_versions(project=JIRA_PROJECT)
22+
sorted_versions = sorted(versions, key=lambda x: x.name, reverse=True)
23+
jira_release = [v for v in sorted_versions if RELEASE_VERSION in v.name]
24+
if len(jira_release) > 0:
25+
issues_in_release = jira.search_issues(
26+
f"project = {jira_release[0].projectId} AND fixVersion = {jira_release[0].id} ORDER BY priority DESC, key ASC")
27+
jira_release_issues.extend([entry.key for entry in issues_in_release])
28+
print(
29+
f">[JIRA: Release {RELEASE_VERSION}]({JIRA_SERVER}/projects/{JIRA_PROJECT}/versions/{jira_release[0].id}/tab/release-report-all-issues)")
30+
else:
31+
print(f">Release not found in JIRA")
32+
33+
# print("Found in JIRA")
34+
# print(f"{sorted(jira_release_issues)}\n")
35+
release_changelog = ''
36+
if CHANGELOG_SOURCE == 'github':
37+
G = Github(GH_TOKEN)
38+
repo = G.get_repo(GH_REPO)
39+
releases = repo.get_releases()
40+
for release in releases:
41+
if RELEASE_VERSION in release.tag_name:
42+
release_changelog = release.body
43+
else:
44+
with open(CHANGELOG_SOURCE, 'r') as f:
45+
release_changelog = f.read()
46+
47+
gh_release_issues = []
48+
if release_changelog:
49+
for line in release_changelog.split("\n"):
50+
if line.startswith("* ") or line.startswith("- "):
51+
line_search = re.findall(r"[a-zA-Z]+-\d+", line)
52+
gh_release_issues.extend(line_search)
53+
else:
54+
print(f">Release not found in GitHub")
55+
exit()
56+
if len(gh_release_issues) > 0:
57+
print(
58+
f">[JIRA: JQL for tasks found in changelog]({JIRA_SERVER}/issues/?jql=key%20in%20({urllib.parse.quote(','.join(gh_release_issues))}))")
59+
60+
# flatten_gh_release_issues = sorted([val for sublist in set(gh_release_issues) for val in sublist])
61+
# print("Found in GitHub")
62+
# print(f"{gh_release_issues}\n")
63+
64+
if len(jira_release_issues) > 0:
65+
missing_in_gh = sorted([task for task in jira_release_issues if task not in gh_release_issues],
66+
key=lambda x: int(x.split('-')[1]))
67+
missing_in_gh_formatted = list(
68+
map(lambda task: "[" + task + "](" + JIRA_SERVER + "/browse/" + task + "): " + jira.issue(task).raw['fields'][
69+
'summary'], missing_in_gh))
70+
missing_in_gh_length = len(missing_in_gh)
71+
72+
if missing_in_gh_length > 0:
73+
print("><details>")
74+
print(f"> <summary>Missing in GitHub release({missing_in_gh_length})</summary>")
75+
print(">")
76+
ready_list = "\n> - ".join(missing_in_gh_formatted)
77+
print(f"> - {ready_list}")
78+
print(">")
79+
print("></details>")
80+
else:
81+
print(f">GitHub release is in sync with JIRA release.")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
jira==3.2.0
2+
PyGithub==1.55

.github/pull_request_template.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
### PR fulfills these requirements
2+
- [ ] Commit message(s) and PR title follows the format `[fix|feat|ci|chore|doc]: TICKET-ID: Short description of change made` ex. `fix: DEV-XXXX: Removed inconsistent code usage causing intermittent errors`
3+
- [ ] Tests for the changes have been added/updated (for bug fixes/features)
4+
- [ ] Docs have been added/updated (for bug fixes/features)
5+
- [ ] Best efforts were made to ensure docs/code are concise and coherent (checked for spelling/grammatical errors, commented out code, debug logs etc.)
6+
- [ ] Self-reviewed and ran all changes on a local instance (for bug fixes/features)
7+
8+
9+
10+
#### Change has impacts in these area(s)
11+
_(check all that apply)_
12+
- [ ] Product design
13+
- [ ] Backend (Database)
14+
- [ ] Backend (API)
15+
- [ ] Frontend
16+
17+
18+
19+
### Describe the reason for change
20+
_(link to issue, supportive screenshots etc.)_
21+
22+
23+
24+
#### What does this fix?
25+
_(if this is a bug fix)_
26+
27+
28+
29+
#### What is the new behavior?
30+
_(if this is a breaking or feature change)_
31+
32+
33+
34+
#### What is the current behavior?
35+
_(if this is a breaking or feature change)_
36+
37+
38+
39+
#### What libraries were added/updated?
40+
_(list all with version changes)_
41+
42+
43+
44+
#### Does this change affect performance?
45+
_(if so describe the impacts positive or negative)_
46+
47+
48+
49+
#### Does this change affect security?
50+
_(if so describe the impacts positive or negative)_
51+
52+
53+
54+
#### What alternative approaches were there?
55+
_(briefly list any if applicable)_
56+
57+
58+
59+
#### What feature flags were used to cover this change?
60+
_(briefly list any if applicable)_
61+
62+
63+
64+
### Does this PR introduce a breaking change?
65+
_(check only one)_
66+
- [ ] Yes, and covered entirely by feature flag(s)
67+
- [ ] Yes, and covered partially by feature flag(s)
68+
- [ ] No
69+
- [ ] Not sure (briefly explain the situation below)
70+
71+
72+
73+
### What level of testing was included in the change?
74+
_(check all that apply)_
75+
- [ ] e2e
76+
- [ ] integration
77+
- [ ] unit
78+
79+
80+
81+
### Which logical domain(s) does this change affect?
82+
_(for bug fixes/features, be as precise as possible. ex. Authentication, Annotation History, Review Stream etc.)_
83+

.github/workflows/build_pypi.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,11 @@ jobs:
146146
core.setOutput("pipy-artifact-url", artifact.url);
147147
core.setOutput("pipy-artifact-digests-sha256", artifact.digests.sha256);
148148
clearInterval(intervalId)
149-
} else if (currentAttempt > MAX_ATTEMPTS) {
149+
} else if (currentAttempt >= MAX_ATTEMPTS) {
150150
clearInterval(intervalId)
151151
throw Error('Max attempts exceeded')
152152
}
153-
}, 6 * 1000)
153+
}, 60 * 1000 )
154154
155155
- name: Attach artifacts to release
156156
if: inputs.release-id

0 commit comments

Comments
 (0)