Merge ~adam-collard/maas/+git/maas-release-tools:assign-bugs into ~maas-committers/maas/+git/maas-release-tools:main

Proposed by Adam Collard
Status: Merged
Approved by: Adam Collard
Approved revision: 7f62eecd6b956de7196640af1700d95b8ed5dc82
Merged at revision: 7f62eecd6b956de7196640af1700d95b8ed5dc82
Proposed branch: ~adam-collard/maas/+git/maas-release-tools:assign-bugs
Merge into: ~maas-committers/maas/+git/maas-release-tools:main
Diff against target: 134 lines (+57/-4)
3 files modified
maas_release_tools/actions.py (+6/-0)
maas_release_tools/launchpad.py (+43/-3)
maas_release_tools/scripts/release_manage.py (+8/-1)
Reviewer Review Type Date Requested Status
Christian Grabowski Approve
Review via email: mp+422836@code.launchpad.net

Commit message

Add assign-bugs-to-milestone script

To post a comment you must log in.
Revision history for this message
Christian Grabowski (cgrabowski) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/maas_release_tools/actions.py b/maas_release_tools/actions.py
2index 7d6d840..8a0c605 100644
3--- a/maas_release_tools/actions.py
4+++ b/maas_release_tools/actions.py
5@@ -17,6 +17,12 @@ class Actions:
6 action = getattr(self, action_name)
7 return action()
8
9+ def assign_bugs_to_milestone(self):
10+ """Assign bugs to a milestone, re-opening and closing as necessary"""
11+ self.lp_actions.assign_bugs_to_milestone(
12+ self.args.bugs, self.args.milestone
13+ )
14+
15 def move_done_bugs(self):
16 """Move completed bugs across milestones."""
17 self.lp_actions.move_done_bugs(self.args.origin, self.args.dest)
18diff --git a/maas_release_tools/launchpad.py b/maas_release_tools/launchpad.py
19index 135a1d3..25d7db7 100644
20--- a/maas_release_tools/launchpad.py
21+++ b/maas_release_tools/launchpad.py
22@@ -1,5 +1,6 @@
23 """Interact with Launchpad API."""
24
25+from contextlib import contextmanager
26 from datetime import datetime
27 import logging
28 from pathlib import Path
29@@ -52,6 +53,27 @@ class LaunchpadActions:
30 dest = self._get_milestone(dest_milestone)
31 self._move_bugs(UNFINISHED_BUGS, origin, dest, dry_run=self.dry_run)
32
33+ def assign_bugs_to_milestone(
34+ self, bugs: list[str], milestone_name: str
35+ ) -> None:
36+ """Assign bugs to a milestone, re-opening and closing as necessary."""
37+ with self._active_milestone(milestone_name) as milestone:
38+ for bug_number in bugs:
39+ bug = self.lp.bugs[bug_number]
40+ for task in bug.bug_tasks:
41+ if task.target == self._project:
42+ self.logger.info(
43+ f"assigning bug {bug_number} to milestone {milestone.name}"
44+ )
45+ task.milestone = milestone
46+ if not self.dry_run:
47+ task.lp_save()
48+ break
49+ else:
50+ self.logger.error(
51+ f"No task found for {bug_number} on project {self._project}"
52+ )
53+
54 def release_milestone(self, name: str):
55 """Release a milestone marking finished bug as released."""
56 milestone = self._get_milestone(name)
57@@ -65,8 +87,8 @@ class LaunchpadActions:
58 bug_task.lp_save()
59
60 self.logger.info(f"closing milestone {milestone.name}")
61+ milestone.is_active = False
62 if not self.dry_run:
63- milestone.is_active = False
64 milestone.lp_save()
65
66 if milestone.release is None:
67@@ -79,7 +101,7 @@ class LaunchpadActions:
68 def _get_client(
69 self, credentials_file: Optional[Path] = None
70 ) -> Launchpad:
71- """Return a Launchopad API client."""
72+ """Return a Launchpad API client."""
73 kwargs = {
74 "service_root": "https://api.launchpad.net",
75 "version": "devel",
76@@ -94,6 +116,24 @@ class LaunchpadActions:
77 raise UnknownLaunchpadEntry("milestone", name)
78 return milestone
79
80+ @contextmanager
81+ def _active_milestone(self, name):
82+ """Allows callers to temporarily open a milestone."""
83+ milestone = self._get_milestone(name)
84+ was_active = milestone.is_active
85+ if not was_active:
86+ self.logger.info(f"marking milestone {name} as temporarily active")
87+ if not self.dry_run:
88+ milestone.is_active = True
89+ if not was_active:
90+ milestone.lp_save()
91+ yield milestone
92+ if not was_active:
93+ self.logger.info(f"marking milestone {name} inactive")
94+ milestone.is_active = False
95+ if not self.dry_run:
96+ milestone.lp_save()
97+
98 def _move_bugs(
99 self,
100 statuses: Sequence[str],
101@@ -106,6 +146,6 @@ class LaunchpadActions:
102 self.logger.info(
103 f"retargeting bug {bug_task.bug.id}: {bug_task.bug.title}"
104 )
105+ bug_task.milestone = dest_milestone
106 if not dry_run:
107- bug_task.milestone = dest_milestone
108 bug_task.lp_save()
109diff --git a/maas_release_tools/scripts/release_manage.py b/maas_release_tools/scripts/release_manage.py
110index 2954476..c22c31e 100644
111--- a/maas_release_tools/scripts/release_manage.py
112+++ b/maas_release_tools/scripts/release_manage.py
113@@ -37,13 +37,20 @@ def parse_args():
114 default=False,
115 help="don't actually perform actions",
116 )
117- parser.add_argument("project", help="the project manage releases for")
118+ parser.add_argument("project", help="the project to manage releases for")
119
120 subparsers = parser.add_subparsers(
121 metavar="ACTION", dest="action", help="action to perform"
122 )
123 subparsers.required = True
124
125+ assign_bugs = subparsers.add_parser(
126+ "assign-bugs-to-milestone",
127+ help=Actions.assign_bugs_to_milestone.__doc__,
128+ )
129+ assign_bugs.add_argument("milestone", help="the milestone to assign")
130+ assign_bugs.add_argument("bugs", nargs="+", help="the bugs to assign")
131+
132 move_done_bugs = subparsers.add_parser(
133 "move-done-bugs", help="move done bugs from a milestone to another"
134 )

Subscribers

People subscribed via source and target branches

to all changes: