[devscripts] 02/02: debpatch: rewrite to use debian.changelog python module
Ximin Luo
infinity0 at debian.org
Tue Apr 18 18:49:50 UTC 2017
This is an automated email from the git hooks/post-receive script.
infinity0 pushed a commit to branch pu/debpatch
in repository devscripts.
commit 290b7ffbe10e50a35b3e369dd4db7a101ae8e9cd
Author: Ximin Luo <infinity0 at debian.org>
Date: Tue Apr 18 20:49:21 2017 +0200
debpatch: rewrite to use debian.changelog python module
This allows us to avoid dpkg-parsechangelog and mostly dch.
Also tidy up some other things based on reviewer feedback.
---
debian/copyright | 20 +-------
scripts/debpatch | 139 ++++++++++++++++++++---------------------------------
scripts/debpatch.1 | 4 ++
3 files changed, 59 insertions(+), 104 deletions(-)
diff --git a/debian/copyright b/debian/copyright
index 438c48e..f3f40dc 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -21,8 +21,8 @@ the GPL, version 2 or later.
- annotate-output, debdiff and nmudiff are released under version 2
(only) of the GPL.
-- debsnap, diff2patches and git-deborig are released under version 3
- or later of the GPL
+- debpatch, debsnap, diff2patches and git-deborig are released under
+ version 3 or later of the GPL
- deb-reversion is under the Artistic License version 2.0.
@@ -82,22 +82,6 @@ License: GPL-3+
On Debian systems, the complete text of the GNU General Public License
version 3 can be found in the /usr/share/common-licenses/GPL-3 file.
-Files: scripts/debpatch*
-Copyright: 2016 Ximin Luo <infinity0 at debian.org>
-License: GPL-3+
- 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; either version 3 of the License, or
- (at your option) any later version.
- .
- 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.
- .
- On Debian systems, the complete text of the GNU General Public License
- version 3 can be found in the /usr/share/common-licenses/GPL-3 file.
-
Files: doc/suspicious-source.1
doc/wrap-and-sort.1
scripts/devscripts/*
diff --git a/scripts/debpatch b/scripts/debpatch
index fd861e3..578392a 100755
--- a/scripts/debpatch
+++ b/scripts/debpatch
@@ -20,6 +20,7 @@ Depends on dpkg-dev, devscripts, python3-unidiff, quilt.
"""
import argparse
+import email.utils
import hashlib
import io
import logging
@@ -30,6 +31,9 @@ import shutil
import subprocess
import sys
import tempfile
+import time
+
+from debian.changelog import Changelog, ChangeBlock
dirname = os.path.dirname
basename = os.path.basename
@@ -40,22 +44,6 @@ DCH_DUMMY_TAIL = "\n -- debpatch dummy tool <infinity0 at debian.org> Thu, 01 Jan
TRY_ENCODINGS = ["utf-8", "latin-1"]
DISTRIBUTION_DEFAULT = "experimental"
-def parse_dch(dch_str, *args):
- return subprocess.run(
- ["dpkg-parsechangelog", "-l-", "-c1"] + list(args),
- input=dch_str,
- check=True,
- universal_newlines=True,
- stdout=subprocess.PIPE,
- ).stdout.rstrip()
-
-def read_dch(dch_str):
- dch = {}
- for i in ("Version", "Distribution", "Urgency", "Maintainer"):
- dch[i] = parse_dch(dch_str, "-S"+i)
- dch["Changes"] = "".join(parse_dch(dch_str, "-SChanges").splitlines(True)[3:])
- return dch
-
def is_dch(path):
return (basename(path) == 'changelog'
and basename(dirname(path)) == 'debian'
@@ -72,68 +60,42 @@ def read_dch_patch(dch_patch):
target_str = hunk_lines_to_str(hunk.target_lines())
# here we assume the debdiff has enough context to see the previous version
# this should be true all the time in practice
- source_version = parse_dch(source_str, "-SVersion")
- target = read_dch(target_str)
+ source_version = str(Changelog(source_str, 1)[0].version)
+ target = Changelog(target_str, 1)[0]
return source_version, target
def apply_dch_patch(source_file, current, patch_name, old_version, target, dry_run):
- # Do not change this text, unless you also add logic to detect markers from
- # previously-released versions.
- marker = "Patch %s applied by debpatch(1)." % patch_name
- if marker in current["Changes"]:
- logging.info("patch %s already applied to d/changelog", patch_name)
- return target["Version"]
-
+ target_version = str(target.version)
dch_args = []
dch_env = dict(os.environ)
- if target["Distribution"] == "UNRELEASED":
- # UNRELEASED causes hard-to-reason-about behaviours in dch, let's avoid that
- newdist = current["Distribution"] if current["Distribution"] != "UNRELEASED" else DISTRIBUTION_DEFAULT
- logging.info("using distribution '%s' instead of 'UNRELEASED'", newdist)
- target["Distribution"] = newdist
-
- if not old_version or not target["Version"].startswith(old_version):
- logging.warn("don't know how to reapply version-change %s to %s" %
- (old_version, target["Version"]))
- version = subprocess.check_output(["sh", "-c",
- "EDITOR=cat dch -n 2>/dev/null | dpkg-parsechangelog -l- -SVersion"
- ]).decode("utf-8").rstrip()
+ if not old_version or not target_version.startswith(old_version):
+ logging.warn("don't know how to rebase version-change (%s => %s) onto %s" %
+ (old_version, target_version, old_version))
+ newlog = subprocess.getoutput("EDITOR=cat dch -n 2>/dev/null").rstrip()
+ version = str(Changelog(newlog, 1)[0].version)
logging.warn("using version %s based on `dch -n`; feel free to make me smarter", version)
else:
- version_suffix = target["Version"][len(old_version):]
- version = current["Version"] + version_suffix
+ version_suffix = target_version[len(old_version):]
+ version = str(current[0].version) + version_suffix
logging.info("using version %s based on suffix %s", version, version_suffix)
if dry_run:
return version
- dch_args += ["-v", version]
- dch_args += ["--force-distribution", "-D", target["Distribution"]]
- dch_args += ["-u", target["Urgency"]]
- if "Maintainer" in target:
- dch_env["DEBEMAIL"] = target["Maintainer"]
- del dch_env["DEBFULLNAME"]
-
- changes = target["Changes"]
- if changes.lstrip().startswith("["):
- changes = "\n" + changes
+ current._blocks.insert(0, target)
+ current.set_version(version)
- token = "DEBPATCH PLACEHOLDER %s DELETEME" % random.randint(0, 2**64)
- shutil.copy(source_file, source_file + ".debpatch.bak")
+ shutil.copy(source_file, source_file + ".new")
try:
- C(["dch", "-c", source_file] + dch_args + [token])
- C(["dch", "-c", source_file, "-a", marker], )
- C(["sed", "-e", "/%s/c\\\n%s" % (token, changes.replace("\n", "\\\n")), "-i", source_file])
+ with open(source_file + ".new", "w") as fp:
+ current.write_to_open_file(fp)
+ os.rename(source_file + ".new", source_file)
except:
- os.rename(source_file, source_file + ".debpatch.err")
logging.warn("failed to patch %s", source_file)
- logging.warn("half-applied changes in %s", source_file + ".debpatch.err")
+ logging.warn("half-applied changes in %s", source_file + ".new")
logging.warn("current working directory is %s", os.getcwd())
- os.rename(source_file + ".debpatch.bak", source_file)
raise
- else:
- os.unlink(source_file + ".debpatch.bak")
def call_patch(patch_str, *args, check=True, **kwargs):
return subprocess.run(
@@ -152,34 +114,27 @@ def check_patch(patch_str, *args, **kwargs):
stderr=subprocess.DEVNULL,
**kwargs).returncode == 0
-def apply_patch_str(patch_name, patch_str):
- if check_patch(patch_str, "-N"):
- call_patch(patch_str)
- logging.info("patch %s applies!", patch_name)
- elif check_patch(patch_str, "-R"):
- logging.info("patch %s already applied", patch_name)
- else:
- call_patch(patch_str, "--dry-run")
- raise ValueError("patch %s doesn't apply!", patch_name)
-
def debpatch(patch, patch_name, args):
- if len(patch_name) > 60:
- # this messes with our dch "already applied" logic detection, sorry
- raise ValueError("pick a shorter patch name; sorry")
-
# don't change anything if...
dry_run = args.target_version
+ with open(args.changelog) as fp:
+ current = Changelog(fp.read())
+
changelog = list(filter(lambda x: is_dch(x.path), patch))
if not changelog:
logging.info("no debian/changelog in patch: %s" % args.patch_file)
old_version = None
- target = {
- "Version": None,
- "Distribution": DISTRIBUTION_DEFAULT,
- "Urgency": "low",
- "Changes": " * Rebase patch %s." % patch_name,
- }
+ target = ChangeBlock(
+ package = current[0].package,
+ author = "%s <%s>" % (os.getenv("DEBFULLNAME"), os.getenv("DEBEMAIL")),
+ date = email.utils.formatdate(time.time(), localtime=True),
+ version = None,
+ distributions = args.distribution,
+ urgency = "low",
+ changes = ["", " * Rebase patch %s." % patch_name, ""],
+ )
+ target.add_trailing_line("")
elif len(changelog) > 1:
raise ValueError("more than one debian/changelog patch???")
else:
@@ -189,23 +144,32 @@ def debpatch(patch, patch_name, args):
if args.source_version:
if old_version:
print(old_version)
- return
+ return False
if not dry_run:
- apply_patch_str(patch_name, str(patch))
+ patch_str = str(patch)
+ if check_patch(patch_str, "-N"):
+ call_patch(patch_str)
+ logging.info("patch %s applies!", patch_name)
+ elif check_patch(patch_str, "-R"):
+ logging.warn("patch %s already applied", patch_name)
+ return False
+ else:
+ call_patch(patch_str, "--dry-run", "-f")
+ raise ValueError("patch %s doesn't apply!", patch_name)
# only apply d/changelog patch if the rest of the patch applied
- with open(args.changelog) as fp:
- current = read_dch(fp.read())
new_version = apply_dch_patch(args.changelog, current, patch_name, old_version, target, dry_run)
if args.target_version:
print(new_version)
- return
+ return False
if args.repl:
import code
code.interact(local=locals())
+ return True
+
def main(args):
parser = argparse.ArgumentParser(
description='Apply a debdiff to a Debian source package')
@@ -213,6 +177,9 @@ def main(args):
help='Output more information')
parser.add_argument('-c', '--changelog', default='debian/changelog',
help='Path to debian/changelog; default: %(default)s')
+ parser.add_argument('-D', '--distribution', default='experimental',
+ help='Distribution to use, if the patch doesn\'t already contain a '
+ 'changelog; default: %(default)s')
parser.add_argument('--repl', action="store_true",
help="Run the python REPL after processing.")
parser.add_argument('--source-version', action="store_true",
@@ -283,8 +250,8 @@ def main(args):
C(["dpkg-source", "-x", "--skip-patches", args.orig_dsc_or_dir, builddir], stdout=stdout)
origdir = os.getcwd()
os.chdir(builddir)
- debpatch(patch, patch_name, args)
- if dry_run:
+ did_patch = debpatch(patch, patch_name, args)
+ if dry_run or not did_patch:
return
os.chdir(origdir)
try:
diff --git a/scripts/debpatch.1 b/scripts/debpatch.1
index 8af6fb7..e94f55c 100644
--- a/scripts/debpatch.1
+++ b/scripts/debpatch.1
@@ -69,6 +69,10 @@ Output more information
\fB\-c\fR CHANGELOG, \fB\-\-changelog\fR CHANGELOG
Path to debian/changelog; default: debian/changelog
.TP
+\fB\-D\fR DISTRIBUTION, \fB\-\-distribution\fR DISTRIBUTION
+Distribution to use, if the patch doesn't already contain a changelog; default:
+experimental
+.TP
\fB\-\-repl\fR
Run the python REPL after processing.
.TP
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/collab-maint/devscripts.git
More information about the devscripts-devel
mailing list