Guido Günther <a...@sigxcpu.org> writes:

Hello,

[...]

>> Ok, I'm moving spawn_dch as a staticmethod in ChangeLog.
>> 
>> I'll use gbp.command_wrapper instead of the system def in scripts/dch.py.
>> 
>> I move too: 
>> 
>> - add_changelog_entry() -> ChangeLog.add_entry()
>> 
>> - add_changelog_section() -> ChangeLog.add_section()
>
> Yes, but these should be methods operating on objects not static
> methods.

Here is a new version of the create-inexistant-changelog patch.

Finally Moving spawn_dch was not necessary, so I removed it from this changeset.

It's based on the addition of gbp.deb.control.Control, so if you pull
this from the dad/create-inexistant-changelog/rebased/on-33796f8 tag,
you will get both.

I made the next diff against experimental, so if you apply the patches
From this email, you will get both too.

QUESTION:
=========

I planed to make a special changelog entry instead of "UNRELEASED" in
the case there is not commit to add to a new ChangeLog (line 534 of
gbp.scripts.dch)

What do you think about this?

diff --git a/gbp/scripts/dch.py b/gbp/scripts/dch.py
index e8b2817..d9079bd 100644
--- a/gbp/scripts/dch.py
+++ b/gbp/scripts/dch.py
@@ -531,7 +531,11 @@ def main(argv):
         if add_section:
             # If we end up here, then there were no commits to include,
             # so we put a dummy message in the new section.
-            add_changelog_section(distribution="UNRELEASED", 
msg=["UNRELEASED"],
+            if cp.is_empty():
+                msg = ["New debian/changelog generated by git-dch."]
+            else:
+                msg=["UNRELEASED"]
+            add_changelog_section(distribution="UNRELEASED", msg=msg,
                                   version=version_change,
                                   dch_options=dch_options,
                                   repo=repo,


Regards.

The following changes since commit 33796f8278e4b7e81674fa7e7f90cfb076804147:

  Add minimalist debian/control object. (2012-05-18 23:35:18 +0200)

are available in the git repository at:

  git://git.baby-gnu.net/git-buildpackage 
tags/dad/create-inexistant-changelog/rebased/on-33796f8

for you to fetch changes up to 48a9ba5b6c8f1fd3ed9e946281b82447caadad0e:

  Create debian/changelog if it does not exist. (2012-05-19 15:05:20 +0200)

----------------------------------------------------------------
This version of the patch is based on dad/add-minimalist-control-object/1.

Moving spawn_dch around was not necessary, nor the ChangeLogSection.

It pass full tests.

----------------------------------------------------------------
Daniel Dehennin (1):
      Create debian/changelog if it does not exist.

 gbp/deb/changelog.py              |   98 +++++++++++++++++------------
 gbp/scripts/dch.py                |   40 ++++++++++--
 tests/11_test_changelog_create.py |  123 +++++++++++++++++++++++++++++++++++++
 3 files changed, 216 insertions(+), 45 deletions(-)
 create mode 100644 tests/11_test_changelog_create.py


PATCHES:
========

From 33796f8278e4b7e81674fa7e7f90cfb076804147 Mon Sep 17 00:00:00 2001
From: Daniel Dehennin <daniel.dehen...@baby-gnu.org>
Date: Fri, 18 May 2012 19:42:16 +0200
Subject: [PATCH 1/2] Add minimalist debian/control object.

* gbp/deb/control.py: New class Control with "name" property to get
  package source name.
---
 gbp/deb/control.py |   63 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 gbp/deb/control.py

diff --git a/gbp/deb/control.py b/gbp/deb/control.py
new file mode 100644
index 0000000..f048f33
--- /dev/null
+++ b/gbp/deb/control.py
@@ -0,0 +1,63 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2012 Daniel Dehennin <daniel.dehen...@baby-gnu.org>
+#    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 2 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.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+"""A Debian Control"""
+
+import email
+import os
+
+class NoControlError(Exception):
+    """No control found"""
+    pass
+
+class ParseControlError(Exception):
+    """Problem parsing control"""
+    pass
+
+class Control(object):
+    """A Debian control"""
+
+    def __init__(self, filename="debian/control"):
+        """
+        Parse an existing control file.
+
+        @param filename: name of the control file
+        @type filename: C{str}
+        @return: Control object
+        @rtype: C{gbp.deb.conrol.Control} object
+        """
+        if not os.access(filename, os.F_OK):
+            raise NoControlError("Control file %s does not exist" % filename)
+
+        self.filename = filename
+        with file(filename) as f:
+            control = email.message_from_file(f)
+
+        if not control.items():
+            raise ParseControlError("Empty control file")
+
+        self._control = control
+
+    # def __getitem__(self, item):
+    #     return self._control[item]
+
+    # def __setitem__(self, item, value):
+    #     self._control[item] = value
+
+    @property
+    def name(self):
+        """The packges name"""
+        return self._control['Source']
-- 
1.7.10


From 48a9ba5b6c8f1fd3ed9e946281b82447caadad0e Mon Sep 17 00:00:00 2001
From: Daniel Dehennin <daniel.dehen...@baby-gnu.org>
Date: Mon, 14 May 2012 18:15:06 +0200
Subject: [PATCH 2/2] Create debian/changelog if it does not exist.

The main purpose of this patch is to permit the creation of an empty
changelog object.

An empty ChangeLog object can be created if the changelog file is
inexistant, null size is not enought for "dch --create".

The main difference results in the need to:
- guess the version number if none was provided on the command line
- try harder to guess the commit to start from
- take care of access to inexistant ChangeLog attributes.

* tests/11_test_changelog_create.py: Test several senarios of
  debian/changelog creation.

* gbp/deb/changelog.py (__init__): Default changelog filename to
  "debian/changelog". Permit to create empty changelog, take care that
  cp['Changes'] and cp['Version'] must be strings.
  (is_empty): Test if the changelog is present, the dch tool only support
  "--create" when the changelog file is absent, not if its size is null.

* gbp/scripts/dch.py: Import new gbp.deb.control.
  (spawn_dch): Set environnement variable EDITOR to /bin/true when
  calling dch with "--create".
  (add_changelog_section): We need to guess version from upstream is the
  changelog is empty and no version was provided. Take care to create the
  changelog if it's empty.
  (guess_snapshot_commit): Can not call repo.find_version() if the
  changelog is empty.
  (guess_version_from_upstream): cp is None when called by
  ChangeLog.create().
  (main): Require the options "upstream-tree" and "upstream-branch".
  Try hard to guess the last commit, last to merge-base.
---
 gbp/deb/changelog.py              |   98 +++++++++++++++++------------
 gbp/scripts/dch.py                |   40 ++++++++++--
 tests/11_test_changelog_create.py |  123 +++++++++++++++++++++++++++++++++++++
 3 files changed, 216 insertions(+), 45 deletions(-)
 create mode 100644 tests/11_test_changelog_create.py

diff --git a/gbp/deb/changelog.py b/gbp/deb/changelog.py
index 7dc51d9..dec8fc3 100644
--- a/gbp/deb/changelog.py
+++ b/gbp/deb/changelog.py
@@ -62,55 +62,66 @@ class ChangeLogSection(object):
 class ChangeLog(object):
     """A Debian changelog"""
 
-    def __init__(self, contents=None, filename=None):
+    def __init__(self, contents=None, filename="debian/changelog"):
         """
         Parse an existing changelog, Either contents, containing the contents
         of a changelog file, or filename, pointing to a changelog file must be
         passed.
         """
-        # Check that either contents or filename is passed (but not both)
-        if (not filename and not contents) or (filename and contents):
-            raise Exception("Either filename or contents must be passed")
-
-        # If a filename was passed, check if it exists
-        if filename and not os.access(filename, os.F_OK):
-            raise NoChangeLogError, "Changelog %s not found" % (filename, )
-
-        # If no filename was passed, let's read from stdin
-        if not filename:
-            filename = '-'
-
-        # Note that if contents is None, stdin will just be closed right
-        # away by communicate.
-        cmd = subprocess.Popen(['dpkg-parsechangelog', '-l%s' % filename],
-                                stdin=subprocess.PIPE,
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE)
-        (output, errors) = cmd.communicate(contents)
-        if cmd.returncode:
-            raise ParseChangeLogError("Failed to parse changelog. "
-                                      "dpkg-parsechangelog said:\n%s" % 
(errors, ))
-        # Parse the result of dpkg-parsechangelog (which looks like
-        # email headers)
-        cp = email.message_from_string(output)
-        try:
-            if ':' in cp['Version']:
-                cp['Epoch'], cp['NoEpoch-Version'] = cp['Version'].split(':', 
1)
-            else:
-                cp['NoEpoch-Version'] = cp['Version']
-            if '-' in cp['NoEpoch-Version']:
-                cp['Upstream-Version'], cp['Debian-Version'] = 
cp['NoEpoch-Version'].rsplit('-', 1)
-            else:
-                cp['Debian-Version'] = cp['NoEpoch-Version']
-        except TypeError:
-            raise ParseChangeLogError, output.split('\n')[0]
 
-        self._cp = cp
+        # Parse contents first or filename if non empty.
+        empty = False
         if contents:
+            read_from = '-'
             self._contents = contents[:]
-        else:
+        elif os.access(filename, os.F_OK) and os.stat(filename).st_size > 0:
+            read_from = filename
             with file(filename) as f:
                 self._contents = f.read()
+        elif os.access(os.path.realpath(os.path.dirname(filename)), os.W_OK):
+            # Use os.path.realpath to cope with
+            # os.path.dirname('changelog') returning ''
+            #
+            # Empty changelog but can be created
+            empty = True
+        else:
+            raise NoChangeLogError("Empty contents and changelog %s, parent 
changelog directory not writable" % filename)
+
+        output = ''
+        if not empty:
+            # Note that if contents is None, stdin will just be closed right
+            # away by communicate.
+            cmd = subprocess.Popen(['dpkg-parsechangelog', '-l%s' % read_from],
+                                   stdin=subprocess.PIPE,
+                                   stdout=subprocess.PIPE,
+                                   stderr=subprocess.PIPE)
+            (output, errors) = cmd.communicate(contents)
+            if cmd.returncode:
+                raise ParseChangeLogError("Failed to parse changelog. "
+                                          "dpkg-parsechangelog said:\n%s" % 
(errors, ))
+
+        # Parse the result of dpkg-parsechangelog (which looks like
+        # email headers)
+        cp = email.message_from_string(output)
+
+        if not empty:
+            try:
+                if ':' in cp['Version']:
+                    cp['Epoch'], cp['NoEpoch-Version'] = 
cp['Version'].split(':', 1)
+                else:
+                    cp['NoEpoch-Version'] = cp['Version']
+                if '-' in cp['NoEpoch-Version']:
+                    cp['Upstream-Version'], cp['Debian-Version'] = 
cp['NoEpoch-Version'].rsplit('-', 1)
+                else:
+                    cp['Debian-Version'] = cp['NoEpoch-Version']
+            except TypeError:
+                raise ParseChangeLogError, output.split('\n')[0]
+        else:
+            cp['Changes'] = ''
+            cp['Version'] = ''
+
+        self._cp = cp
+        self.filename = filename
 
     def __getitem__(self, item):
         return self._cp[item]
@@ -204,3 +215,12 @@ class ChangeLog(object):
         Get sections in the changelog
         """
         return list(self.sections_iter)
+
+    def is_empty(self):
+        """
+        Whether the changelog file does not exist
+
+        @return: C{True} if the changelog file is missing, C{False} otherwise
+        @rtype: C{bool}
+        """
+        return not os.access(self.filename, os.F_OK)
diff --git a/gbp/scripts/dch.py b/gbp/scripts/dch.py
index 14dff29..e8b2817 100644
--- a/gbp/scripts/dch.py
+++ b/gbp/scripts/dch.py
@@ -31,6 +31,7 @@ from gbp.errors import GbpError
 from gbp.deb import compare_versions
 from gbp.deb.git import GitRepositoryError, DebianGitRepository
 from gbp.deb.changelog import ChangeLog, NoChangeLogError
+from gbp.deb.control import Control
 
 user_customizations = {}
 snapshot_re = re.compile("\s*\*\* SNAPSHOT build 
@(?P<commit>[a-z0-9]+)\s+\*\*")
@@ -75,6 +76,9 @@ def spawn_dch(msg=[], author=None, email=None, 
newversion=False, version=None,
     if author and email:
         env = """DEBFULLNAME="%s" DEBEMAIL="%s" """ % (author, email)
 
+    if dch_options.find("--create") != -1:
+        env += "EDITOR=/bin/true"
+
     if distribution:
         distopt = "--distribution=%s" % distribution
 
@@ -124,10 +128,14 @@ def guess_version_from_upstream(repo, 
upstream_tag_format, cp):
 def add_changelog_section(msg, distribution, repo, options, cp,
                           author=None, email=None, version={}, dch_options=''):
     """Add a new section to the changelog"""
-    if not version and not cp.is_native():
+    if not version and (cp.is_empty() or not cp.is_native()):
         v = guess_version_from_upstream(repo, options.upstream_tag, cp)
         if v:
             version['version'] = v
+    if cp.is_empty():
+        control = Control()
+        dch_options += " --create --package=%s" % control.name
+
     spawn_dch(msg=msg, newversion=True, version=version, author=author,
               email=email, distribution=distribution, dch_options=dch_options)
 
@@ -269,7 +277,7 @@ def guess_snapshot_commit(cp, repo, options):
     # If the current topmost changelog entry has already been tagged rely on
     # the version information only. The upper level relies then on the version
     # info anyway:
-    if repo.find_version(options.debian_tag, cp.version):
+    if not cp.is_empty() and repo.find_version(options.debian_tag, cp.version):
         return None
     # If we didn't find a snapshot header we look at the point the changelog
     # was last touched.
@@ -357,6 +365,8 @@ def main(argv):
     parser.add_option_group(custom_group)
 
     parser.add_boolean_config_file_option(option_name = "ignore-branch", 
dest="ignore_branch")
+    naming_group.add_config_file_option(option_name="upstream-tree", 
dest="upstream_tree")
+    naming_group.add_config_file_option(option_name="upstream-branch", 
dest="upstream_branch")
     naming_group.add_config_file_option(option_name="debian-branch", 
dest="debian_branch")
     naming_group.add_config_file_option(option_name="upstream-tag", 
dest="upstream_tag")
     naming_group.add_config_file_option(option_name="debian-tag", 
dest="debian_tag")
@@ -429,7 +439,7 @@ def main(argv):
             since = options.since
         else:
             since = ''
-            if options.auto:
+            if options.auto or cp.is_empty():
                 since = guess_snapshot_commit(cp, repo, options)
                 if since:
                     gbp.log.info("Continuing from commit '%s'" % since)
@@ -437,7 +447,24 @@ def main(argv):
                 else:
                     gbp.log.info("Couldn't find snapshot header, using version 
info")
             if not since:
-                since = repo.find_version(options.debian_tag, cp['Version'])
+                # Take care of empty debian/changelog
+                if cp.is_empty():
+                    pattern = options.upstream_tag % dict(version='*')
+                    upstream = None
+                    try:
+                        upstream = repo.find_tag('HEAD', pattern=pattern)
+                        gbp.log.debug('Found upstream tag %s' % upstream)
+                    except GitRepositoryError:
+                        gbp.log.debug('No upstream tag found, try upstream 
branch')
+                    if upstream == None or options.upstream_tree == 'branch':
+                        upstream = options.upstream_branch
+                    since = repo.get_merge_base('HEAD', upstream)
+                    gbp.log.debug("Found merge-base %s" % since)
+                    if since and options.snapshot:
+                        # Snapshot can not be guessed
+                        found_snapshot_header = True
+                else:
+                    since = repo.find_version(options.debian_tag, 
cp['Version'])
                 if not since:
                     raise GbpError, "Version %s not found" % cp['Version']
 
@@ -448,16 +475,17 @@ def main(argv):
         commits.reverse()
 
         # add a new changelog section if:
-        if options.new_version or options.bpo or options.nmu or options.qa:
+        if cp.is_empty() or options.new_version or options.bpo or options.nmu 
or options.qa:
             if options.bpo:
                 version_change['increment'] = '--bpo'
             elif  options.nmu:
                 version_change['increment'] = '--nmu'
             elif  options.qa:
                 version_change['increment'] = '--qa'
-            else:
+            elif options.new_version:
                 version_change['version'] = options.new_version
             # the user wants to force a new version
+            # or the changelog was empty and we need to guess version
             add_section = True
         elif cp['Distribution'] != "UNRELEASED" and not found_snapshot_header 
and commits:
             # the last version was a release and we have pending commits
diff --git a/tests/11_test_changelog_create.py 
b/tests/11_test_changelog_create.py
new file mode 100644
index 0000000..b613ce5
--- /dev/null
+++ b/tests/11_test_changelog_create.py
@@ -0,0 +1,123 @@
+# vim: set fileencoding=utf-8 :
+
+"""Test L{Changelog}'s create"""
+
+import unittest
+
+from testutils import DebianGitTestRepo
+
+from gbp.scripts import dch
+from gbp.deb.changelog import ChangeLog
+
+import os
+import re
+
+snap_header = 
r'^test-package\s\(1.0-1~1\.gbp([0-9a-f]{6})\)\sUNRELEASED;\surgency=low'
+snap_mark = r'\s{2}\*{2}\sSNAPSHOT\sbuild\s@'
+
+class TestScriptDch(DebianGitTestRepo):
+    """Test git-dch"""
+
+    def setUp(self):
+        DebianGitTestRepo.setUp(self)
+        self.add_file("foo", "bar")
+        self.repo.create_tag("upstream/1.0", msg="upstream version 1.0")
+        self.repo.create_branch("debian")
+        self.repo.set_branch("debian")
+        self.upstream_tag = "upstream/%(version)s"
+        self.top = os.path.abspath(os.path.curdir)
+        os.mkdir(os.path.join(self.repo.path, "debian"))
+        os.chdir(self.repo.path)
+        self.add_file("debian/control", """Source: test-package\nSection: 
test\n""")
+
+    def tearDown(self):
+        os.chdir(self.top)
+        DebianGitTestRepo.tearDown(self)
+
+    def test_create_from_dch_main(self):
+        """Test dch.py like git-dch script does: must not include upstream log 
in debian/changelog"""
+        ret = dch.main(["--upstream-tag=%s" % self.upstream_tag, 
"--debian-branch=debian"])
+        self.assertEqual(ret, 0)
+        lines = file("debian/changelog").readlines()
+        assert "test-package (1.0-1) UNRELEASED; urgency=low\n" in lines
+        assert """  * added debian/control\n""" in lines
+
+    def test_create_from_dch_main_with_auto(self):
+        """Test dch.py like git-dch script does: guess last commit"""
+        ret = dch.main(["--upstream-tag=%s" % self.upstream_tag, 
"--debian-branch=debian", "-a"])
+        self.assertEqual(ret, 0)
+        lines = file("debian/changelog").readlines()
+        assert "test-package (1.0-1) UNRELEASED; urgency=low\n" in lines
+        assert """  * added debian/control\n""" in lines
+
+    def test_create_from_dch_main_with_snapshot(self):
+        """Test dch.py like git-dch script does: snashot mode"""
+        ret = dch.main(["--upstream-tag=%s" % self.upstream_tag, 
"--debian-branch=debian", "-S"])
+        self.assertEqual(ret, 0)
+        lines = file("debian/changelog").readlines()
+        header = re.search(snap_header, lines[0])
+        self.assertEqual(header.lastindex, 1)
+        assert re.search(snap_mark + header.group(1), lines[2])
+        assert """  * added debian/control\n""" in lines
+
+    def test_create_from_dch_main_after_delete(self):
+        """Test dch.py like git-dch script does: debian/changelog was 
deleted"""
+        self.add_file("debian/changelog", "fake")
+        self.repo.remove_files("debian/changelog")
+        self.repo.commit_files(self.repo.path, "removed debian/changelog")
+        ret = dch.main(["--upstream-tag=%s" % self.upstream_tag, 
"--debian-branch=debian"])
+        self.assertEqual(ret, 0)
+        lines = file("debian/changelog").readlines()
+        assert "test-package (1.0-1) UNRELEASED; urgency=low\n" in lines
+        assert """  * removed debian/changelog\n""" not in lines
+        assert """  * UNRELEASED\n""" in lines
+
+    def test_create_from_dch_main_with_auto_snapshot(self):
+        """Test dch.py like git-dch script does: guess last commit, snashot"""
+        ret = dch.main(["--upstream-tag=%s" % self.upstream_tag, 
"--debian-branch=debian", "-a", "-S"])
+        self.assertEqual(ret, 0)
+        lines = file("debian/changelog").readlines()
+        header = re.search(snap_header, lines[0])
+        self.assertEqual(header.lastindex, 1)
+        assert re.search(snap_mark + header.group(1), lines[2])
+        assert """  * added debian/control\n""" in lines
+
+    def test_create_from_dch_main_with_auto_after_delete(self):
+        """Test dch.py like git-dch script does: guess last commit, 
debian/changelog was deleted"""
+        self.add_file("debian/changelog", "fake")
+        self.repo.remove_files("debian/changelog")
+        self.repo.commit_files(self.repo.path, "removed debian/changelog")
+        ret = dch.main(["--upstream-tag=%s" % self.upstream_tag, 
"--debian-branch=debian", "-a"])
+        self.assertEqual(ret, 0)
+        lines = file("debian/changelog").readlines()
+        assert "test-package (1.0-1) UNRELEASED; urgency=low\n" in lines
+        assert """  * removed debian/changelog\n""" not in lines
+        assert """  * UNRELEASED\n""" in lines
+
+    def test_create_from_dch_main_with_snapshot_after_delete(self):
+        """Test dch.py like git-dch script does: snapshot, debian/changelog 
was deleted"""
+        self.add_file("debian/changelog", "fake")
+        self.repo.remove_files("debian/changelog")
+        self.repo.commit_files(self.repo.path, "removed debian/changelog")
+        ret = dch.main(["--upstream-tag=%s" % self.upstream_tag, 
"--debian-branch=debian", "-S"])
+        self.assertEqual(ret, 0)
+        lines = file("debian/changelog").readlines()
+        header = re.search(snap_header, lines[0])
+        self.assertEqual(header.lastindex, 1)
+        assert re.search(snap_mark + header.group(1), lines[2])
+        assert """  * removed debian/changelog\n""" not in lines
+        assert """  * UNRELEASED\n""" in lines
+
+    def test_create_from_dch_main_with_auto_snapshot_after_delete(self):
+        """Test dch.py like git-dch script does: guess last commit, snapshot, 
debian/changelog was deleted"""
+        self.add_file("debian/changelog", "fake")
+        self.repo.remove_files("debian/changelog")
+        self.repo.commit_files(self.repo.path, "removed debian/changelog")
+        ret = dch.main(["--upstream-tag=%s" % self.upstream_tag, 
"--debian-branch=debian", "-a", "-S"])
+        self.assertEqual(ret, 0)
+        lines = file("debian/changelog").readlines()
+        header = re.search(snap_header, lines[0])
+        self.assertEqual(header.lastindex, 1)
+        assert re.search(snap_mark + header.group(1), lines[2])
+        assert """  * removed debian/changelog\n""" not in lines
+        assert """  * UNRELEASED\n""" in lines
-- 
1.7.10


-- 
Daniel Dehennin
Récupérer ma clef GPG:
gpg --keyserver pgp.mit.edu --recv-keys 0x7A6FE2DF

Attachment: pgphlik8dOi3i.pgp
Description: PGP signature

Reply via email to