On Sat, May 19, 2012 at 03:23:36PM +0200, Daniel Dehennin wrote: [..snip..] > + 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,
Leaving the UNRELEASED looks more consistent to me. Cheers, -- Guido > > > 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 <[email protected]> > 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 <[email protected]> > +# 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 <[email protected]> > 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 -- To UNSUBSCRIBE, email to [email protected] with a subject of "unsubscribe". Trouble? Contact [email protected]

