ubuntu-dev-tools/requestsync
Benjamin Drung 909d945af4 Replace deprecated optparse with argparse
Signed-off-by: Benjamin Drung <benjamin.drung@canonical.com>
2023-01-31 13:33:18 +01:00

412 lines
13 KiB
Python
Executable File

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# (C) 2007 Canonical Ltd., Steve Kowalik
# Authors:
# Martin Pitt <martin.pitt@ubuntu.com>
# Steve Kowalik <stevenk@ubuntu.com>
# Michael Bienia <geser@ubuntu.com>
# Daniel Hahler <ubuntu@thequod.de>
# Iain Lane <laney@ubuntu.com>
# Jonathan Davies <jpds@ubuntu.com>
# Markus Korn <thekorn@gmx.de> (python-launchpadlib support)
#
# ##################################################################
#
# 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 2.
#
# 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.
#
# See file /usr/share/common-licenses/GPL-2 for more details.
#
# ##################################################################
import argparse
import os
import sys
from distro_info import UbuntuDistroInfo
from ubuntutools import getLogger
from ubuntutools.config import UDTConfig, ubu_email
from ubuntutools.lp import udtexceptions
from ubuntutools.misc import require_utf8
from ubuntutools.question import EditBugReport, confirmation_prompt
from ubuntutools.version import Version
Logger = getLogger()
#
# entry point
#
def main():
# Our usage options.
usage = "%(prog)s [options] <source package> [<target release> [base version]]"
parser = argparse.ArgumentParser(usage=usage)
parser.add_argument(
"-d", dest="dist", default="unstable", help="Debian distribution to sync from."
)
parser.add_argument(
"-k",
dest="keyid",
default=None,
help="GnuPG key ID to use for signing report "
"(only used when emailing the sync request).",
)
parser.add_argument(
"-n",
action="store_true",
dest="newpkg",
default=False,
help="Whether package to sync is a new package in Ubuntu.",
)
parser.add_argument(
"--email",
action="store_true",
default=False,
help="Use a PGP-signed email for filing the sync request, rather than the LP API.",
)
parser.add_argument(
"--lp",
dest="deprecated_lp_flag",
action="store_true",
default=False,
help=argparse.SUPPRESS,
)
parser.add_argument(
"-l",
"--lpinstance",
metavar="INSTANCE",
dest="lpinstance",
default=None,
help="Launchpad instance to connect to (default: production).",
)
parser.add_argument(
"-s", action="store_true", dest="sponsorship", default=False, help="Force sponsorship"
)
parser.add_argument(
"-C",
action="store_true",
dest="missing_changelog_ok",
default=False,
help="Allow changelog to be manually filled in when missing",
)
parser.add_argument(
"-e",
action="store_true",
dest="ffe",
default=False,
help="Use this after FeatureFreeze for non-bug fix "
"syncs, changes default subscription to the "
"appropriate release team.",
)
parser.add_argument(
"--no-conf",
action="store_true",
dest="no_conf",
default=False,
help="Don't read config files or environment variables",
)
parser.add_argument("source_package", help=argparse.SUPPRESS)
parser.add_argument("release", nargs="?", help=argparse.SUPPRESS)
parser.add_argument("base_version", nargs="?", type=Version, help=argparse.SUPPRESS)
args = parser.parse_args()
require_utf8()
config = UDTConfig(args.no_conf)
if args.deprecated_lp_flag:
Logger.info("The --lp flag is now default, ignored.")
if args.email:
args.lpapi = False
else:
args.lpapi = config.get_value("USE_LPAPI", default=True, boolean=True)
if args.lpinstance is None:
args.lpinstance = config.get_value("LPINSTANCE")
if args.keyid is None:
args.keyid = config.get_value("KEYID")
if not args.lpapi:
if args.lpinstance == "production":
bug_mail_domain = "bugs.launchpad.net"
elif args.lpinstance == "staging":
bug_mail_domain = "bugs.staging.launchpad.net"
else:
Logger.error("Error: Unknown launchpad instance: %s", args.lpinstance)
sys.exit(1)
mailserver_host = config.get_value(
"SMTP_SERVER", default=None, compat_keys=["UBUSMTP", "DEBSMTP"]
)
if not args.lpapi and not mailserver_host:
try:
import DNS
DNS.DiscoverNameServers()
mxlist = DNS.mxlookup(bug_mail_domain)
firstmx = mxlist[0]
mailserver_host = firstmx[1]
except ImportError:
Logger.error("Please install python-dns to support Launchpad mail server lookup.")
sys.exit(1)
mailserver_port = config.get_value(
"SMTP_PORT", default=25, compat_keys=["UBUSMTP_PORT", "DEBSMTP_PORT"]
)
mailserver_user = config.get_value("SMTP_USER", compat_keys=["UBUSMTP_USER", "DEBSMTP_USER"])
mailserver_pass = config.get_value("SMTP_PASS", compat_keys=["UBUSMTP_PASS", "DEBSMTP_PASS"])
# import the needed requestsync module
if args.lpapi:
from ubuntutools.lp.lpapicache import Distribution, Launchpad
from ubuntutools.requestsync.lp import (
check_existing_reports,
get_debian_srcpkg,
get_ubuntu_delta_changelog,
get_ubuntu_srcpkg,
need_sponsorship,
post_bug,
)
# See if we have LP credentials and exit if we don't -
# cannot continue in this case
try:
# devel for changelogUrl()
Launchpad.login(service=args.lpinstance, api_version="devel")
except IOError:
sys.exit(1)
else:
from ubuntutools.requestsync.mail import (
check_existing_reports,
get_debian_srcpkg,
get_ubuntu_delta_changelog,
get_ubuntu_srcpkg,
mail_bug,
need_sponsorship,
)
if not any(x in os.environ for x in ("UBUMAIL", "DEBEMAIL", "EMAIL")):
Logger.error(
"The environment variable UBUMAIL, DEBEMAIL or EMAIL needs "
"to be set to let this script mail the sync request."
)
sys.exit(1)
newsource = args.newpkg
sponsorship = args.sponsorship
distro = args.dist
ffe = args.ffe
lpapi = args.lpapi
need_interaction = False
srcpkg = args.source_package
if not args.release:
if lpapi:
args.release = Distribution("ubuntu").getDevelopmentSeries().name
else:
ubu_info = UbuntuDistroInfo()
args.release = ubu_info.devel()
Logger.warning("Target release missing - assuming %s", args.release)
# Get the current Ubuntu source package
try:
ubuntu_srcpkg = get_ubuntu_srcpkg(srcpkg, args.release, "Proposed")
ubuntu_version = Version(ubuntu_srcpkg.getVersion())
ubuntu_component = ubuntu_srcpkg.getComponent()
newsource = False # override the -n flag
except udtexceptions.PackageNotFoundException:
ubuntu_srcpkg = None
ubuntu_version = Version("~")
ubuntu_component = None # Set after getting the Debian info
if not newsource:
Logger.info("'%s' doesn't exist in 'Ubuntu %s'.", srcpkg, args.release)
Logger.info("Do you want to sync a new package?")
confirmation_prompt()
newsource = True
except udtexceptions.SeriesNotFoundException as error:
Logger.error(error)
sys.exit(1)
# Get the requested Debian source package
try:
debian_srcpkg = get_debian_srcpkg(srcpkg, distro)
debian_version = Version(debian_srcpkg.getVersion())
debian_component = debian_srcpkg.getComponent()
except udtexceptions.PackageNotFoundException as error:
Logger.error(error)
sys.exit(1)
except udtexceptions.SeriesNotFoundException as error:
Logger.error(error)
sys.exit(1)
if ubuntu_component is None:
if debian_component == "main":
ubuntu_component = "universe"
else:
ubuntu_component = "multiverse"
# Stop if Ubuntu has already the version from Debian or a newer version
if (ubuntu_version >= debian_version) and args.lpapi:
# try rmadison
import ubuntutools.requestsync.mail
try:
debian_srcpkg = ubuntutools.requestsync.mail.get_debian_srcpkg(srcpkg, distro)
debian_version = Version(debian_srcpkg.getVersion())
debian_component = debian_srcpkg.getComponent()
except udtexceptions.PackageNotFoundException as error:
Logger.error(error)
sys.exit(1)
if ubuntu_version == debian_version:
Logger.error(
"The versions in Debian and Ubuntu are the same already (%s). Aborting.",
ubuntu_version,
)
sys.exit(1)
if ubuntu_version > debian_version:
Logger.error(
"The version in Ubuntu (%s) is newer than the version in Debian (%s). Aborting.",
ubuntu_version,
debian_version,
)
sys.exit(1)
# -s flag not specified - check if we do need sponsorship
if not sponsorship:
sponsorship = need_sponsorship(srcpkg, ubuntu_component, args.release)
if not sponsorship and not ffe:
Logger.error(
"Consider using syncpackage(1) for syncs that "
"do not require feature freeze exceptions."
)
# Check for existing package reports
if not newsource:
check_existing_reports(srcpkg)
# Generate bug report
pkg_to_sync = "%s %s (%s) from Debian %s (%s)" % (
srcpkg,
debian_version,
ubuntu_component,
distro,
debian_component,
)
title = "Sync %s" % pkg_to_sync
if ffe:
title = "FFe: " + title
report = "Please sync %s\n\n" % pkg_to_sync
if "ubuntu" in str(ubuntu_version):
need_interaction = True
Logger.info("Changes have been made to the package in Ubuntu.")
Logger.info("Please edit the report and give an explanation.")
Logger.info("Not saving the report file will abort the request.")
report += (
"Explanation of the Ubuntu delta and why it can be "
"dropped:\n%s\n>>> ENTER_EXPLANATION_HERE <<<\n\n"
% get_ubuntu_delta_changelog(ubuntu_srcpkg)
)
if ffe:
need_interaction = True
Logger.info("To approve FeatureFreeze exception, you need to state")
Logger.info("the reason why you feel it is necessary.")
Logger.info("Not saving the report file will abort the request.")
report += "Explanation of FeatureFreeze exception:\n>>> ENTER_EXPLANATION_HERE <<<\n\n"
if need_interaction:
confirmation_prompt()
base_version = args.base_version or ubuntu_version
if newsource:
report += "All changelog entries:\n\n"
else:
report += "Changelog entries since current %s version %s:\n\n" % (
args.release,
ubuntu_version,
)
changelog = debian_srcpkg.getChangelog(since_version=base_version)
if not changelog:
if not args.missing_changelog_ok:
Logger.error(
"Did not retrieve any changelog entries. "
"Do you need to specify '-C'? "
"Was the package recently uploaded? (check "
"http://packages.debian.org/changelogs/)"
)
sys.exit(1)
else:
need_interaction = True
changelog = "XXX FIXME: add changelog here XXX"
report += changelog
editor = EditBugReport(title, report)
editor.edit(optional=not need_interaction)
title, report = editor.get_report()
if "XXX FIXME" in report:
Logger.error(
"changelog boilerplate found in report, "
"please manually add changelog when using '-C'"
)
sys.exit(1)
# bug status and bug subscriber
status = "confirmed"
subscribe = "ubuntu-archive"
if sponsorship:
status = "new"
subscribe = "ubuntu-sponsors"
if ffe:
status = "new"
subscribe = "ubuntu-release"
srcpkg = not newsource and srcpkg or None
if lpapi:
# Map status to the values expected by LP API
mapping = {"new": "New", "confirmed": "Confirmed"}
# Post sync request using LP API
post_bug(srcpkg, subscribe, mapping[status], title, report)
else:
email_from = ubu_email(export=False)[1]
# Mail sync request
mail_bug(
srcpkg,
subscribe,
status,
title,
report,
bug_mail_domain,
args.keyid,
email_from,
mailserver_host,
mailserver_port,
mailserver_user,
mailserver_pass,
)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
Logger.error("User abort.")
sys.exit(2)