ubuntu-dev-tools/pm-helper
2023-06-14 16:52:56 -07:00

150 lines
4.7 KiB
Python
Executable File

#!/usr/bin/python3
# Find the next thing to work on for proposed-migration
# Copyright (C) 2023 Canonical Ltd.
# Author: Steve Langasek <steve.langasek@ubuntu.com>
# 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 <http://www.gnu.org/licenses/>.
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())