[debrepatch] 01/03: debpatch: use debian.changelog python module instead of dch/dpkg-parsechangelog
Ximin Luo
infinity0 at debian.org
Tue Apr 18 18:27:53 UTC 2017
This is an automated email from the git hooks/post-receive script.
infinity0 pushed a commit to branch master
in repository debrepatch.
commit b0944cf6a76c2dc53e960d8fc67fbf31c18877c6
Author: Ximin Luo <infinity0 at debian.org>
Date: Tue Apr 18 20:09:19 2017 +0200
debpatch: use debian.changelog python module instead of dch/dpkg-parsechangelog
---
debpatch | 132 ++++++++++++++++++++++++---------------------------------------
1 file changed, 50 insertions(+), 82 deletions(-)
diff --git a/debpatch b/debpatch
index fd861e3..b6a5304 100755
--- a/debpatch
+++ b/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,43 @@ 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):
+ 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"]))
+ (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()
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 +115,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 +145,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 +178,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 +251,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:
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/debrepatch.git
More information about the Reproducible-commits
mailing list