diff --git a/debian/changelog b/debian/changelog index c88bce0..de6f16a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -22,7 +22,10 @@ ubuntu-dev-tools (0.137) UNRELEASED; urgency=low - Do the report boiler-plate checking in a script that wraps an editor, so that we only edit the report once, after checking for duplicates. - rm the tmpdir with a little more force (shutil.rmtree) (LP: #899399) - * New Tool: ubuntu-upload-permission: Query upload permissions (LP: #876554) + * New Tools: (LP: #876554) + - ubuntu-upload-permission: Query upload permissions. + - ubuntu-is-seeded: Query a package's seed status. Whether it is on + current daily images and/or part of the supported seed. [ Andreas Moog ] * sponsor-patch: Check permission to unsubscribe sponsors-team (LP: #896884) diff --git a/debian/control b/debian/control index 7255fb1..aff2abc 100644 --- a/debian/control +++ b/debian/control @@ -105,6 +105,7 @@ Description: useful tools for Ubuntu developers - syncpackage - helper to prepare .changes file to upload synced packages - ubuntu-build - give commands to the Launchpad build daemons from the command line. + - ubuntu-is-seeded - query if a package is safe to upload during a freeze. - ubuntu-iso - output information of an Ubuntu ISO image. - ubuntu-upload-permission - query / list the upload permissions for a package. diff --git a/debian/copyright b/debian/copyright index c4de0f3..8ff2b25 100644 --- a/debian/copyright +++ b/debian/copyright @@ -150,6 +150,7 @@ Files: doc/pull-debian-debdiff.1 doc/reverse-depends.1 doc/sponsor-patch.1 doc/ubuntu-dev-tools.5 + doc/ubuntu-is-seeded.1 doc/ubuntu-upload-permission.1 doc/update-maintainer.1 enforced-editing-wrapper @@ -159,6 +160,7 @@ Files: doc/pull-debian-debdiff.1 reverse-depends sponsor-patch test-data/* + ubuntu-is-seeded ubuntu-upload-permission ubuntutools/archive.py ubuntutools/builder.py diff --git a/doc/ubuntu-is-seeded.1 b/doc/ubuntu-is-seeded.1 new file mode 100644 index 0000000..1947ba4 --- /dev/null +++ b/doc/ubuntu-is-seeded.1 @@ -0,0 +1,60 @@ +.\" Copyright (C) 2011, Stefano Rivera +.\" +.\" Permission to use, copy, modify, and/or distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +.\" REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +.\" AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +.\" LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +.\" OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +.\" PERFORMANCE OF THIS SOFTWARE. +.TH ubuntu\-is\-seeded 1 "December 2011" ubuntu\-dev\-tools + +.SH NAME +ubuntu\-is\-seeded \- Determine whether a package is safe to upload +during a freeze + +.SH SYNOPSIS +.B ubuntu\-is\-seeded \fR[\fIoptions\fR] \fIpackage\fR... + +.SH DESCRIPTION +Lists all the current daily images containing the specified packages. +Or whether the package is part of the supported seed. +.PP +If it isn't on an image, it should be safe to upload. +During the final freeze, one should avoid packages in the supported seed +too. +.PP +An index of the current manifests is downloaded from UbuntuWire. + +.SH OPTIONS +.TP +\fB\-b\fR, \fB\-\-binary\fR +The packages specified are binary packages. +This is faster than source packages, as otherwise we must query LP to +determine the binary packages that every specified source package +builds. +.TP +\fB\-u\fR \fIURL\fR, \fB\-\-data\-url\fR=\fIURL\fR +URL for index of seeded packages. +Default: UbuntuWire's service at +\fBhttp://qa.ubuntuwire.org/ubuntu-seeded-packages/seeded.json.gz\fR. +.TP +\fB\-h\fR, \fB\-\-help\fR +Display a help message and exit + +.SH EXAMPLES +All the images that contain unity: +.IP +.nf +.B ubuntu\-is\-seeded -b unity +.fi + +.SH AUTHORS +\fBubuntu\-is\-seeded\fR and this manpage were written by Stefano Rivera +. +.PP +Both are released under the terms of the ISC License. diff --git a/setup.py b/setup.py index 6c895e5..840bfb2 100755 --- a/setup.py +++ b/setup.py @@ -40,6 +40,7 @@ scripts = ['404main', 'submittodebian', 'syncpackage', 'ubuntu-build', + 'ubuntu-is-seeded', 'ubuntu-iso', 'ubuntu-upload-permission', 'update-maintainer', diff --git a/ubuntu-is-seeded b/ubuntu-is-seeded new file mode 100755 index 0000000..b443b00 --- /dev/null +++ b/ubuntu-is-seeded @@ -0,0 +1,129 @@ +#!/usr/bin/python +# +# Copyright (C) 2011, Stefano Rivera +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import collections +import gzip +import json +import optparse +import os +import time +import urllib + +from devscripts.logger import Logger + +from ubuntutools.lp.lpapicache import Distribution, PackageNotFoundException + +DATA_URL = 'http://qa.ubuntuwire.org/ubuntu-seeded-packages/seeded.json.gz' + + +def load_index(url): + '''Download a new copy of the image contents index, if necessary, + and read it. + ''' + cachedir = os.path.expanduser('~/.cache/ubuntu-dev-tools') + fn = os.path.join(cachedir, 'seeded.json.gz') + + if (not os.path.isfile(fn) + or time.time() - os.path.getmtime(fn) > 60 * 60 * 2): + if not os.path.isdir(cachedir): + os.makedirs(cachedir) + urllib.urlretrieve(url, fn) + + with gzip.open(fn, 'r') as f: + return json.load(f) + + +def resolve_binaries(sources): + '''Return a dict of source:binaries for all binary packages built by + sources + ''' + archive = Distribution('ubuntu').getArchive() + binaries = {} + for source in sources: + try: + spph = archive.getSourcePackage(source) + except PackageNotFoundException, e: + Logger.error(str(e)) + continue + binaries[source] = sorted(set(bpph.getPackageName() + for bpph in spph.getBinaries())) + + return binaries + + +def present_on(appearences): + '''Format a list of (flavor, type) tuples into a human-readable string''' + present = collections.defaultdict(set) + for flavor, type_ in appearences: + present[flavor].add(type_) + for flavor, types in present.iteritems(): + if len(types) > 1: + types.discard('supported') + output = [' %s: %s' % (flavor, ', '.join(sorted(types))) + for flavor, types in present.iteritems()] + output.sort() + return '\n'.join(output) + + +def output_binaries(index, binaries): + '''Print binaries found in index''' + for binary in binaries: + if binary in index: + print "%s is seeded in:" % binary + print present_on(index[binary]) + else: + print "%s is not seeded." % binary + + +def output_by_source(index, by_source): + '''Print binaries found in index. Grouped by source''' + for source, binaries in by_source.iteritems(): + seen = False + for binary in binaries: + if binary in index: + seen = True + print "%s (from %s) is seeded in:" % (binary, source) + print present_on(index[binary]) + if not seen: + print "%s's binaries are not seeded." % source + + +def main(): + '''Query which images the specified packages are on''' + parser = optparse.OptionParser('%prog [options] package...') + parser.add_option('-b', '--binary', + default=False, action='store_true', + help="Binary packages are being specified, " + "not source packages (fast)") + parser.add_option('-u', '--data-url', metavar='URL', + default=DATA_URL, + help='URL for the seeded packages index. ' + 'Default: UbuntuWire') + options, args = parser.parse_args() + + if len(args) < 1: + parser.error("At least one package must be specified") + + index = load_index(options.data_url) + if options.binary: + output_binaries(index, args) + else: + binaries = resolve_binaries(args) + output_by_source(index, binaries) + + +if __name__ == '__main__': + main()