#!/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 from argparse import ArgumentParser import sys import webbrowser 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("LP: #%d: %s" % (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 == "%s (%s)" % (package, series): our_task = task break elif task.bug_target_name == "%s (Ubuntu)" % package: our_task = task if our_task.assignee == launchpad.me: print("Bug already assigned to you.") return True elif our_task.assignee: print("Currently assigned to %s" % 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 = 'proposed-migration for %s %s' % (package, version), tags = ('update-excuse'), target = 'https://api.launchpad.net/devel/ubuntu/+source/%s' % package, description = '%s %s is stuck in -proposed.' % (package, version) ) task = bug.bug_tasks[0] task.assignee = launchpad.me task.lp_save() print("Opening %s in browser" % bug.web_link) 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("There is 1 open update-excuse bug against %s" % package) else: print("There are %d open update-excuse bugs against %s" \ % (len(bugs), 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("Package %s not found in -proposed." % args.package) 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())