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

Subscribers

People subscribed via source and target branches

to all changes: