[git-buildpackage] [PATCH] dch: fake timestamp

Raphael Lisicki raphael.lisicki at siemens.com
Mon Oct 24 12:25:41 CEST 2022


Allow to derive timestamp for changelog from commit to obtain reproducible
changelog files
---
 gbp/deb/changelog.py | 14 ++++++++++++--
 gbp/scripts/dch.py   | 26 ++++++++++++++++----------
 2 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/gbp/deb/changelog.py b/gbp/deb/changelog.py
index fdee1a9d..0f2fe575 100644
--- a/gbp/deb/changelog.py
+++ b/gbp/deb/changelog.py
@@ -19,6 +19,8 @@
 import email
 import os
 import subprocess
+from datetime import datetime
+from dateutil.parser import isoparse
 from gbp.command_wrappers import Command
 
 
@@ -234,7 +236,7 @@ class ChangeLog(object):
 
     @staticmethod
     def spawn_dch(msg=[], author=None, email=None, newversion=False, version=None,
-                  release=False, distribution=None, dch_options=None):
+                  release=False, distribution=None, dch_options=None, faketime=None):
         """
         Spawn dch
 
@@ -285,7 +287,15 @@ class ChangeLog(object):
             args.append('[[[insert-git-dch-commit-message-here]]]')
         else:
             args.append('')
-        dch = Command('debchange', args, extra_env=env, capture_stderr=True)
+        dch = ""
+        if faketime:
+            (timestamp, timezone) = faketime.split(" ")
+            datestring = datetime.fromtimestamp(int(timestamp)).isoformat() + timezone
+            isodate = isoparse(datestring)
+            args = ["-f", isodate.isoformat(sep=" "), "debchange"] + args
+            dch = Command('faketime', args, extra_env=env, capture_stderr=True)
+        else:
+            dch = Command('debchange', args, extra_env=env, capture_stderr=True)
         dch.run_error = Command._f("Dch failed: {stderr_or_reason}")
         dch([], quiet=True)
         if msg:
diff --git a/gbp/scripts/dch.py b/gbp/scripts/dch.py
index ff4fb95c..19f59c97 100644
--- a/gbp/scripts/dch.py
+++ b/gbp/scripts/dch.py
@@ -78,7 +78,7 @@ def get_author_email(repo, use_git_config):
     return author, email
 
 
-def fixup_section(repo, use_git_author, options, dch_options):
+def fixup_section(repo, use_git_author, options, dch_options, timestamp=None):
     """
     Fixup the changelog header and trailer's committer and email address
 
@@ -108,7 +108,7 @@ def fixup_section(repo, use_git_author, options, dch_options):
             break
     else:
         opts.append(mainttrailer_opts[0])
-    ChangeLog.spawn_dch(msg='', author=author, email=email, dch_options=dch_options + opts)
+    ChangeLog.spawn_dch(msg='', author=author, email=email, dch_options=dch_options + opts, faketime=timestamp)
 
 
 def snapshot_version(version):
@@ -174,17 +174,17 @@ def mangle_changelog(changelog, cp, snapshot=''):
         raise GbpError("Error mangling changelog %s" % e)
 
 
-def do_release(changelog, repo, cp, use_git_author, dch_options):
+def do_release(changelog, repo, cp, use_git_author, dch_options, timestamp=None):
     """Remove the snapshot header and set the distribution"""
     author, email = get_author_email(repo, use_git_author)
     (release, snapshot) = snapshot_version(cp['Version'])
     if snapshot:
         cp['MangledVersion'] = release
         mangle_changelog(changelog, cp)
-    cp.spawn_dch(release=True, author=author, email=email, dch_options=dch_options)
+    cp.spawn_dch(release=True, author=author, email=email, dch_options=dch_options, faketime=timestamp)
 
 
-def do_snapshot(changelog, repo, next_snapshot):
+def do_snapshot(changelog, repo, next_snapshot, timestamp=None):
     """
     Add new snapshot banner to most recent changelog section.
     The next snapshot number is calculated by eval()'ing next_snapshot.
@@ -207,11 +207,12 @@ def parse_commit(repo, commitid, opts, last_commit=False):
     commit_info = repo.get_commit_info(commitid)
     author = commit_info['author'].name
     email = commit_info['author'].email
+    date = commit_info['author'].date
     format_entry = user_customizations.get('format_changelog_entry')
     if not format_entry:
         format_entry = dch.format_changelog_entry
     entry = format_entry(commit_info, opts, last_commit=last_commit)
-    return entry, (author, email)
+    return entry, (author, email, date)
 
 
 def guess_documented_commit(cp, repo, tagformat):
@@ -376,6 +377,8 @@ def build_parser(name):
                              help="mark as release")
     version_group.add_option("-S", "--snapshot", action="store_true", dest="snapshot", default=False,
                              help="mark as snapshot build")
+    version_group.add_option("--fake-timestamp", action="store_true", dest="fake_timestamp", default=False,
+                             help="set debian changelog timestamp to same value as last commit, allows to reproduce snapshots")
     version_group.add_option("-D", "--distribution", dest="distribution", help="Set distribution")
     version_group.add_option("--force-distribution", action="store_true", dest="force_distribution", default=False,
                              help="Force the provided distribution to be used, "
@@ -543,15 +546,17 @@ def main(argv):
                 version_change['version'] = v
 
         i = 0
+        date = None
         for c in commits:
             i += 1
             parsed = parse_commit(repo, c, options,
                                   last_commit=(i == len(commits)))
-            commit_msg, (commit_author, commit_email) = parsed
+            commit_msg, (commit_author, commit_email, commit_date) = parsed
             if not commit_msg:
                 # Some commits can be ignored
                 continue
 
+            date = commit_date
             if add_section:
                 # Add a section containing just this message (we can't
                 # add an empty section with dch)
@@ -578,13 +583,14 @@ def main(argv):
                            dch_options=dch_options)
 
         fixup_section(repo, use_git_author=options.use_git_author, options=options,
-                      dch_options=dch_options)
+                      dch_options=dch_options, timestamp=date if options.fake_timestamp else None)
+
 
         if options.release:
             do_release(changelog, repo, cp, use_git_author=options.use_git_author,
-                       dch_options=dch_options)
+                       dch_options=dch_options, timestamp=date if options.fake_timestamp else None)
         elif options.snapshot:
-            (snap, commit, version) = do_snapshot(changelog, repo, options.snapshot_number)
+            (snap, commit, version) = do_snapshot(changelog, repo, options.snapshot_number, date if options.fake_timestamp else None)
             gbp.log.info("Changelog %s (snapshot #%d) prepared up to %s" % (version, snap, commit[:7]))
 
         if editor_cmd:
-- 
2.25.1



More information about the git-buildpackage mailing list