[Merge] ~vorlon/ubuntu-dev-tools:pm-helper into ubuntu-dev-tools:main

Simon Quigley mp+444677 at code.launchpad.net
Mon Jan 29 04:37:48 UTC 2024


Review: Approve

I'm no longer going to block on test cases. Let's move this forward, if there is interest in a test case we can address that later.

I apologize for not looking at this sooner, given the last uploads to ubuntu-dev-tools are from me. I will take care of the upload tomorrow, along with some testing and any necessary fixes.

I noticed two potential pedantic items, I'll check them further then fix as appropriate.

Thank you all for your hard work on this! I'm certain this will improve collaboration.

Diff comments:

> diff --git a/pm-helper b/pm-helper
> new file mode 100755
> index 0000000..9cdc176
> --- /dev/null
> +++ b/pm-helper
> @@ -0,0 +1,149 @@
> +#!/usr/bin/python3
> +# Find the next thing to work on for proposed-migration
> +# Copyright (C) 2023 Canonical Ltd.
> +# Author: Steve Langasek <steve.langasek at 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,

This needs testing with the staging Launchpad instance, since production is default but other options are supported.

> +        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())
> diff --git a/ubuntutools/utils.py b/ubuntutools/utils.py
> new file mode 100644
> index 0000000..dcc354e
> --- /dev/null
> +++ b/ubuntutools/utils.py
> @@ -0,0 +1,82 @@
> +# Copyright (C) 2019-2023 Canonical Ltd.
> +# Author: Brian Murray <brian.murray at canonical.com> et al.
> +
> +# This program is free software: you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; version 3 of the License.
> +#
> +# 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/>.
> +
> +"""Portions of archive related code that is re-used by various tools."""
> +
> +from datetime import datetime
> +import os
> +import re
> +import urllib.request
> +
> +import dateutil.parser
> +from dateutil.tz import tzutc
> +
> +
> +def get_cache_dir():
> +    cache_dir = os.environ.get('XDG_CACHE_HOME',
> +                               os.path.expanduser(os.path.join('~', '.cache')))
> +    uat_cache = os.path.join(cache_dir, 'ubuntu-archive-tools')

Probably needs to be ubuntu-dev-tools, no?

> +    os.makedirs(uat_cache, exist_ok=True)
> +    return uat_cache
> +
> +
> +def get_url(url, force_cached):
> +    ''' Return file to the URL, possibly caching it
> +    '''
> +    cache_file = None
> +
> +    # ignore bileto urls wrt caching, they're usually too small to matter
> +    # and we don't do proper cache expiry
> +    m = re.search('ubuntu-archive-team.ubuntu.com/proposed-migration/'
> +                  '([^/]*)/([^/]*)',
> +                  url)
> +    if m:
> +        cache_dir = get_cache_dir()
> +        cache_file = os.path.join(cache_dir, '%s_%s' % (m.group(1), m.group(2)))
> +    else:
> +        # test logs can be cached, too
> +        m = re.search(
> +            'https://autopkgtest.ubuntu.com/results/autopkgtest-[^/]*/([^/]*)/([^/]*)'
> +            '/[a-z0-9]*/([^/]*)/([_a-f0-9]*)@/log.gz',
> +            url)
> +        if m:
> +            cache_dir = get_cache_dir()
> +            cache_file = os.path.join(
> +                cache_dir, '%s_%s_%s_%s.gz' % (
> +                    m.group(1), m.group(2), m.group(3), m.group(4)))
> +
> +    if cache_file:
> +        try:
> +            prev_mtime = os.stat(cache_file).st_mtime
> +        except FileNotFoundError:
> +            prev_mtime = 0
> +        prev_timestamp = datetime.fromtimestamp(prev_mtime, tz=tzutc())
> +        new_timestamp = datetime.now(tz=tzutc()).timestamp()
> +        if force_cached:
> +            return open(cache_file, 'rb')
> +
> +    f = urllib.request.urlopen(url)
> +
> +    if cache_file:
> +        remote_ts = dateutil.parser.parse(f.headers['last-modified'])
> +        if remote_ts > prev_timestamp:
> +            with open('%s.new' % cache_file, 'wb') as new_cache:
> +                for line in f:
> +                    new_cache.write(line)
> +            os.rename('%s.new' % cache_file, cache_file)
> +            os.utime(cache_file, times=(new_timestamp, new_timestamp))
> +        f.close()
> +        f = open(cache_file, 'rb')
> +    return f


-- 
https://code.launchpad.net/~vorlon/ubuntu-dev-tools/+git/ubuntu-dev-tools/+merge/444677
Your team Ubuntu Development Team is subscribed to branch ubuntu-dev-tools:main.




More information about the Ubuntu-reviews mailing list