Compare commits

..

No commits in common. "main" and "0.203" have entirely different histories.
main ... 0.203

38 changed files with 213 additions and 444 deletions

View File

@ -34,7 +34,6 @@ disable=fixme,locally-disabled,missing-docstring,useless-option-value,
duplicate-code, duplicate-code,
too-many-instance-attributes, too-many-instance-attributes,
too-many-nested-blocks, too-many-nested-blocks,
too-many-positional-arguments,
too-many-lines, too-many-lines,

View File

@ -25,7 +25,6 @@ import shutil
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
from typing import Any, NoReturn
from urllib.parse import quote from urllib.parse import quote
try: try:
@ -51,7 +50,7 @@ from ubuntutools.question import YesNoQuestion
Logger = getLogger() Logger = getLogger()
def error(msg: str, *args: Any) -> NoReturn: def error(msg, *args):
Logger.error(msg, *args) Logger.error(msg, *args)
sys.exit(1) sys.exit(1)

View File

@ -23,7 +23,6 @@
import argparse import argparse
import sys import sys
from typing import Any, NoReturn
from launchpadlib.errors import HTTPError from launchpadlib.errors import HTTPError
from launchpadlib.launchpad import Launchpad from launchpadlib.launchpad import Launchpad
@ -34,7 +33,7 @@ from ubuntutools.config import UDTConfig
Logger = getLogger() Logger = getLogger()
def error_out(msg: str, *args: Any) -> NoReturn: def error_out(msg, *args):
Logger.error(msg, *args) Logger.error(msg, *args)
sys.exit(1) sys.exit(1)

1
debian/.gitignore vendored
View File

@ -1 +0,0 @@
files

102
debian/changelog vendored
View File

@ -1,105 +1,3 @@
ubuntu-dev-tools (0.209) UNRELEASED; urgency=medium
[ Colin Watson ]
* Demote sudo to Recommends, and indicate which tools need it in the
package description.
[ Florent 'Skia' Jacquet ]
* pm-helper: make use of YesNoQuestion
-- Mattia Rizzolo <mattia@debian.org> Tue, 06 Jan 2026 17:55:43 +0100
ubuntu-dev-tools (0.208) unstable; urgency=medium
[ Gianfranco Costamagna ]
* ubuntu-build: consider amd64v3 as valid architecture
[ Sebastien Bacher ]
* ubuntu-build: fix non batch mode errors.
[ Benjamin Drung ]
* Format code with black and isort
* ubuntutools/pullpkg.py: initialize vcscmd
* make pylint and mypy happy
* mark non-returning functions with typing.NoReturn
* run-linters: add --errors-only mode and run this during package build
* Drop Lintian overrides related to .pyc files
* Drop obsolete Rules-Requires-Root: no
* run mypy during package build
* sponsor-patch: stop checking for bzr being present
* Modernize SourcePackage._run_lintian()
* requestsync: support pocket parameter in get_ubuntu_srcpkg (LP: #2115990)
-- Benjamin Drung <bdrung@debian.org> Wed, 03 Dec 2025 16:33:47 +0100
ubuntu-dev-tools (0.207) unstable; urgency=medium
* Team upload.
[ Dan Streetman ]
* Fix pull-lp-source --upload-queue (LP: #2110061)
[ Colin Watson ]
* Optimize Launchpad collection handling.
-- Colin Watson <cjwatson@debian.org> Mon, 15 Sep 2025 15:58:34 +0100
ubuntu-dev-tools (0.206) unstable; urgency=medium
[ Dan Bungert ]
* mk-sbuild: enable pkgmaintainermangler
[ Shengjing Zhu ]
* import-bug-from-debian: package option is overridden and not used
[ Fernando Bravo Hernández ]
* Parsing arch parameter to getBinaryPackage() (LP: #2081861)
[ Simon Quigley ]
* Read ~/.devscripts in a more robust way, to ideally pick up multi-line
variables (Closes: #725418).
* mk-sbuild: default to using UTC for schroots (LP: #2097159).
* syncpackage: s/syncblacklist/syncblocklist/g
* syncpackage: Cache the sync blocklist in-memory, so it's not fetched
multiple times when syncing more than one package.
* syncpackage: Catch exceptions cleanly, simply skipping to the next
package (erring on the side of caution) if there is an error doing the
download (LP: #1943286).
-- Simon Quigley <tsimonq2@debian.org> Tue, 04 Mar 2025 13:43:15 -0600
ubuntu-dev-tools (0.205) unstable; urgency=medium
* [syncpackage] When syncing multiple packages, if one of the packages is in
the sync blocklist, do not exit, simply continue.
* [syncpackage] Do not use exit(1) on an error or exception unless it
applies to all packages, instead return None so we can continue to the
next package.
* [syncpackage] Add support for -y or --yes, noted that it should be used
with care.
* Update Standards-Version to 4.7.2, no changes needed.
-- Simon Quigley <tsimonq2@debian.org> Sat, 01 Mar 2025 11:29:54 -0600
ubuntu-dev-tools (0.204) unstable; urgency=medium
[ Simon Quigley ]
* Update Standards-Version to 4.7.1, no changes needed.
* Add several Lintian overrides related to .pyc files.
* Add my name to the copyright file.
* Rename bitesize to lp-bitesize (Closes: #1076224).
* Add a manpage for running-autopkgtests.
* Add a large warning at the top of mk-sbuild encouraging the use of the
unshare backend. This is to provide ample warning to users.
* Remove mail line from default ~/.sbuildrc, to resolve the undeclared
dependency on sendmail (Closes: #1074632).
[ Julien Plissonneau Duquène ]
* Fix reverse-depends -b crash on packages that b-d on themselves
(Closes: #1087760).
-- Simon Quigley <tsimonq2@debian.org> Mon, 24 Feb 2025 19:54:39 -0600
ubuntu-dev-tools (0.203) unstable; urgency=medium ubuntu-dev-tools (0.203) unstable; urgency=medium
[ Steve Langasek ] [ Steve Langasek ]

24
debian/control vendored
View File

@ -8,17 +8,16 @@ Uploaders:
Mattia Rizzolo <mattia@debian.org>, Mattia Rizzolo <mattia@debian.org>,
Simon Quigley <tsimonq2@debian.org>, Simon Quigley <tsimonq2@debian.org>,
Build-Depends: Build-Depends:
debhelper-compat (= 13),
dh-make,
dh-python,
black <!nocheck>, black <!nocheck>,
dctrl-tools, dctrl-tools,
debhelper-compat (= 13),
devscripts (>= 2.11.0~), devscripts (>= 2.11.0~),
dh-make,
dh-python,
distro-info (>= 0.2~), distro-info (>= 0.2~),
flake8, flake8,
isort <!nocheck>, isort <!nocheck>,
lsb-release, lsb-release,
mypy <!nocheck>,
pylint <!nocheck>, pylint <!nocheck>,
python3-all, python3-all,
python3-apt, python3-apt,
@ -31,9 +30,9 @@ Build-Depends:
python3-pytest, python3-pytest,
python3-requests <!nocheck>, python3-requests <!nocheck>,
python3-setuptools, python3-setuptools,
python3-typeshed <!nocheck>,
python3-yaml <!nocheck>, python3-yaml <!nocheck>,
Standards-Version: 4.7.2 Standards-Version: 4.7.0
Rules-Requires-Root: no
Vcs-Git: https://git.launchpad.net/ubuntu-dev-tools Vcs-Git: https://git.launchpad.net/ubuntu-dev-tools
Vcs-Browser: https://git.launchpad.net/ubuntu-dev-tools Vcs-Browser: https://git.launchpad.net/ubuntu-dev-tools
Homepage: https://launchpad.net/ubuntu-dev-tools Homepage: https://launchpad.net/ubuntu-dev-tools
@ -41,12 +40,12 @@ Homepage: https://launchpad.net/ubuntu-dev-tools
Package: ubuntu-dev-tools Package: ubuntu-dev-tools
Architecture: all Architecture: all
Depends: Depends:
dpkg-dev,
binutils, binutils,
dctrl-tools, dctrl-tools,
devscripts (>= 2.11.0~), devscripts (>= 2.11.0~),
diffstat, diffstat,
distro-info (>= 0.2~), distro-info (>= 0.2~),
dpkg-dev,
dput, dput,
lsb-release, lsb-release,
python3, python3,
@ -60,6 +59,7 @@ Depends:
python3-ubuntutools (= ${binary:Version}), python3-ubuntutools (= ${binary:Version}),
python3-yaml, python3-yaml,
sensible-utils, sensible-utils,
sudo,
tzdata, tzdata,
${misc:Depends}, ${misc:Depends},
${perl:Depends}, ${perl:Depends},
@ -72,11 +72,10 @@ Recommends:
genisoimage, genisoimage,
lintian, lintian,
patch, patch,
sbuild | pbuilder | cowbuilder,
python3-dns, python3-dns,
quilt, quilt,
reportbug (>= 3.39ubuntu1), reportbug (>= 3.39ubuntu1),
sbuild | pbuilder | cowbuilder,
sudo,
ubuntu-keyring | ubuntu-archive-keyring, ubuntu-keyring | ubuntu-archive-keyring,
Suggests: Suggests:
bzr | brz, bzr | brz,
@ -93,7 +92,7 @@ Description: useful tools for Ubuntu developers
willing to help fix it. willing to help fix it.
- check-mir - check support status of build/binary dependencies - check-mir - check support status of build/binary dependencies
- check-symbols - will compare and give you a diff of the exported symbols of - check-symbols - will compare and give you a diff of the exported symbols of
all .so files in a binary package. [sudo] all .so files in a binary package.
- dch-repeat - used to repeat a change log into an older release. - dch-repeat - used to repeat a change log into an older release.
- grab-merge - grabs a merge from merges.ubuntu.com easily. - grab-merge - grabs a merge from merges.ubuntu.com easily.
- grep-merges - search for pending merges from Debian. - grep-merges - search for pending merges from Debian.
@ -101,10 +100,9 @@ Description: useful tools for Ubuntu developers
- merge-changelog - manually merges two Debian changelogs with the same base - merge-changelog - manually merges two Debian changelogs with the same base
version. version.
- mk-sbuild - script to create LVM snapshot chroots via schroot and - mk-sbuild - script to create LVM snapshot chroots via schroot and
sbuild. [sbuild, sudo] sbuild.
- pbuilder-dist, cowbuilder-dist - wrapper script for managing several build - pbuilder-dist, cowbuilder-dist - wrapper script for managing several build
chroots (for different Ubuntu and Debian releases) on the same system. chroots (for different Ubuntu and Debian releases) on the same system.
[pbuilder | cowbuilder, sudo]
- pull-debian-debdiff - attempts to find and download a specific version of - pull-debian-debdiff - attempts to find and download a specific version of
a Debian package and its immediate parent to generate a debdiff. a Debian package and its immediate parent to generate a debdiff.
- pull-debian-source - downloads the latest source package available in - pull-debian-source - downloads the latest source package available in
@ -124,7 +122,7 @@ Description: useful tools for Ubuntu developers
autopkgtests on the Ubuntu autopkgtest infrastructure autopkgtests on the Ubuntu autopkgtest infrastructure
- seeded-in-ubuntu - query if a package is safe to upload during a freeze. - seeded-in-ubuntu - query if a package is safe to upload during a freeze.
- setup-packaging-environment - assistant to get an Ubuntu installation - setup-packaging-environment - assistant to get an Ubuntu installation
ready for packaging work. [sudo] ready for packaging work.
- sponsor-patch - Downloads a patch from a Launchpad bug, patches the source - sponsor-patch - Downloads a patch from a Launchpad bug, patches the source
package, and uploads it (to Ubuntu or a PPA) package, and uploads it (to Ubuntu or a PPA)
- submittodebian - automatically send your changes to Debian as a bug report. - submittodebian - automatically send your changes to Debian as a bug report.

6
debian/copyright vendored
View File

@ -11,7 +11,6 @@ Files: backportpackage
doc/check-symbols.1 doc/check-symbols.1
doc/requestsync.1 doc/requestsync.1
doc/ubuntu-iso.1 doc/ubuntu-iso.1
doc/running-autopkgtests.1
GPL-2 GPL-2
README.updates README.updates
requestsync requestsync
@ -26,7 +25,6 @@ Copyright: 2007, Albert Damen <albrt@gmx.net>
2010, Evan Broder <evan@ebroder.net> 2010, Evan Broder <evan@ebroder.net>
2006-2007, Luke Yelavich <themuso@ubuntu.com> 2006-2007, Luke Yelavich <themuso@ubuntu.com>
2009-2010, Michael Bienia <geser@ubuntu.com> 2009-2010, Michael Bienia <geser@ubuntu.com>
2024-2025, Simon Quigley <tsimonq2@debian.org>
2010-2011, Stefano Rivera <stefanor@ubuntu.com> 2010-2011, Stefano Rivera <stefanor@ubuntu.com>
2008, Stephan Hermann <sh@sourcecode.de> 2008, Stephan Hermann <sh@sourcecode.de>
2007, Steve Kowalik <stevenk@ubuntu.com> 2007, Steve Kowalik <stevenk@ubuntu.com>
@ -74,14 +72,14 @@ License: GPL-2+
On Debian systems, the complete text of the GNU General Public License On Debian systems, the complete text of the GNU General Public License
version 2 can be found in the /usr/share/common-licenses/GPL-2 file. version 2 can be found in the /usr/share/common-licenses/GPL-2 file.
Files: doc/lp-bitesize.1 Files: doc/bitesize.1
doc/check-mir.1 doc/check-mir.1
doc/grab-merge.1 doc/grab-merge.1
doc/merge-changelog.1 doc/merge-changelog.1
doc/pm-helper.1 doc/pm-helper.1
doc/setup-packaging-environment.1 doc/setup-packaging-environment.1
doc/syncpackage.1 doc/syncpackage.1
lp-bitesize bitesize
check-mir check-mir
GPL-3 GPL-3
grab-merge grab-merge

3
debian/rules vendored
View File

@ -3,11 +3,10 @@
override_dh_auto_clean: override_dh_auto_clean:
dh_auto_clean dh_auto_clean
rm -f .coverage rm -f .coverage
rm -rf .mypy_cache .tox rm -rf .tox
override_dh_auto_test: override_dh_auto_test:
ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
./run-linters --errors-only
python3 -m pytest -v ubuntutools python3 -m pytest -v ubuntutools
endif endif

View File

@ -4,5 +4,4 @@ Depends:
python3-pytest, python3-pytest,
python3-setuptools, python3-setuptools,
@, @,
Restrictions: Restrictions: allow-stderr
allow-stderr,

View File

@ -1,21 +1,21 @@
.TH lp-bitesize "1" "May 9 2010" "ubuntu-dev-tools" .TH bitesize "1" "May 9 2010" "ubuntu-dev-tools"
.SH NAME .SH NAME
lp-bitesize \- Add \fBbitesize\fR tag to bugs and add a comment. bitesize \- Add \fBbitesize\fR tag to bugs and add a comment.
.SH SYNOPSIS .SH SYNOPSIS
.B lp-bitesize \fR<\fIbug number\fR> .B bitesize \fR<\fIbug number\fR>
.br .br
.B lp-bitesize \-\-help .B bitesize \-\-help
.SH DESCRIPTION .SH DESCRIPTION
\fBlp-bitesize\fR adds a bitesize tag to the bug, if it's not there yet. It \fBbitesize\fR adds a bitesize tag to the bug, if it's not there yet. It
also adds a comment to the bug indicating that you are willing to help with also adds a comment to the bug indicating that you are willing to help with
fixing it. fixing it.
It checks for permission to operate on a given bug first, It checks for permission to operate on a given bug first,
then perform required tasks on Launchpad. then perform required tasks on Launchpad.
.SH OPTIONS .SH OPTIONS
Listed below are the command line options for \fBlp-bitesize\fR: Listed below are the command line options for \fBbitesize\fR:
.TP .TP
.BR \-h ", " \-\-help .BR \-h ", " \-\-help
Display a help message and exit. Display a help message and exit.
@ -48,7 +48,7 @@ The default value for \fB--lpinstance\fR.
.BR ubuntu\-dev\-tools (5) .BR ubuntu\-dev\-tools (5)
.SH AUTHORS .SH AUTHORS
\fBlp-bitesize\fR and this manual page were written by Daniel Holbach \fBbitesize\fR and this manual page were written by Daniel Holbach
<daniel.holbach@canonical.com>. <daniel.holbach@canonical.com>.
.PP .PP
Both are released under the terms of the GNU General Public License, version 3. Both are released under the terms of the GNU General Public License, version 3.

View File

@ -20,7 +20,7 @@ like for example \fBpbuilder\-feisty\fP, \fBpbuilder\-sid\fP, \fBpbuilder\-gutsy
.PP .PP
The same applies to \fBcowbuilder\-dist\fP, which uses cowbuilder. The main The same applies to \fBcowbuilder\-dist\fP, which uses cowbuilder. The main
difference between both is that pbuilder compresses the created chroot as a difference between both is that pbuilder compresses the created chroot as a
tarball, thus using less disc space but needing to uncompress (and possibly a tarball, thus using less disc space but needing to uncompress (and possibly
compress) its contents again on each run, and cowbuilder doesn't do this. compress) its contents again on each run, and cowbuilder doesn't do this.
.SH USAGE .SH USAGE

View File

@ -1,15 +0,0 @@
.TH running\-autopkgtests "1" "18 January 2024" "ubuntu-dev-tools"
.SH NAME
running\-autopkgtests \- dumps a list of currently running autopkgtests
.SH SYNOPSIS
.B running\-autopkgtests
.SH DESCRIPTION
Dumps a list of currently running and queued tests in Autopkgtest.
Pass --running to only see running tests, or --queued to only see
queued tests. Passing both will print both, which is the default behavior.
.SH AUTHOR
.B running\-autopkgtests
was written by Chris Peterson <chris.peterson@canonical.com>.

View File

@ -58,7 +58,7 @@ Display more progress information.
\fB\-F\fR, \fB\-\-fakesync\fR \fB\-F\fR, \fB\-\-fakesync\fR
Perform a fakesync, to work around a tarball mismatch between Debian and Perform a fakesync, to work around a tarball mismatch between Debian and
Ubuntu. Ubuntu.
This option ignores blocklisting, and performs a local sync. This option ignores blacklisting, and performs a local sync.
It implies \fB\-\-no\-lp\fR, and will leave a signed \fB.changes\fR file It implies \fB\-\-no\-lp\fR, and will leave a signed \fB.changes\fR file
for you to upload. for you to upload.
.TP .TP

View File

@ -43,7 +43,7 @@ operations.
\fB\-a\fR ARCHITECTURE, \fB\-\-arch\fR=\fIARCHITECTURE\fR \fB\-a\fR ARCHITECTURE, \fB\-\-arch\fR=\fIARCHITECTURE\fR
Rebuild or rescore a specific architecture. Valid Rebuild or rescore a specific architecture. Valid
architectures are: architectures are:
armhf, arm64, amd64, amd64v3, i386, powerpc, ppc64el, riscv64, s390x. armhf, arm64, amd64, i386, powerpc, ppc64el, riscv64, s390x.
.TP .TP
Batch processing: Batch processing:
.IP .IP
@ -66,7 +66,7 @@ Rescore builds to <priority>.
\fB\-\-arch\fR=\fIARCHITECTURE\fR \fB\-\-arch\fR=\fIARCHITECTURE\fR
Affect only 'architecture' (can be used several Affect only 'architecture' (can be used several
times). Valid architectures are: times). Valid architectures are:
armhf, arm64, amd64, amd64v3, i386, powerpc, ppc64el, riscv64, s390x. arm64, amd64, i386, powerpc, ppc64el, riscv64, s390x.
.IP .IP
\fB\-A=\fIARCHIVE\fR \fB\-A=\fIARCHIVE\fR
Act on the named archive (ppa) instead of on the main Ubuntu archive. Act on the named archive (ppa) instead of on the main Ubuntu archive.

View File

@ -150,7 +150,7 @@ def process_bugs(
err = False err = False
for bug in bugs: for bug in bugs:
ubupackage = bug.source ubupackage = package = bug.source
if package: if package:
ubupackage = package ubupackage = package
bug_num = bug.bug_num bug_num = bug.bug_num

View File

@ -22,7 +22,6 @@
# pylint: enable=invalid-name # pylint: enable=invalid-name
import sys import sys
from typing import NoReturn
from debian.changelog import Changelog from debian.changelog import Changelog
@ -31,7 +30,7 @@ from ubuntutools import getLogger
Logger = getLogger() Logger = getLogger()
def usage(exit_code: int = 1) -> NoReturn: def usage(exit_code=1):
Logger.info( Logger.info(
"""Usage: merge-changelog <left changelog> <right changelog> """Usage: merge-changelog <left changelog> <right changelog>

View File

@ -155,7 +155,6 @@ proxy="_unset_"
DEBOOTSTRAP_NO_CHECK_GPG=0 DEBOOTSTRAP_NO_CHECK_GPG=0
EATMYDATA=1 EATMYDATA=1
CCACHE=0 CCACHE=0
USE_PKGBINARYMANGLER=0
while :; do while :; do
case "$1" in case "$1" in
@ -304,27 +303,11 @@ if [ ! -w /var/lib/sbuild ]; then
# Prepare a usable default .sbuildrc # Prepare a usable default .sbuildrc
if [ ! -e ~/.sbuildrc ]; then if [ ! -e ~/.sbuildrc ]; then
cat > ~/.sbuildrc <<EOM cat > ~/.sbuildrc <<EOM
# *** THIS COMMAND IS DEPRECATED ***
#
# In sbuild 0.87.0 and later, the unshare backend is available. This is
# expected to become the default in a future release.
#
# This is the new preferred way of building Debian packages, making the manual
# creation of schroots no longer necessary. To retain the default behavior,
# you may remove this comment block and continue.
#
# To test the unshare backend while retaining the default settings, run sbuild
# with --chroot-mode=unshare like this:
# $ sbuild --chroot-mode=unshare --dist=unstable hello
#
# To switch to the unshare backend by default (recommended), uncomment the
# following lines and delete the rest of the file (with the exception of the
# last two lines):
#\$chroot_mode = 'unshare';
#\$unshare_mmdebstrap_keep_tarball = 1;
# *** VERIFY AND UPDATE \$mailto and \$maintainer_name BELOW *** # *** VERIFY AND UPDATE \$mailto and \$maintainer_name BELOW ***
# Mail address where logs are sent to (mandatory, no default!)
\$mailto = '$USER';
# Name to use as override in .changes files for the Maintainer: field # Name to use as override in .changes files for the Maintainer: field
#\$maintainer_name='$USER <$USER@localhost>'; #\$maintainer_name='$USER <$USER@localhost>';
@ -668,7 +651,6 @@ ubuntu)
if ubuntu_dist_ge "$RELEASE" "edgy"; then if ubuntu_dist_ge "$RELEASE" "edgy"; then
# Add pkgbinarymangler (edgy and later) # Add pkgbinarymangler (edgy and later)
BUILD_PKGS="$BUILD_PKGS pkgbinarymangler" BUILD_PKGS="$BUILD_PKGS pkgbinarymangler"
USE_PKGBINARYMANGLER=1
# Disable recommends for a smaller chroot (gutsy and later only) # Disable recommends for a smaller chroot (gutsy and later only)
if ubuntu_dist_ge "$RELEASE" "gutsy"; then if ubuntu_dist_ge "$RELEASE" "gutsy"; then
BUILD_PKGS="--no-install-recommends $BUILD_PKGS" BUILD_PKGS="--no-install-recommends $BUILD_PKGS"
@ -928,8 +910,8 @@ if [ -n "$TEMP_PREFERENCES" ]; then
sudo mv "$TEMP_PREFERENCES" $MNT/etc/apt/preferences.d/proposed.pref sudo mv "$TEMP_PREFERENCES" $MNT/etc/apt/preferences.d/proposed.pref
fi fi
# Copy the timezone (uncomment this if you want to use your local time zone) # Copy the timezone (comment this out if you want to leave the chroot at UTC)
#sudo cp -P --remove-destination /etc/localtime /etc/timezone "$MNT"/etc/ sudo cp -P --remove-destination /etc/localtime /etc/timezone "$MNT"/etc/
# Create a schroot entry for this chroot # Create a schroot entry for this chroot
TEMP_SCHROOTCONF=`mktemp -t schrootconf-XXXXXX` TEMP_SCHROOTCONF=`mktemp -t schrootconf-XXXXXX`
TEMPLATE_SCHROOTCONF=~/.mk-sbuild.schroot.conf TEMPLATE_SCHROOTCONF=~/.mk-sbuild.schroot.conf
@ -1048,25 +1030,6 @@ EOF
EOM EOM
fi fi
if [ "$USE_PKGBINARYMANGLER" = 1 ]; then
sudo bash -c "cat >> $MNT/finish.sh" <<EOM
mkdir -p /etc/pkgbinarymangler/
cat > /etc/pkgbinarymangler/maintainermangler.conf <<EOF
# pkgmaintainermangler configuration file
# pkgmaintainermangler will do nothing unless enable is set to "true"
enable: true
# Configure what happens if /CurrentlyBuilding is present, but invalid
# (i. e. it does not contain a Package: field). If "ignore" (default),
# the file is ignored (i. e. the Maintainer field is mangled) and a
# warning is printed. If "fail" (or any other value), pkgmaintainermangler
# exits with an error, which causes a package build to fail.
invalid_currentlybuilding: ignore
EOF
EOM
fi
if [ -n "$TARGET_ARCH" ]; then if [ -n "$TARGET_ARCH" ]; then
sudo bash -c "cat >> $MNT/finish.sh" <<EOM sudo bash -c "cat >> $MNT/finish.sh" <<EOM
# Configure target architecture # Configure target architecture
@ -1085,7 +1048,7 @@ apt-get update || true
echo set debconf/frontend Noninteractive | debconf-communicate echo set debconf/frontend Noninteractive | debconf-communicate
echo set debconf/priority critical | debconf-communicate echo set debconf/priority critical | debconf-communicate
# Install basic build tool set, trying to match buildd # Install basic build tool set, trying to match buildd
apt-get -y --force-yes -o Dpkg::Options::="--force-confold" install $BUILD_PKGS apt-get -y --force-yes install $BUILD_PKGS
# Set up expected /dev entries # Set up expected /dev entries
if [ ! -r /dev/stdin ]; then ln -s /proc/self/fd/0 /dev/stdin; fi if [ ! -r /dev/stdin ]; then ln -s /proc/self/fd/0 /dev/stdin; fi
if [ ! -r /dev/stdout ]; then ln -s /proc/self/fd/1 /dev/stdout; fi if [ ! -r /dev/stdout ]; then ln -s /proc/self/fd/1 /dev/stdout; fi

View File

@ -38,7 +38,6 @@ import shutil
import subprocess import subprocess
import sys import sys
from contextlib import suppress from contextlib import suppress
from typing import NoReturn
import debian.deb822 import debian.deb822
from distro_info import DebianDistroInfo, DistroDataOutdated, UbuntuDistroInfo from distro_info import DebianDistroInfo, DistroDataOutdated, UbuntuDistroInfo
@ -412,7 +411,7 @@ class PbuilderDist:
] + arguments ] + arguments
def show_help(exit_code: int = 0) -> NoReturn: def show_help(exit_code=0):
"""help() -> None """help() -> None
Print a help message for pbuilder-dist, and exit with the given code. Print a help message for pbuilder-dist, and exit with the given code.

View File

@ -22,7 +22,6 @@ from argparse import ArgumentParser
import yaml import yaml
from launchpadlib.launchpad import Launchpad from launchpadlib.launchpad import Launchpad
from ubuntutools.question import YesNoQuestion
from ubuntutools.utils import get_url from ubuntutools.utils import get_url
# proposed-migration is only concerned with the devel series; unlike other # proposed-migration is only concerned with the devel series; unlike other
@ -57,8 +56,10 @@ def claim_excuses_bug(launchpad, bug, package):
if our_task.assignee: if our_task.assignee:
print(f"Currently assigned to {our_task.assignee.name}") print(f"Currently assigned to {our_task.assignee.name}")
answer = YesNoQuestion().ask("Do you want to claim this bug?", "no") print("""Do you want to claim this bug? [yN] """, end="")
if answer == "yes": sys.stdout.flush()
response = sys.stdin.readline()
if response.strip().lower().startswith("y"):
our_task.assignee = launchpad.me our_task.assignee = launchpad.me
our_task.lp_save() our_task.lp_save()
return True return True
@ -130,9 +131,7 @@ def main():
if not proposed_version: if not proposed_version:
print(f"Package {args.package} not found in -proposed.") print(f"Package {args.package} not found in -proposed.")
sys.exit(1) sys.exit(1)
answer = YesNoQuestion().ask("Do you want to create a bug?", "no") create_excuses_bug(args.launchpad, args.package, proposed_version)
if answer == "yes":
create_excuses_bug(args.launchpad, args.package, proposed_version)
except ValueError as e: except ValueError as e:
sys.stderr.write(f"{e}\n") sys.stderr.write(f"{e}\n")
else: else:

View File

@ -4,7 +4,3 @@ line-length = 99
[tool.isort] [tool.isort]
line_length = 99 line_length = 99
profile = "black" profile = "black"
[tool.mypy]
disallow_incomplete_defs = true
ignore_missing_imports = true

View File

@ -46,7 +46,7 @@ Logger = getLogger()
# #
def main() -> None: def main():
# Our usage options. # Our usage options.
usage = "%(prog)s [options] <source package> [<target release> [base version]]" usage = "%(prog)s [options] <source package> [<target release> [base version]]"
parser = argparse.ArgumentParser(usage=usage) parser = argparse.ArgumentParser(usage=usage)
@ -153,7 +153,6 @@ def main() -> None:
import DNS # pylint: disable=import-outside-toplevel import DNS # pylint: disable=import-outside-toplevel
DNS.DiscoverNameServers() DNS.DiscoverNameServers()
# imported earlier, pylint: disable-next=possibly-used-before-assignment
mxlist = DNS.mxlookup(bug_mail_domain) mxlist = DNS.mxlookup(bug_mail_domain)
firstmx = mxlist[0] firstmx = mxlist[0]
mailserver_host = firstmx[1] mailserver_host = firstmx[1]
@ -215,7 +214,6 @@ def main() -> None:
if not args.release: if not args.release:
if lpapi: if lpapi:
# imported earlier, pylint: disable-next=possibly-used-before-assignment
args.release = Distribution("ubuntu").getDevelopmentSeries().name args.release = Distribution("ubuntu").getDevelopmentSeries().name
else: else:
ubu_info = UbuntuDistroInfo() ubu_info = UbuntuDistroInfo()
@ -379,7 +377,6 @@ def main() -> None:
# Map status to the values expected by LP API # Map status to the values expected by LP API
mapping = {"new": "New", "confirmed": "Confirmed"} mapping = {"new": "New", "confirmed": "Confirmed"}
# Post sync request using LP API # Post sync request using LP API
# imported earlier, pylint: disable-next=possibly-used-before-assignment
post_bug(srcpkg, subscribe, mapping[status], title, report) post_bug(srcpkg, subscribe, mapping[status], title, report)
else: else:
email_from = ubu_email(export=False)[1] email_from = ubu_email(export=False)[1]

View File

@ -183,7 +183,7 @@ def display_verbose(package, values):
Logger.info("No reverse dependencies found") Logger.info("No reverse dependencies found")
return return
def log_package(values, package, arch, dependency, visited, offset=0): def log_package(values, package, arch, dependency, offset=0):
line = f"{' ' * offset}* {package}" line = f"{' ' * offset}* {package}"
if all_archs and set(arch) != all_archs: if all_archs and set(arch) != all_archs:
line += f" [{' '.join(sorted(arch))}]" line += f" [{' '.join(sorted(arch))}]"
@ -192,9 +192,6 @@ def display_verbose(package, values):
line += " " * (30 - len(line)) line += " " * (30 - len(line))
line += f" (for {dependency})" line += f" (for {dependency})"
Logger.info(line) Logger.info(line)
if package in visited:
return
visited = visited.copy().add(package)
data = values.get(package) data = values.get(package)
if data: if data:
offset = offset + 1 offset = offset + 1
@ -205,7 +202,6 @@ def display_verbose(package, values):
rdep["Package"], rdep["Package"],
rdep.get("Architectures", all_archs), rdep.get("Architectures", all_archs),
rdep.get("Dependency"), rdep.get("Dependency"),
visited,
offset, offset,
) )
@ -227,7 +223,6 @@ def display_verbose(package, values):
rdep["Package"], rdep["Package"],
rdep.get("Architectures", all_archs), rdep.get("Architectures", all_archs),
rdep.get("Dependency"), rdep.get("Dependency"),
{package},
) )
Logger.info("") Logger.info("")

View File

@ -4,45 +4,16 @@ set -eu
# Copyright 2023, Canonical Ltd. # Copyright 2023, Canonical Ltd.
# SPDX-License-Identifier: GPL-3.0 # SPDX-License-Identifier: GPL-3.0
PYTHON_SCRIPTS=$(find . -maxdepth 1 -type f -exec grep -l '^#! */usr/bin/python3$' {} +) PYTHON_SCRIPTS=$(grep -l -r '^#! */usr/bin/python3$' .)
run_black() { echo "Running black..."
echo "Running black..." black --check --diff . $PYTHON_SCRIPTS
black -C --check --diff . ${PYTHON_SCRIPTS}
}
run_isort() { echo "Running isort..."
echo "Running isort..." isort --check-only --diff .
isort --check-only --diff .
}
run_flake8() { echo "Running flake8..."
echo "Running flake8..." flake8 --max-line-length=99 --ignore=E203,W503 . $PYTHON_SCRIPTS
flake8 --max-line-length=99 --ignore=E203,W503 . $PYTHON_SCRIPTS
}
run_mypy() { echo "Running pylint..."
echo "Running mypy..." pylint $(find * -name '*.py') $PYTHON_SCRIPTS
mypy .
mypy --scripts-are-modules $PYTHON_SCRIPTS
}
run_pylint() {
echo "Running pylint..."
pylint "$@" $(find * -name '*.py') $PYTHON_SCRIPTS
}
if test "${1-}" = "--errors-only"; then
# Run only linters that can detect real errors (ignore formatting)
run_black || true
run_isort || true
run_flake8 || true
run_mypy
run_pylint --errors-only
else
run_black
run_isort
run_flake8
run_mypy
run_pylint
fi

View File

@ -32,13 +32,13 @@ def make_pep440_compliant(version: str) -> str:
scripts = [ scripts = [
"backportpackage", "backportpackage",
"bitesize",
"check-mir", "check-mir",
"check-symbols", "check-symbols",
"dch-repeat", "dch-repeat",
"grab-merge", "grab-merge",
"grep-merges", "grep-merges",
"import-bug-from-debian", "import-bug-from-debian",
"lp-bitesize",
"merge-changelog", "merge-changelog",
"mk-sbuild", "mk-sbuild",
"pbuilder-dist", "pbuilder-dist",

View File

@ -22,7 +22,6 @@
import argparse import argparse
import fnmatch import fnmatch
import functools
import logging import logging
import os import os
import shutil import shutil
@ -144,7 +143,7 @@ def sync_dsc(
if ubuntu_ver.is_modified_in_ubuntu(): if ubuntu_ver.is_modified_in_ubuntu():
if not force: if not force:
Logger.error("--force is required to discard Ubuntu changes.") Logger.error("--force is required to discard Ubuntu changes.")
return None sys.exit(1)
Logger.warning( Logger.warning(
"Overwriting modified Ubuntu version %s, setting current version to %s", "Overwriting modified Ubuntu version %s, setting current version to %s",
@ -158,7 +157,7 @@ def sync_dsc(
src_pkg.pull() src_pkg.pull()
except DownloadError as e: except DownloadError as e:
Logger.error("Failed to download: %s", str(e)) Logger.error("Failed to download: %s", str(e))
return None sys.exit(1)
src_pkg.unpack() src_pkg.unpack()
needs_fakesync = not (need_orig or ubu_pkg.verify_orig()) needs_fakesync = not (need_orig or ubu_pkg.verify_orig())
@ -167,13 +166,13 @@ def sync_dsc(
Logger.warning("Performing a fakesync") Logger.warning("Performing a fakesync")
elif not needs_fakesync and fakesync: elif not needs_fakesync and fakesync:
Logger.error("Fakesync not required, aborting.") Logger.error("Fakesync not required, aborting.")
return None sys.exit(1)
elif needs_fakesync and not fakesync: elif needs_fakesync and not fakesync:
Logger.error( Logger.error(
"The checksums of the Debian and Ubuntu packages " "The checksums of the Debian and Ubuntu packages "
"mismatch. A fake sync using --fakesync is required." "mismatch. A fake sync using --fakesync is required."
) )
return None sys.exit(1)
if fakesync: if fakesync:
# Download Ubuntu files (override Debian source tarballs) # Download Ubuntu files (override Debian source tarballs)
@ -181,7 +180,7 @@ def sync_dsc(
ubu_pkg.pull() ubu_pkg.pull()
except DownloadError as e: except DownloadError as e:
Logger.error("Failed to download: %s", str(e)) Logger.error("Failed to download: %s", str(e))
return None sys.exit(1)
# change into package directory # change into package directory
directory = src_pkg.source + "-" + new_ver.upstream_version directory = src_pkg.source + "-" + new_ver.upstream_version
@ -266,7 +265,7 @@ def sync_dsc(
returncode = subprocess.call(cmd) returncode = subprocess.call(cmd)
if returncode != 0: if returncode != 0:
Logger.error("Source-only build with debuild failed. Please check build log above.") Logger.error("Source-only build with debuild failed. Please check build log above.")
return None sys.exit(1)
def fetch_source_pkg(package, dist, version, component, ubuntu_release, mirror): def fetch_source_pkg(package, dist, version, component, ubuntu_release, mirror):
@ -296,7 +295,7 @@ def fetch_source_pkg(package, dist, version, component, ubuntu_release, mirror):
udtexceptions.SeriesNotFoundException, udtexceptions.SeriesNotFoundException,
) as e: ) as e:
Logger.error(str(e)) Logger.error(str(e))
return None sys.exit(1)
if version is None: if version is None:
version = Version(debian_srcpkg.getVersion()) version = Version(debian_srcpkg.getVersion())
try: try:
@ -307,7 +306,7 @@ def fetch_source_pkg(package, dist, version, component, ubuntu_release, mirror):
ubuntu_version = Version("~") ubuntu_version = Version("~")
except udtexceptions.SeriesNotFoundException as e: except udtexceptions.SeriesNotFoundException as e:
Logger.error(str(e)) Logger.error(str(e))
return None sys.exit(1)
if ubuntu_version >= version: if ubuntu_version >= version:
# The LP importer is maybe out of date # The LP importer is maybe out of date
debian_srcpkg = requestsync_mail_get_debian_srcpkg(package, dist) debian_srcpkg = requestsync_mail_get_debian_srcpkg(package, dist)
@ -321,7 +320,7 @@ def fetch_source_pkg(package, dist, version, component, ubuntu_release, mirror):
ubuntu_version, ubuntu_version,
ubuntu_release, ubuntu_release,
) )
return None sys.exit(1)
if component is None: if component is None:
component = debian_srcpkg.getComponent() component = debian_srcpkg.getComponent()
@ -330,7 +329,7 @@ def fetch_source_pkg(package, dist, version, component, ubuntu_release, mirror):
return DebianSourcePackage(package, version.full_version, component, mirrors=mirrors) return DebianSourcePackage(package, version.full_version, component, mirrors=mirrors)
def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False, yes=False): def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False):
"""Copy a source package from Debian to Ubuntu using the Launchpad API.""" """Copy a source package from Debian to Ubuntu using the Launchpad API."""
ubuntu = Distribution("ubuntu") ubuntu = Distribution("ubuntu")
debian_archive = Distribution("debian").getArchive() debian_archive = Distribution("debian").getArchive()
@ -353,7 +352,7 @@ def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False, ye
"Debian version %s has not been picked up by LP yet. Please try again later.", "Debian version %s has not been picked up by LP yet. Please try again later.",
src_pkg.version, src_pkg.version,
) )
return None sys.exit(1)
try: try:
ubuntu_spph = get_ubuntu_srcpkg(src_pkg.source, ubuntu_series, ubuntu_pocket) ubuntu_spph = get_ubuntu_srcpkg(src_pkg.source, ubuntu_series, ubuntu_pocket)
@ -374,7 +373,7 @@ def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False, ye
base_version = ubuntu_version.get_related_debian_version() base_version = ubuntu_version.get_related_debian_version()
if not force and ubuntu_version.is_modified_in_ubuntu(): if not force and ubuntu_version.is_modified_in_ubuntu():
Logger.error("--force is required to discard Ubuntu changes.") Logger.error("--force is required to discard Ubuntu changes.")
return None sys.exit(1)
# Check whether a fakesync would be required. # Check whether a fakesync would be required.
if not src_pkg.dsc.compare_dsc(ubuntu_pkg.dsc): if not src_pkg.dsc.compare_dsc(ubuntu_pkg.dsc):
@ -382,7 +381,7 @@ def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False, ye
"The checksums of the Debian and Ubuntu packages " "The checksums of the Debian and Ubuntu packages "
"mismatch. A fake sync using --fakesync is required." "mismatch. A fake sync using --fakesync is required."
) )
return None sys.exit(1)
except udtexceptions.PackageNotFoundException: except udtexceptions.PackageNotFoundException:
base_version = Version("~") base_version = Version("~")
Logger.info( Logger.info(
@ -403,10 +402,9 @@ def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False, ye
if sponsoree: if sponsoree:
Logger.info("Sponsoring this sync for %s (%s)", sponsoree.display_name, sponsoree.name) Logger.info("Sponsoring this sync for %s (%s)", sponsoree.display_name, sponsoree.name)
if not yes: answer = YesNoQuestion().ask("Sync this package", "no")
answer = YesNoQuestion().ask("Sync this package", "no") if answer != "yes":
if answer != "yes": return
return
try: try:
ubuntu_archive.copyPackage( ubuntu_archive.copyPackage(
@ -421,37 +419,26 @@ def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False, ye
except HTTPError as error: except HTTPError as error:
Logger.error("HTTP Error %s: %s", error.response.status, error.response.reason) Logger.error("HTTP Error %s: %s", error.response.status, error.response.reason)
Logger.error(error.content) Logger.error(error.content)
return None sys.exit(1)
Logger.info("Request succeeded; you should get an e-mail once it is processed.") Logger.info("Request succeeded; you should get an e-mail once it is processed.")
bugs = sorted(set(bugs)) bugs = sorted(set(bugs))
if bugs: if bugs:
Logger.info("Launchpad bugs to be closed: %s", ", ".join(str(bug) for bug in bugs)) Logger.info("Launchpad bugs to be closed: %s", ", ".join(str(bug) for bug in bugs))
Logger.info("Please wait for the sync to be successful before closing bugs.") Logger.info("Please wait for the sync to be successful before closing bugs.")
if yes: answer = YesNoQuestion().ask("Close bugs", "yes")
if answer == "yes":
close_bugs(bugs, src_pkg.source, src_pkg.version.full_version, changes, sponsoree) close_bugs(bugs, src_pkg.source, src_pkg.version.full_version, changes, sponsoree)
else:
answer = YesNoQuestion().ask("Close bugs", "yes")
if answer == "yes":
close_bugs(bugs, src_pkg.source, src_pkg.version.full_version, changes, sponsoree)
@functools.lru_cache(maxsize=1) def is_blacklisted(query):
def _fetch_sync_blocklist() -> str: """Determine if package "query" is in the sync blacklist
url = "https://ubuntu-archive-team.ubuntu.com/sync-blocklist.txt" Returns tuple of (blacklisted, comments)
with urllib.request.urlopen(url) as f: blacklisted is one of False, 'CURRENT', 'ALWAYS'
sync_blocklist = f.read().decode("utf-8")
return sync_blocklist
def is_blocklisted(query):
"""Determine if package "query" is in the sync blocklist
Returns tuple of (blocklisted, comments)
blocklisted is one of False, 'CURRENT', 'ALWAYS'
""" """
series = Launchpad.distributions["ubuntu"].current_series series = Launchpad.distributions["ubuntu"].current_series
lp_comments = series.getDifferenceComments(source_package_name=query) lp_comments = series.getDifferenceComments(source_package_name=query)
blocklisted = False blacklisted = False
comments = [ comments = [
f"{c.body_text}\n -- {c.comment_author.name}" f"{c.body_text}\n -- {c.comment_author.name}"
f" {c.comment_date.strftime('%a, %d %b %Y %H:%M:%S +0000')}" f" {c.comment_date.strftime('%a, %d %b %Y %H:%M:%S +0000')}"
@ -459,34 +446,32 @@ def is_blocklisted(query):
] ]
for diff in series.getDifferencesTo(source_package_name_filter=query): for diff in series.getDifferencesTo(source_package_name_filter=query):
if diff.status == "Blacklisted current version" and blocklisted != "ALWAYS": if diff.status == "Blacklisted current version" and blacklisted != "ALWAYS":
blocklisted = "CURRENT" blacklisted = "CURRENT"
if diff.status == "Blacklisted always": if diff.status == "Blacklisted always":
blocklisted = "ALWAYS" blacklisted = "ALWAYS"
try: # Old blacklist:
sync_blocklist = _fetch_sync_blocklist() url = "https://ubuntu-archive-team.ubuntu.com/sync-blacklist.txt"
except OSError: with urllib.request.urlopen(url) as f:
print("WARNING: unable to download the sync blocklist. Erring on the side of caution.") applicable_lines = []
return ("ALWAYS", "INTERNAL ERROR: Unable to fetch sync blocklist") for line in f:
line = line.decode("utf-8")
if not line.strip():
applicable_lines = []
continue
applicable_lines.append(line)
try:
line = line[: line.index("#")]
except ValueError:
pass
source = line.strip()
if source and fnmatch.fnmatch(query, source):
comments += ["From sync-blacklist.txt:"] + applicable_lines
blacklisted = "ALWAYS"
break
applicable_lines = [] return (blacklisted, comments)
for line in sync_blocklist.splitlines():
if not line.strip():
applicable_lines = []
continue
applicable_lines.append(line)
try:
line = line[: line.index("#")]
except ValueError:
pass
source = line.strip()
if source and fnmatch.fnmatch(query, source):
comments += ["From sync-blocklist.txt:"] + applicable_lines
blocklisted = "ALWAYS"
break
return (blocklisted, comments)
def close_bugs(bugs, package, version, changes, sponsoree): def close_bugs(bugs, package, version, changes, sponsoree):
@ -523,12 +508,6 @@ def parse():
epilog = f"See {os.path.basename(sys.argv[0])}(1) for more info." epilog = f"See {os.path.basename(sys.argv[0])}(1) for more info."
parser = argparse.ArgumentParser(usage=usage, epilog=epilog) parser = argparse.ArgumentParser(usage=usage, epilog=epilog)
parser.add_argument(
"-y",
"--yes",
action="store_true",
help="Automatically sync without prompting. Use with caution and care.",
)
parser.add_argument("-d", "--distribution", help="Debian distribution to sync from.") parser.add_argument("-d", "--distribution", help="Debian distribution to sync from.")
parser.add_argument("-r", "--release", help="Specify target Ubuntu release.") parser.add_argument("-r", "--release", help="Specify target Ubuntu release.")
parser.add_argument("-V", "--debian-version", help="Specify the version to sync from.") parser.add_argument("-V", "--debian-version", help="Specify the version to sync from.")
@ -733,38 +712,36 @@ def main():
args.release, args.release,
args.debian_mirror, args.debian_mirror,
) )
if not src_pkg:
continue
blocklisted, comments = is_blocklisted(src_pkg.source) blacklisted, comments = is_blacklisted(src_pkg.source)
blocklist_fail = False blacklist_fail = False
if blocklisted: if blacklisted:
messages = [] messages = []
if blocklisted == "CURRENT": if blacklisted == "CURRENT":
Logger.debug( Logger.debug(
"Source package %s is temporarily blocklisted " "Source package %s is temporarily blacklisted "
"(blocklisted_current). " "(blacklisted_current). "
"Ubuntu ignores these for now. " "Ubuntu ignores these for now. "
"See also LP: #841372", "See also LP: #841372",
src_pkg.source, src_pkg.source,
) )
else: else:
if args.fakesync: if args.fakesync:
messages += ["Doing a fakesync, overriding blocklist."] messages += ["Doing a fakesync, overriding blacklist."]
else: else:
blocklist_fail = True blacklist_fail = True
messages += [ messages += [
"If this package needs a fakesync, use --fakesync", "If this package needs a fakesync, use --fakesync",
"If you think this package shouldn't be " "If you think this package shouldn't be "
"blocklisted, please file a bug explaining your " "blacklisted, please file a bug explaining your "
"reasoning and subscribe ~ubuntu-archive.", "reasoning and subscribe ~ubuntu-archive.",
] ]
if blocklist_fail: if blacklist_fail:
Logger.error("Source package %s is blocklisted.", src_pkg.source) Logger.error("Source package %s is blacklisted.", src_pkg.source)
elif blocklisted == "ALWAYS": elif blacklisted == "ALWAYS":
Logger.info("Source package %s is blocklisted.", src_pkg.source) Logger.info("Source package %s is blacklisted.", src_pkg.source)
if messages: if messages:
for message in messages: for message in messages:
for line in textwrap.wrap(message): for line in textwrap.wrap(message):
@ -776,17 +753,14 @@ def main():
for line in textwrap.wrap(comment): for line in textwrap.wrap(comment):
Logger.info(" %s", line) Logger.info(" %s", line)
if blocklist_fail: if blacklist_fail:
continue sys.exit(1)
if args.lp: if args.lp:
if not copy( copy(src_pkg, args.release, args.bugs, sponsoree, args.simulate, args.force)
src_pkg, args.release, args.bugs, sponsoree, args.simulate, args.force, args.yes
):
continue
else: else:
os.environ["DEB_VENDOR"] = "Ubuntu" os.environ["DEB_VENDOR"] = "Ubuntu"
if not sync_dsc( sync_dsc(
src_pkg, src_pkg,
args.distribution, args.distribution,
args.release, args.release,
@ -798,8 +772,7 @@ def main():
args.simulate, args.simulate,
args.force, args.force,
args.fakesync, args.fakesync,
): )
continue
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -87,13 +87,17 @@ def retry_builds(pkg, archs):
return f"Retrying builds of '{pkg.source_package_name}':\n{msg}" return f"Retrying builds of '{pkg.source_package_name}':\n{msg}"
def parse_args(argv: list[str], valid_archs: set[str]) -> argparse.Namespace: def main():
"""Parse command line arguments and return namespace."""
# Usage. # Usage.
usage = "%(prog)s <srcpackage> <release> <operation>\n\n" usage = "%(prog)s <srcpackage> <release> <operation>\n\n"
usage += "Where operation may be one of: rescore, retry, or status.\n" usage += "Where operation may be one of: rescore, retry, or status.\n"
usage += "Only Launchpad Buildd Admins may rescore package builds." usage += "Only Launchpad Buildd Admins may rescore package builds."
# Valid architectures.
valid_archs = set(
["armhf", "arm64", "amd64", "i386", "powerpc", "ppc64el", "riscv64", "s390x"]
)
# Prepare our option parser. # Prepare our option parser.
parser = argparse.ArgumentParser(usage=usage) parser = argparse.ArgumentParser(usage=usage)
@ -144,23 +148,7 @@ def parse_args(argv: list[str], valid_archs: set[str]) -> argparse.Namespace:
parser.add_argument("packages", metavar="package", nargs="*", help=argparse.SUPPRESS) parser.add_argument("packages", metavar="package", nargs="*", help=argparse.SUPPRESS)
# Parse our options. # Parse our options.
args = parser.parse_args(argv) args = parser.parse_args()
if not args.batch:
# Check we have the correct number of arguments.
if len(args.packages) < 3:
parser.error("Incorrect number of arguments.")
return args
def main():
# Valid architectures.
valid_archs = set(
["armhf", "arm64", "amd64", "amd64v3", "i386", "powerpc", "ppc64el", "riscv64", "s390x"]
)
args = parse_args(sys.argv[1:], valid_archs)
launchpad = Launchpad.login_with("ubuntu-dev-tools", "production", version="devel") launchpad = Launchpad.login_with("ubuntu-dev-tools", "production", version="devel")
ubuntu = launchpad.distributions["ubuntu"] ubuntu = launchpad.distributions["ubuntu"]
@ -179,13 +167,21 @@ def main():
Logger.error(error) Logger.error(error)
sys.exit(1) sys.exit(1)
else: else:
package = str(args.packages[0]).lower() # Check we have the correct number of arguments.
release = str(args.packages[1]).lower() if len(args.packages) < 3:
operation = str(args.packages[2]).lower() parser.error("Incorrect number of arguments.")
try:
package = str(args.packages[0]).lower()
release = str(args.packages[1]).lower()
operation = str(args.packages[2]).lower()
except IndexError:
parser.print_help()
sys.exit(1)
archive = launchpad.archives.getByReference(reference=args.archive) archive = launchpad.archives.getByReference(reference=args.archive)
try: try:
distroseries = ubuntu.getSeries(name_or_version=release.split("-")[0]) distroseries = ubuntu.getSeries(name_or_version=release)
except lazr.restfulclient.errors.NotFound as error: except lazr.restfulclient.errors.NotFound as error:
Logger.error(error) Logger.error(error)
sys.exit(1) sys.exit(1)
@ -238,11 +234,11 @@ def main():
# are in place. # are in place.
if operation == "retry": if operation == "retry":
necessary_privs = archive.checkUpload( necessary_privs = archive.checkUpload(
component=component, component=sources.getComponent(),
distroseries=distroseries, distroseries=distroseries,
person=launchpad.me, person=launchpad.me,
pocket=pocket, pocket=pocket,
sourcepackagename=sources.source_package_name, sourcepackagename=sources.getPackageName(),
) )
if not necessary_privs: if not necessary_privs:
Logger.error( Logger.error(

View File

@ -65,7 +65,7 @@ def main():
err = True err = True
continue continue
Logger.info("%s%s", prefix, version) Logger.info(prefix + version)
if err: if err:
sys.exit(1) sys.exit(1)

View File

@ -165,7 +165,6 @@ class SourcePackage(ABC):
series = kwargs.get("series") series = kwargs.get("series")
pocket = kwargs.get("pocket") pocket = kwargs.get("pocket")
status = kwargs.get("status") status = kwargs.get("status")
arch = kwargs.get("arch")
verify_signature = kwargs.get("verify_signature", False) verify_signature = kwargs.get("verify_signature", False)
try_binary = kwargs.get("try_binary", True) try_binary = kwargs.get("try_binary", True)
@ -185,7 +184,6 @@ class SourcePackage(ABC):
self._series = series self._series = series
self._pocket = pocket self._pocket = pocket
self._status = status self._status = status
self._arch = arch
# dscfile can be either a path or an URL. misc.py's download() will # dscfile can be either a path or an URL. misc.py's download() will
# later fiture it out # later fiture it out
self._dsc_source = dscfile self._dsc_source = dscfile
@ -254,7 +252,6 @@ class SourcePackage(ABC):
) )
try: try:
params["archtag"] = self._arch
bpph = archive.getBinaryPackage(self.source, **params) bpph = archive.getBinaryPackage(self.source, **params)
except PackageNotFoundException as bpnfe: except PackageNotFoundException as bpnfe:
# log binary lookup failure, in case it provides hints # log binary lookup failure, in case it provides hints
@ -340,9 +337,11 @@ class SourcePackage(ABC):
def _archive_servers(self): def _archive_servers(self):
"Generator for mirror and master servers" "Generator for mirror and master servers"
# Always provide the mirrors first # Always provide the mirrors first
yield from self.mirrors for server in self.mirrors:
yield server
# Don't repeat servers that are in both mirrors and masters # Don't repeat servers that are in both mirrors and masters
yield from set(self.masters) - set(self.mirrors) for server in set(self.masters) - set(self.mirrors):
yield server
def _source_urls(self, name): def _source_urls(self, name):
"Generator of sources for name" "Generator of sources for name"
@ -633,7 +632,8 @@ class DebianSourcePackage(SourcePackage):
def _source_urls(self, name): def _source_urls(self, name):
"Generator of sources for name" "Generator of sources for name"
yield from super()._source_urls(name) for url in super()._source_urls(name):
yield url
if name in self.snapshot_files: if name in self.snapshot_files:
yield self.snapshot_files[name] yield self.snapshot_files[name]
@ -731,7 +731,6 @@ class PersonalPackageArchiveSourcePackage(UbuntuSourcePackage):
class UbuntuCloudArchiveSourcePackage(PersonalPackageArchiveSourcePackage): class UbuntuCloudArchiveSourcePackage(PersonalPackageArchiveSourcePackage):
"Download / unpack an Ubuntu Cloud Archive source package" "Download / unpack an Ubuntu Cloud Archive source package"
TEAM = "ubuntu-cloud-archive" TEAM = "ubuntu-cloud-archive"
PROJECT = "cloud-archive" PROJECT = "cloud-archive"
VALID_POCKETS = ["updates", "proposed", "staging"] VALID_POCKETS = ["updates", "proposed", "staging"]
@ -928,8 +927,8 @@ class UbuntuCloudArchiveSourcePackage(PersonalPackageArchiveSourcePackage):
class _WebJSON: class _WebJSON:
def getHostUrl(self): def getHostUrl(self): # pylint: disable=no-self-use
raise NotImplementedError(f"{self.__class__.__name__}.getHostUrl() is not implemented") raise Exception("Not implemented")
def load(self, path=""): def load(self, path=""):
reader = codecs.getreader("utf-8") reader = codecs.getreader("utf-8")

View File

@ -50,7 +50,7 @@ class UDTConfig:
"KEYID": None, "KEYID": None,
} }
# Populated from the configuration files: # Populated from the configuration files:
config: dict[str, str] = {} config = {}
def __init__(self, no_conf=False, prefix=None): def __init__(self, no_conf=False, prefix=None):
self.no_conf = no_conf self.no_conf = no_conf
@ -61,26 +61,28 @@ class UDTConfig:
self.config = self.parse_devscripts_config() self.config = self.parse_devscripts_config()
@staticmethod @staticmethod
def parse_devscripts_config() -> dict[str, str]: def parse_devscripts_config():
"""Read the devscripts configuration files, and return the values as a """Read the devscripts configuration files, and return the values as a
dictionary dictionary
""" """
config = {} config = {}
for filename in ("/etc/devscripts.conf", "~/.devscripts"): for filename in ("/etc/devscripts.conf", "~/.devscripts"):
try: try:
with open(os.path.expanduser(filename), "r", encoding="utf-8") as f: f = open(os.path.expanduser(filename), "r", encoding="utf-8")
content = f.read()
except IOError: except IOError:
continue continue
try: for line in f:
tokens = shlex.split(content, comments=True) parsed = shlex.split(line, comments=True)
except ValueError as e: if len(parsed) > 1:
Logger.error("Error parsing %s: %s", filename, e) Logger.warning(
continue "Cannot parse variable assignment in %s: %s",
for token in tokens: getattr(f, "name", "<config>"),
if "=" in token: line,
key, value = token.split("=", 1) )
if len(parsed) >= 1 and "=" in parsed[0]:
key, value = parsed[0].split("=", 1)
config[key] = value config[key] = value
f.close()
return config return config
def get_value(self, key, default=None, boolean=False, compat_keys=()): def get_value(self, key, default=None, boolean=False, compat_keys=()):

View File

@ -26,7 +26,6 @@ import logging
import os import os
import re import re
from copy import copy from copy import copy
from typing import Any
from urllib.error import URLError from urllib.error import URLError
from urllib.parse import urlparse from urllib.parse import urlparse
@ -140,7 +139,7 @@ class BaseWrapper(metaclass=MetaWrapper):
A base class from which other wrapper classes are derived. A base class from which other wrapper classes are derived.
""" """
resource_type: str | tuple[str, str] = "" # it's a base class after all resource_type: str = None # it's a base class after all
def __new__(cls, data): def __new__(cls, data):
if isinstance(data, str) and data.startswith(str(Launchpad._root_uri)): if isinstance(data, str) and data.startswith(str(Launchpad._root_uri)):
@ -291,8 +290,9 @@ class Distribution(BaseWrapper):
Returns a list of all DistroSeries objects. Returns a list of all DistroSeries objects.
""" """
if not self._have_all_series: if not self._have_all_series:
for series in self.series: for series in Launchpad.load(self.series_collection_link).entries:
self._cache_series(DistroSeries(series)) series_link = DistroSeries(series["self_link"])
self._cache_series(series_link)
self._have_all_series = True self._have_all_series = True
allseries = filter(lambda s: s.active, self._series.values()) allseries = filter(lambda s: s.active, self._series.values())
@ -668,19 +668,20 @@ class Archive(BaseWrapper):
rversion = getattr(record, "binary_package_version", None) rversion = getattr(record, "binary_package_version", None)
else: else:
rversion = getattr(record, "source_package_version", None) rversion = getattr(record, "source_package_version", None)
skipmsg = f"Skipping version {rversion}: "
if record.pocket not in pockets: if record.pocket not in pockets:
err_msg = f"pocket {record.pocket} not in ({','.join(pockets)})" err_msg = f"pocket {record.pocket} not in ({','.join(pockets)})"
Logger.debug("Skipping version %s: %s", rversion, err_msg) Logger.debug(skipmsg + err_msg)
continue continue
if record.status not in statuses: if record.status not in statuses:
err_msg = f"status {record.status} not in ({','.join(statuses)})" err_msg = f"status {record.status} not in ({','.join(statuses)})"
Logger.debug("Skipping version %s: %s", rversion, err_msg) Logger.debug(skipmsg + err_msg)
continue continue
release = wrapper(record) release = wrapper(record)
if binary and archtag and archtag != release.arch: if binary and archtag and archtag != release.arch:
err_msg = f"arch {release.arch} does not match requested arch {archtag}" err_msg = f"arch {release.arch} does not match requested arch {archtag}"
Logger.debug("Skipping version %s: %s", rversion, err_msg) Logger.debug(skipmsg + err_msg)
continue continue
# results are ordered so first is latest # results are ordered so first is latest
cache[index] = release cache[index] = release
@ -1405,7 +1406,10 @@ class PersonTeam(BaseWrapper, metaclass=MetaPersonTeam):
def getPPAs(self): def getPPAs(self):
if self._ppas is None: if self._ppas is None:
ppas = [Archive(ppa) for ppa in self._lpobject.ppas] ppas = [
Archive(ppa["self_link"])
for ppa in Launchpad.load(self._lpobject.ppas_collection_link).entries
]
self._ppas = {ppa.name: ppa for ppa in ppas} self._ppas = {ppa.name: ppa for ppa in ppas}
return self._ppas return self._ppas
@ -1430,7 +1434,10 @@ class Project(BaseWrapper):
The list will be sorted by date_created, in descending order. The list will be sorted by date_created, in descending order.
""" """
if not self._series: if not self._series:
series = [ProjectSeries(s) for s in self._lpobject.series] series = [
ProjectSeries(s["self_link"])
for s in Launchpad.load(self._lpobject.series_collection_link).entries
]
self._series = sorted(series, key=lambda s: s.date_created, reverse=True) self._series = sorted(series, key=lambda s: s.date_created, reverse=True)
return self._series.copy() return self._series.copy()
@ -1502,7 +1509,7 @@ class Packageset(BaseWrapper): # pylint: disable=too-few-public-methods
resource_type = "packageset" resource_type = "packageset"
_lp_packagesets = None _lp_packagesets = None
_source_sets: dict[tuple[str, str | None, bool], Any] = {} _source_sets = {}
@classmethod @classmethod
def setsIncludingSource(cls, sourcepackagename, distroseries=None, direct_inclusion=False): def setsIncludingSource(cls, sourcepackagename, distroseries=None, direct_inclusion=False):

View File

@ -340,7 +340,6 @@ class PullPkg:
params = {} params = {}
params["package"] = options["package"] params["package"] = options["package"]
params["arch"] = options["arch"]
if options["release"]: if options["release"]:
(release, version, pocket) = self.parse_release_and_version( (release, version, pocket) = self.parse_release_and_version(
@ -436,7 +435,7 @@ class PullPkg:
if options["upload_queue"]: if options["upload_queue"]:
# upload queue API is different/simpler # upload queue API is different/simpler
self.pull_upload_queue( # pylint: disable=missing-kwoa self.pull_upload_queue( # pylint: disable=missing-kwoa
pull, download_only=options["download_only"], **params pull, arch=options["arch"], download_only=options["download_only"], **params
) )
return return
@ -471,7 +470,6 @@ class PullPkg:
uri, uri,
) )
vcscmd = ""
if vcs == "Bazaar": if vcs == "Bazaar":
vcscmd = " $ bzr branch " + uri vcscmd = " $ bzr branch " + uri
elif vcs == "Git": elif vcs == "Git":

View File

@ -62,17 +62,8 @@ def get_debian_srcpkg(name, release):
return DebianSourcePackage(package=name, series=release).lp_spph return DebianSourcePackage(package=name, series=release).lp_spph
def get_ubuntu_srcpkg(name, release, pocket="Proposed"): def get_ubuntu_srcpkg(name, release):
srcpkg = UbuntuSourcePackage(package=name, series=release, pocket=pocket) return UbuntuSourcePackage(package=name, series=release).lp_spph
try:
return srcpkg.lp_spph
except PackageNotFoundException:
if pocket != "Release":
parent_pocket = "Release"
if pocket == "Updates":
parent_pocket = "Proposed"
return get_ubuntu_srcpkg(name, release, parent_pocket)
raise
def need_sponsorship(name, component, release): def need_sponsorship(name, component, release):

View File

@ -16,7 +16,6 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import sys import sys
from typing import NoReturn
from ubuntutools.question import Question, YesNoQuestion from ubuntutools.question import Question, YesNoQuestion
@ -43,7 +42,7 @@ def ask_for_manual_fixing():
user_abort() user_abort()
def user_abort() -> NoReturn: def user_abort():
"""Print abort and quit the program.""" """Print abort and quit the program."""
print("User abort.") print("User abort.")

View File

@ -17,7 +17,6 @@
import logging import logging
import os import os
import pathlib
import re import re
import subprocess import subprocess
import sys import sys
@ -408,16 +407,22 @@ class SourcePackage:
return True return True
def _run_lintian(self) -> str: def _run_lintian(self):
"""Runs lintian on either the source or binary changes file. """Runs lintian on either the source or binary changes file.
Returns the filename of the created lintian output file. Returns the filename of the created lintian output file.
""" """
# Determine whether to use the source or binary build for lintian # Determine whether to use the source or binary build for lintian
package_and_version = f"{self._package}_{strip_epoch(self._version)}"
if self._build_log: if self._build_log:
build_changes = f"{package_and_version}_{self._builder.get_architecture()}.changes" build_changes = (
self._package
+ "_"
+ strip_epoch(self._version)
+ "_"
+ self._builder.get_architecture()
+ ".changes"
)
changes_for_lintian = os.path.join(self._buildresult, build_changes) changes_for_lintian = os.path.join(self._buildresult, build_changes)
else: else:
changes_for_lintian = self._changes_file changes_for_lintian = self._changes_file
@ -425,12 +430,18 @@ class SourcePackage:
# Check lintian # Check lintian
assert os.path.isfile(changes_for_lintian), f"{changes_for_lintian} does not exist." assert os.path.isfile(changes_for_lintian), f"{changes_for_lintian} does not exist."
cmd = ["lintian", "-IE", "--pedantic", "-q", "--profile", "ubuntu", changes_for_lintian] cmd = ["lintian", "-IE", "--pedantic", "-q", "--profile", "ubuntu", changes_for_lintian]
lintian_file = pathlib.Path(self._workdir) / f"{package_and_version}.lintian" lintian_filename = os.path.join(
Logger.debug("%s > %s", " ".join(cmd), lintian_file) self._workdir, self._package + "_" + strip_epoch(self._version) + ".lintian"
with lintian_file.open("wb") as outfile: )
subprocess.run(cmd, stdout=outfile, check=True) Logger.debug("%s > %s", " ".join(cmd), lintian_filename)
report = subprocess.check_output(cmd, encoding="utf-8")
return str(lintian_file) # write lintian report file
lintian_file = open(lintian_filename, "w", encoding="utf-8")
lintian_file.writelines(report)
lintian_file.close()
return lintian_filename
def sync(self, upload, series, bug_number, requester): def sync(self, upload, series, bug_number, requester):
"""Does a sync of the source package.""" """Does a sync of the source package."""

View File

@ -46,9 +46,11 @@ def is_command_available(command, check_sbin=False):
def check_dependencies(): def check_dependencies():
"Do we have all the commands we need for full functionality?" "Do we have all the commands we need for full functionality?"
missing = [] missing = []
for cmd in ("patch", "quilt", "dput", "lintian"): for cmd in ("patch", "bzr", "quilt", "dput", "lintian"):
if not is_command_available(cmd): if not is_command_available(cmd):
missing.append(cmd) missing.append(cmd)
if not is_command_available("bzr-buildpackage"):
missing.append("bzr-builddeb")
if not any( if not any(
is_command_available(cmd, check_sbin=True) for cmd in ("pbuilder", "sbuild", "cowbuilder") is_command_available(cmd, check_sbin=True) for cmd in ("pbuilder", "sbuild", "cowbuilder")
): ):
@ -210,14 +212,14 @@ def get_open_ubuntu_bug_task(launchpad, bug, branch=None):
sys.exit(1) sys.exit(1)
elif len(ubuntu_tasks) == 1: elif len(ubuntu_tasks) == 1:
task = ubuntu_tasks[0] task = ubuntu_tasks[0]
elif branch and branch[1] == "ubuntu": if len(ubuntu_tasks) > 1 and branch and branch[1] == "ubuntu":
tasks = [t for t in ubuntu_tasks if t.get_series() == branch[2] and t.package == branch[3]] tasks = [t for t in ubuntu_tasks if t.get_series() == branch[2] and t.package == branch[3]]
if len(tasks) > 1: if len(tasks) > 1:
# A bug targeted to the development series? # A bug targeted to the development series?
tasks = [t for t in tasks if t.series is not None] tasks = [t for t in tasks if t.series is not None]
assert len(tasks) == 1 assert len(tasks) == 1
task = tasks[0] task = tasks[0]
else: elif len(ubuntu_tasks) > 1:
task_list = [t.get_short_info() for t in ubuntu_tasks] task_list = [t.get_short_info() for t in ubuntu_tasks]
Logger.debug( Logger.debug(
"%i Ubuntu tasks exist for bug #%i.\n%s", "%i Ubuntu tasks exist for bug #%i.\n%s",

View File

@ -60,7 +60,7 @@ class ExamplePackage:
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
self._create(Path(tmpdir)) self._create(Path(tmpdir))
def _create(self, directory: Path) -> None: def _create(self, directory: Path):
pkgdir = directory / self.dirname pkgdir = directory / self.dirname
pkgdir.mkdir() pkgdir.mkdir()
(pkgdir / self.content_filename).write_text(self.content_text) (pkgdir / self.content_filename).write_text(self.content_text)

View File

@ -28,7 +28,6 @@ class BinaryTests(unittest.TestCase):
def test_keyring_installed(self): def test_keyring_installed(self):
"""Smoke test for required lp api dependencies""" """Smoke test for required lp api dependencies"""
try: try:
# pylint: disable-next=import-outside-toplevel,unused-import
import keyring # noqa: F401 import keyring # noqa: F401
except ModuleNotFoundError as error: except ModuleNotFoundError:
raise ModuleNotFoundError("package python3-keyring is not installed") from error raise ModuleNotFoundError("package python3-keyring is not installed")

View File

@ -12,7 +12,7 @@
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR # 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 # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
"""Tests for running_autopkgtests """ Tests for running_autopkgtests
Tests using cached data from autopkgtest servers. Tests using cached data from autopkgtest servers.
These tests only ensure code changes don't change parsing behavior These tests only ensure code changes don't change parsing behavior