Skip to content

Commit 101ebef

Browse files
committed
Merge branch 'tagman' of github.com:yaswant/SimSys_Scripts into tagman
2 parents e6c4c98 + d49a9fe commit 101ebef

4 files changed

Lines changed: 6372 additions & 2861 deletions

File tree

.github/workflows/trigger-project-workflow.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Trigger Review Project
22

33
on:
44
pull_request_target:
5-
types: ["opened", "synchronize", "reopened", "edited", "review_requested", "review_request_removed"]
5+
types: ["opened", "synchronize", "reopened", "edited", "review_requested", "review_request_removed", "closed"]
66
pull_request_review:
77
pull_request_review_comment:
88

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# -----------------------------------------------------------------------------
2+
# (C) Crown copyright Met Office. All rights reserved.
3+
# The file LICENCE, distributed with this code, contains details of the terms
4+
# under which the code may be used.
5+
# -----------------------------------------------------------------------------
6+
7+
"""
8+
This script will run the processes needed to close off and finish a milestone
9+
* Archive all completed PRs against the milestone in the Review Tracker Project
10+
* Close the milestone in each repository
11+
* Report on:
12+
* Closed PRs at a different milestone
13+
* Remaining open PRs and issues against this milestone
14+
* Closed PRs against this milestone
15+
"""
16+
from pathlib import Path
17+
import argparse
18+
from review_project import ProjectData
19+
20+
21+
def print_banner(message: str) -> None:
22+
print("\n")
23+
print("=" * len(message))
24+
print(message)
25+
print("=" * len(message))
26+
27+
28+
def still_open(open_prs: dict, current_milestone: str) -> int:
29+
"""
30+
Report on all open pull requests for the current milestone
31+
"""
32+
33+
print_banner(f"Checking for open pull requests for {current_milestone}")
34+
35+
total = 0
36+
37+
for repo in open_prs:
38+
print(f"{repo} \n{'-'*len(repo)}")
39+
for pr in open_prs[repo]:
40+
print(f"{pr.status: <18} #{pr.number: <5} {pr.title}")
41+
42+
count = len(open_prs[repo])
43+
print(f"-> {count} open pull request(s) in {repo} \n")
44+
total += count
45+
46+
if total == 0:
47+
print(f"No open pull requests for {current_milestone} \n")
48+
49+
return total
50+
51+
52+
def closed_other(
53+
closed_prs: dict, current_milestone: str, dry_run: bool = False
54+
) -> int:
55+
"""
56+
Report on closed pull requests not at the current milestone.
57+
"""
58+
59+
print_banner(f"Checking for closed pull requests not for {current_milestone}")
60+
61+
total = 0
62+
63+
for milestone in closed_prs:
64+
if milestone == current_milestone:
65+
continue
66+
67+
elif milestone == "None":
68+
print(f"Setting pull requests with no milestone " f"to {current_milestone}")
69+
for repo in closed_prs[milestone]:
70+
for pr in closed_prs[milestone][repo]:
71+
pr.modify_milestone(current_milestone, dry_run)
72+
73+
else:
74+
for repo in closed_prs[milestone]:
75+
print(f"{repo} \n{'-' * len(repo)}")
76+
for pr in closed_prs[milestone][repo]:
77+
print(f"#{pr.number : <5} {pr.title}")
78+
79+
count = len(closed_prs[milestone][repo])
80+
print(
81+
f"-> {count} closed pull request(s) in {repo} at milestone {milestone} \n"
82+
)
83+
total += count
84+
85+
return total
86+
87+
88+
def check_ready(data: ProjectData, milestone: str, dry_run: bool = False) -> None:
89+
"""
90+
Check if the milestone is ready to be closed by confirming that:
91+
* all pull requests for this milestone have been completed
92+
* all closed pull requests in the project are in this milestone.
93+
94+
Give the user the choice to continue regardless since there may be valid
95+
exceptions.
96+
"""
97+
open_prs = data.get_milestone(milestone=milestone, status="open")
98+
closed_prs = data.get_all_milestones(status="closed")
99+
total_open = still_open(open_prs, milestone)
100+
total_other = closed_other(closed_prs, milestone, dry_run)
101+
102+
if total_open or total_other:
103+
print("=" * 50)
104+
print(
105+
f"{total_open} open pull request(s) in {milestone} and "
106+
f"{total_other} closed pull request(s) not in {milestone}."
107+
)
108+
cont = input("Would you like to continue with closing this milestone? (y/n) ")
109+
110+
if cont == "n":
111+
exit(0)
112+
elif cont != "y":
113+
print("Unrecognised input, please select y or n")
114+
115+
116+
def report(data: ProjectData, milestone: str) -> None:
117+
"""
118+
Report on the pull requests completed in this milestone
119+
"""
120+
121+
print_banner(f"Pull requests completed for {milestone}")
122+
123+
closed = data.get_milestone(milestone=milestone, status="closed")
124+
125+
total = 0
126+
for repo in closed:
127+
count = len(closed[repo])
128+
total += count
129+
print(f"{repo: <20} {count: >3} pull requests")
130+
131+
print(f"{total} pull requests completed in {milestone}")
132+
133+
134+
def parse_args():
135+
"""
136+
Read command line args
137+
"""
138+
139+
testfile = Path(__file__).parent / "test" / "test.json"
140+
141+
parser = argparse.ArgumentParser(
142+
"Archive milestone contents within the project and close the "
143+
"milestone within each repository."
144+
)
145+
146+
parser.add_argument("--milestone", help="Milestone to archive and close.")
147+
parser.add_argument(
148+
"--test",
149+
action="store_true",
150+
help="Use test input files.",
151+
)
152+
parser.add_argument(
153+
"--capture_project",
154+
action="store_true",
155+
help="Capture the current project status into the test file",
156+
)
157+
parser.add_argument(
158+
"--file",
159+
default=testfile,
160+
help="Filepath to test data for either capturing the project status, "
161+
"or use as input data.",
162+
)
163+
parser.add_argument(
164+
"--dry",
165+
action="store_true",
166+
help="Dry run. Print commands, don't action them. Always true when "
167+
"running with test data.",
168+
)
169+
170+
args = parser.parse_args()
171+
172+
args.file = Path(args.file)
173+
args.file = args.file.expanduser().resolve()
174+
175+
if args.test:
176+
args.dry = True
177+
178+
return args
179+
180+
181+
def main(
182+
milestone: str, test: bool, capture_project: bool, file: Path, dry: bool
183+
) -> None:
184+
185+
# Get milestone data
186+
if test:
187+
data = ProjectData.from_file(file)
188+
else:
189+
data = ProjectData.from_github(capture_project, file)
190+
191+
# Process data and report on status
192+
check_ready(data, milestone, dry)
193+
report(data, milestone)
194+
195+
# Archive pull requests at the milestone
196+
print_banner(f"Archiving Milestone {milestone}")
197+
data.archive_milestone(milestone, dry_run=dry)
198+
199+
# Close milestones
200+
# TODO: run this command from here, rather than prompting user. Leaving
201+
# like this until script feels stable.
202+
print_banner("Milestone Completed in Simulation Systems Review Tracker project")
203+
print("Run this command to close the milestone in all repositories:")
204+
print(f'./sbin/gh_manage_milestones -t "{milestone}" -m close')
205+
206+
207+
if __name__ == "__main__":
208+
args = parse_args()
209+
main(args.milestone, args.test, args.capture_project, args.file, args.dry)

0 commit comments

Comments
 (0)