#!/usr/bin/python3 # Find the next thing to work on for proposed-migration # Copyright (C) 2023 Canonical Ltd. # Author: Steve Langasek # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, version 3. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . import lzma import sys import webbrowser from argparse import ArgumentParser import yaml from launchpadlib.launchpad import Launchpad from ubuntutools.utils import get_url # proposed-migration is only concerned with the devel series; unlike other # tools, don't make this configurable excuses_url = "https://ubuntu-archive-team.ubuntu.com/proposed-migration/update_excuses.yaml.xz" def get_proposed_version(excuses, package): for k in excuses["sources"]: if k["source"] == package: return k.get("new-version") return None def claim_excuses_bug(launchpad, bug, package): print(f"LP: #{bug.id}: {bug.title}") ubuntu = launchpad.distributions["ubuntu"] series = ubuntu.current_series.fullseriesname for task in bug.bug_tasks: # targeting to a series doesn't make the default task disappear, # it just makes it useless if task.bug_target_name == f"{package} ({series})": our_task = task break if task.bug_target_name == f"{package} (Ubuntu)": our_task = task if our_task.assignee == launchpad.me: print("Bug already assigned to you.") return True if our_task.assignee: print(f"Currently assigned to {our_task.assignee.name}") print("""Do you want to claim this bug? [yN] """, end="") sys.stdout.flush() response = sys.stdin.readline() if response.strip().lower().startswith("y"): our_task.assignee = launchpad.me our_task.lp_save() return True return False def create_excuses_bug(launchpad, package, version): print("Will open a new bug") bug = launchpad.bugs.createBug( title=f"proposed-migration for {package} {version}", tags=("update-excuse"), target=f"https://api.launchpad.net/devel/ubuntu/+source/{package}", description=f"{package} {version} is stuck in -proposed.", ) task = bug.bug_tasks[0] task.assignee = launchpad.me task.lp_save() print(f"Opening {bug.web_link} in browser") webbrowser.open(bug.web_link) return bug def has_excuses_bugs(launchpad, package): ubuntu = launchpad.distributions["ubuntu"] pkg = ubuntu.getSourcePackage(name=package) if not pkg: raise ValueError(f"No such source package: {package}") tasks = pkg.searchTasks(tags=["update-excuse"], order_by=["id"]) bugs = [task.bug for task in tasks] if not bugs: return False if len(bugs) == 1: print(f"There is 1 open update-excuse bug against {package}") else: print(f"There are {len(bugs)} open update-excuse bugs against {package}") for bug in bugs: if claim_excuses_bug(launchpad, bug, package): return True return True def main(): parser = ArgumentParser() parser.add_argument("-l", "--launchpad", dest="launchpad_instance", default="production") parser.add_argument( "-v", "--verbose", default=False, action="store_true", help="be more verbose" ) parser.add_argument("package", nargs="?", help="act on this package only") args = parser.parse_args() args.launchpad = Launchpad.login_with("pm-helper", args.launchpad_instance, version="devel") f = get_url(excuses_url, False) with lzma.open(f) as lzma_f: excuses = yaml.load(lzma_f, Loader=yaml.CSafeLoader) if args.package: try: if not has_excuses_bugs(args.launchpad, args.package): proposed_version = get_proposed_version(excuses, args.package) if not proposed_version: print(f"Package {args.package} not found in -proposed.") sys.exit(1) create_excuses_bug(args.launchpad, args.package, proposed_version) except ValueError as e: sys.stderr.write(f"{e}\n") else: pass # for now if __name__ == "__main__": sys.exit(main())