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

Guido Günther agx at sigxcpu.org
Sun Dec 4 13:02:21 CET 2022


Hi Raphael,
On Mon, Oct 24, 2022 at 12:25:01PM +0200, Raphael Lisicki wrote:
> 
> Allow to derive timestamp for changelog from commit to obtain reproducible
> changelog files

That's a great idea. Could you update the manpage as well and add a test?
Cheers,
 -- Guido

> ---
>  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 you do:

    date = commit_date if options.fake_timestamp else None

>              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)
> +

you should be able to drop the condition here


>  
>          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)

here

>          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)

and here - improving readability.
Cheer,
 -- Guido

>              gbp.log.info("Changelog %s (snapshot #%d) prepared up to %s" % (version, snap, commit[:7]))
>  
>          if editor_cmd:
> -- 
> 2.25.1
> 
> _______________________________________________
> git-buildpackage mailing list
> git-buildpackage at lists.sigxcpu.org
> http://lists.sigxcpu.org/mailman/listinfo/git-buildpackage
> 


More information about the git-buildpackage mailing list