Julien Jehannet avatar Julien Jehannet committed 0c44bc0

migration-clcommands.diff ready for review
clcommands-clean.diff ready for review
clcommands-project.diff ready for review
clcommands-setup.diff ready for review
clcommands-login.diff ready for review
clcommands-tag.diff ready for review
clcommands-build.diff ready for review
clcommands-piuparts.diff ready for review
clcommands-script.diff ready for review
clcommands-check.diff ready for review
refactor-pbuilderrc.diff ready for review
clcommands-build-pristine.diff ready for review
refactor-setup_file.diff ready for review
clcommands-build-source.diff ready for review
logger-improvements.diff ready for review
lgp-hook-autoshell.diff ready for review

Comments (0)

Files changed (27)

TODOLIST

-# HG changeset patch
-# Date 1304623868 -7200
-# User Julien Jehannet <julien@smaf.org>
-# Parent e856c1a694f01a64ffb9721b196ab6c72b471782
-
-diff --git a/TODOLIST b/TODOLIST
-new file mode 100644
---- /dev/null
-+++ b/TODOLIST
-@@ -0,0 +1,42 @@
-+preparedist
-+-----------
-+
-+- check if documentation is up-to-date
-+- check fichiers dans dépôts (avant hg clean)
-+- check changelog closing (pkginfo)
-+- check license/copying
-+- check/create template
-+- virer ask, confirm, condexec et utiliser lgc
-+
-+check
-+-----
-+* -i --interactive
-+	build doc if not up to date (-q)
-+	close changelog
-+
-+* check_debsrc
-+
-+* check preparedist
-+  -s preparedist ?
-+
-+* check_hg_repo (à faire aussi dans le build ?)
-+
-+	vérifier les changesets incomings
-+	vérifier les patches appliqués
-+	>>> from mercurial.dispatch import dispatch as hg_call
-+	>>> hg_call(['status'])
-+
-+build
-+-----
-+* ajouter la possibilité de lancer les checks en plus ?
-+  --checks  yes/no
-+  (utiliser un process en parallèle ? mais il faut régler tous les checkers bloquants avant)
-+
-+* copy files to resultdir
-+  new copy back system (using dedicated thread)
-+
-+templates
-+---------
-+new-project
-+copying
-+announce
-diff --git a/doc/lgp/bug.rst b/doc/lgp/bug.rst
-new file mode 100644
---- /dev/null
-+++ b/doc/lgp/bug.rst
-@@ -0,0 +1,2 @@
-+- no pristine tarball when the first build failed for a same distribution
-+- too many logs ?

WIP-build-pristine-refactoring.diff

+# HG changeset patch
+# Date 1304976068 -7200
+# User Julien Jehannet <julien@smaf.org>
+# Parent bc9c5b99ece96476725af2d9f15a84afb0262ebb
+
+diff --git a/lgp/build_pristine.py b/lgp/build_pristine.py
+--- a/lgp/build_pristine.py
++++ b/lgp/build_pristine.py
+@@ -61,6 +61,7 @@ class Pristine(SetupInfo):
+         if self.config.orig_tarball:
+             self.logger.info('use original source archive (tarball): %s',
+                              self.config.orig_tarball)
++        # super method must not be called
+ 
+     def run(self, args):
+         Cleaner(config=self.config).run(args)
+@@ -80,10 +81,16 @@ class Pristine(SetupInfo):
+         XXX No call to move_package_files()
+ 
+         Use cases:
+-        1. initial -> ignore orig_tarball
+-        2. not initial and not orig_tarball -> error
+-        3. not initial and uri orig_tarball -> fetch
+-        4. not initial and not existing orig_tarball -> fetch
++        1. if no parameter *and* not initial:
++            fetch it by Debian way (`get-orig-source`)
++            and return new location
++            E. stop with informative message
++        2. if initial:
++            copy it to tmpdir and return new location
++            E. if no copied
++        4. if no parameter *but* initial:
++            create it from project directory in tmpdir
++            and return new location
+ 
+         See:
+         http://www.debian.org/doc/debian-policy/ch-source.html
+@@ -98,75 +105,78 @@ class Pristine(SetupInfo):
+         tarball = '%s_%s.orig.tar.gz' % fileparts
+         upstream_tarball = '%s-%s.tar.gz' % fileparts
+ 
+-        if (self.config.orig_tarball and
+-            self.is_initial_debian_revision() and
+-            self.config.orig_tarball.startswith(('ftp://', 'http://'))):
+-            self.logger.warn("retrieve pristine tarball remotely is not recommended")
++        if not self.config.orig_tarball and not self.is_initial_debian_revision():
++            # run optional `debian/rules get-orig-source` makefile target to
++            # retrieve pristine tarball by default (Debian way)
++            if not self.config.orig_tarball:
++                self.logger.info('trying to retrieve pristine tarball '
++                                 'by using optional `get-orig-source` target...')
++                try:
++                    cmd = ["fakeroot", "debian/rules", "get-orig-source"]
++                    check_call(cmd, stderr=stderr)
++                except CalledProcessError, err:
++                    self.logger.warn("run '%s' without success" % ' '.join(cmd))
++                else:
++                    # use parent directory as uscan does
++                    tarball = osp.realpath(osp.join('..', upstream_tarball))
++                    return self._set_pristine_location(tarball)
++            # if still missing, raise error
++            debian_revision = self.get_debian_version().rsplit('-', 1)[1]
++            self.logger.error("pristine tarball is required when you don't build "
++                              "the first revision of a debian package "
++                              "(use '--orig-tarball' option)")
++            self.logger.error("If you haven't the original tarball version, you could run: "
++                              "'apt-get source --tar-only %s'"
++                              % self.get_debian_name())
++            raise LGPException('unable to build upstream tarball of %s package '
++                               'for Debian revision "%s"'
++                               % (self.get_debian_name(), debian_revision))
+ 
+-        if self.config.orig_tarball and self.is_initial_debian_revision():
+-            msg = "you are passing a pristine tarball in command line for an initial revision"
+-            self.logger.warn(msg)
++        if self.config.orig_tarball:
++            if self.is_initial_debian_revision():
++                msg = ("you are passing a pristine tarball in command line "
++                       "for an initial revision")
++                self.logger.error(msg) # not fatal
++            if self.config.orig_tarball.startswith(('ftp://', 'http://')):
++                msg = "retrieve pristine tarball remotely is not recommended"
++                self.logger.warn(msg)
++            try:
++                tarball = osp.join(tmpdir, tarball)
++                # we copy and rename at the same time
++                urllib.urlretrieve(self.config.orig_tarball, tarball)
++            except IOError, err:
++                self.logger.critical("the provided original source archive (tarball) "
++                                     "can't be retrieved from given location: %s"
++                                     % self.config.orig_tarball)
++                raise LGPException(err)
++            else:
++                return self._set_pristine_location(tarball)
+ 
+-        # creation of source archive if not provided
+-        if not self.config.orig_tarball and self.is_initial_debian_revision():
+-            self.logger.info("creating new pristine tarball from working directory...")
++        if self.is_initial_debian_revision():
++            # creation of source archive if not provided
++            self.logger.info("creating new pristine tarball from project directory...")
+             try:
+                 self._run_command("sdist", dist_dir=tmpdir)
+-                upstream_tarball = osp.join(tmpdir, upstream_tarball)
+-                self._set_pristine_location(upstream_tarball)
+             except CalledProcessError, err:
+                 self.logger.error("creation of the source archive failed")
+                 self.logger.error("check if the version '%s' is really tagged in "
+                                   "your repository" % self.get_upstream_version())
+                 raise LGPCommandException("source distribution wasn't properly built", err)
+-        elif not self.config.orig_tarball:
+-            # if not an initial version, run optional debian/rules get-orig-source
+-            # makefile target to retrieve pristine tarball by default (Debian way)
+-            self.logger.info('trying to retrieve pristine tarball remotely '
+-                             'by using optional `get-orig-source` target...')
+-            try:
+-                cmd = ["fakeroot", "debian/rules", "get-orig-source"]
+-                check_call(cmd, stderr=stderr)
+-            except CalledProcessError, err:
+-                self.logger.warn("run '%s' without success" % ' '.join(cmd))
+             else:
+-                # use parent directory as uscan does
+-                upstream_tarball = osp.realpath(osp.join('..', tarball))
+-                self._set_pristine_location(upstream_tarball)
++                tarball = osp.join(tmpdir, upstream_tarball)
++                return self._set_pristine_location(tarball)
+ 
+-        # coherence of `orig-tarball` option presence and initial debian revision
+-        if not self.config.orig_tarball and not self.is_initial_debian_revision():
+-            debian_revision = self.get_debian_version().rsplit('-', 1)[1]
+-            self.logger.error("pristine tarball is required when you don't build "
+-                              "the first revision of a debian package "
+-                              "(use '--orig-tarball' option)")
+-            self.logger.info("If you haven't the original tarball version, you could run: "
+-                             "'apt-get source --tar-only %s'"
+-                             % self.get_debian_name())
+-            raise LGPException('unable to build upstream tarball of %s package '
+-                               'for Debian revision "%s"'
+-                               % (self.get_debian_name(), debian_revision))
+-
+-        if os.path.exists(tarball):
+-            self.logger.debug("will erase previous pristine tarball file: %s", tarball)
+-            os.unlink(tarball)
+-
+-        try:
+-            urllib.urlretrieve(self.config.orig_tarball, tarball) # auto-renaming here
+-            self._set_pristine_location(tarball)
+-        except IOError, err:
+-            self.logger.critical("the provided original source archive (tarball) "
+-                                 "can't be retrieved from given location: %s"
+-                                 % self.config.orig_tarball)
+-            raise LGPException(err)
+-
+-        # make a coherence checks about the pristine tarball
+-        if not os.path.basename(self.config.orig_tarball).startswith(self.get_upstream_name()):
+-            msg = "pristine tarball filename doesn't start with upstream name '%s'. really suspect..."
+-            self.logger.warn(msg % self.get_upstream_name())
+-        return self.config.orig_tarball
++        # must never happened but just in case..
++        raise LGPException("pristine tarball cannot be retrieved")
+ 
+     def _set_pristine_location(self, location):
+         msg = 'pristine tarball not found: %s' % location
+         assert osp.isfile(location), msg
+         self.config.orig_tarball = location
++        # make a post coherence checks about the pristine tarball
++        if not os.path.basename(self.config.orig_tarball).startswith(self.get_upstream_name()):
++            msg = "pristine tarball filename doesn't start with upstream name '%s'. really suspect..."
++            self.logger.warn(msg % self.get_upstream_name())
++        msg= 'using original source archive (tarball):\n\t%s'
++        self.logger.debug(msg, self.config.orig_tarball)
++        return self.config.orig_tarball

WIP-lgp-build-source-improve.diff

+# HG changeset patch
+# Date 1304022143 -7200
+# User Julien Jehannet <julien@smaf.org>
+# Parent 6c237e84f7c346b589c11197ffcdcf37a12003d9
+lgp: build-source: improve code modularization
+
+New:
+* always finalize changelog last entry by updating timestamp and author information
+  (depending of DEBFULLNAME and DEBEMAIL now mandatory) 
+
+Changes:
+* ensure tarball extraction location with format <upstream-name>-<upstream-version>
+* use some Debian environment checker tests from Checker command
+* prefer `debchange()` over sed invocations
+
+diff --git a/lgp/build_source.py b/lgp/build_source.py
+--- a/lgp/build_source.py
++++ b/lgp/build_source.py
+@@ -21,14 +21,13 @@ import sys
+ import logging
+ import os.path as osp
+ import glob
+-import time
+ from subprocess import check_call, CalledProcessError
+ 
+ from logilab.common.shellutils import cp
+ from logilab.common.fileutils import export
+ 
+ from logilab.devtools.lgp import LGP
+-from logilab.devtools.lgp.exceptions import LGPException, LGPCommandException
++from logilab.devtools.lgp.exceptions import LGPCommandException
+ 
+ from logilab.devtools.lgp.setupinfo import SetupInfo
+ from logilab.devtools.lgp.build_pristine import Pristine
+@@ -89,7 +88,7 @@ class DscBuilder(SetupInfo):
+                 logging.info("Debian source control file: %s", dscfile)
+                 self._sign_file(dscfile)
+ 
+-    def make_debian_source_package(self, tmpdir, pristine_tarball, current_distrib):
++    def make_debian_source_package(self, tmpdir, pristine_tarball, distrib):
+         """create a debian source package
+ 
+         This function must be called inside an unpacked source
+@@ -98,26 +97,33 @@ class DscBuilder(SetupInfo):
+ 
+         See:
+         - http://www.debian.org/doc/maint-guide/ch-build.en.html#s-option-sa
++        """
++        arguments = ""
+ 
+-        :param:
+-            origpath: path to orig.tar.gz tarball
+-        """
+-        src_tmpdir = os.path.join(tmpdir, current_distrib)
++        src_tmpdir = os.path.join(tmpdir, distrib)
+         os.mkdir(src_tmpdir)
+ 
+-        self.prepare_source_archive(src_tmpdir, pristine_tarball, current_distrib)
+-
+-        arguments = ""
++        # obtain current format version
+         format = utils.guess_debian_source_format()
+         if format == "1.0":
++            # copy is mandatory to be compatible with format 1.0
++            logging.debug("copy pristine tarball to prepare Debian source package (diff)")
++            cp(pristine_tarball, src_tmpdir)
+             arguments+='--no-copy'
+ 
++        # prepare and extract the upstream pristine tarball
++        origpath = self._prepare_source_archive(src_tmpdir, pristine_tarball,
++                                                distrib)
++
++        # support of the multi-distribution overriding
++        self._manage_current_distribution(origpath, distrib)
++
+         msg = "building Debian source package (format: %s) for '%s' ..."
+-        logging.info(msg % (format, current_distrib))
++        logging.info(msg % (format, distrib))
+ 
+         with utils.chdir(src_tmpdir):
+             try:
+-                cmd = 'dpkg-source %s -b %s' % (arguments, self.origpath)
++                cmd = 'dpkg-source %s -b %s' % (arguments, origpath)
+                 logging.debug("running dpkg-source command: %s ..." % cmd)
+                 check_call(cmd.split(), stdout=sys.stdout)
+             except CalledProcessError, err:
+@@ -127,49 +133,27 @@ class DscBuilder(SetupInfo):
+         assert osp.isfile(dscfile)
+         return dscfile
+ 
+-    def prepare_source_archive(self, src_tmpdir, pristine_tarball, current_distrib):
+-        """prepare and extract the upstream tarball
+-
+-        FIXME replace by TarFile Object
+-        """
+-        # obtain current format version
+-        #format = utils.guess_debian_source_format(tmpdir)
+-
+-        # Mandatory to be compatible with format 1.0
+-        logging.debug("copy pristine tarball to prepare Debian source package (diff)")
+-        cp(pristine_tarball, src_tmpdir)
+-
++    def _prepare_source_archive(self, src_tmpdir, pristine_tarball, distrib):
++        """prepare and extract the upstream pristine tarball"""
++        tarname_format = "%s-%s" % (self.get_upstream_name(), self.get_upstream_version())
++        origpath = osp.join(src_tmpdir, tarname_format)
++        os.mkdir(origpath)
+         logging.debug("extracting original source archive for %s distribution in %s"
+-                      % (current_distrib or "default", src_tmpdir))
++                      % (distrib or "default", origpath))
+         try:
+-            cmd = 'tar --atime-preserve --preserve-permissions --preserve-order -xzf %s -C %s'\
+-                  % (pristine_tarball, src_tmpdir)
++            # Ensure the origpath format in getting pristine tarball files because
++            # it can be different of the standard <upstream-name>-<upstream-version>
++            # if pristine tarball was retrieve remotely (vcs frontend for example)
++            cmd = ('tar --atime-preserve --preserve-permissions --preserve-order '
++                   '-xzf %s -C %s --strip-components=1' % (pristine_tarball, origpath))
++            #logging.debug("running tar command: %s ..." % cmd)
+             check_call(cmd.split(), stdout=sys.stdout)
+         except CalledProcessError, err:
+             raise LGPCommandException('an error occured while extracting the '
+                                       'upstream tarball', err)
++        return origpath
+ 
+-        # Find the right orig path in tarball
+-        # It can be different of the standard <upstream-name>-<upstream-version>
+-        # if pristine tarball was retrieve remotely (vcs frontend for example)
+-        self.origpath = [d for d in os.listdir(src_tmpdir)
+-                         if osp.isdir(osp.join(src_tmpdir, d))][0]
+-
+-        tarname_format = "%s-%s" % (self.get_upstream_name(), self.get_upstream_version())
+-        if self.origpath != tarname_format:
+-            logging.warn("source directory of original source archive (pristine tarball) "
+-                         "has not the expected format (%s): %s" % (tarname_format,
+-                                                                   self.origpath))
+-
+-        # directory containing the debianized source tree
+-        # (i.e. with a debian sub-directory and maybe changes to the original files)
+-        # origpath is depending of the upstream convention
+-        self.origpath = osp.join(src_tmpdir, self.origpath)
+-
+-        # support of the multi-distribution
+-        return self.manage_current_distribution(current_distrib)
+-
+-    def manage_current_distribution(self, current_distrib):
++    def _manage_current_distribution(self, origpath, distrib):
+         """manage debian files depending of the current distrib from options
+ 
+         We copy debian_dir directory into tmp build depending of the target distribution
+@@ -180,44 +164,35 @@ class DscBuilder(SetupInfo):
+         The distribution value will always be rewritten in final changelog.
+ 
+         This is specific to Logilab (debian directory is in project directory)
++
++        :param: origpath: path to orig.tar.gz tarball
++        :param: distrib: current distribution to build
+         """
+-        try:
+-            # don't forget the final slash!
+-            export(osp.join(self.config.pkg_dir, 'debian'), osp.join(self.origpath, 'debian/'),
+-                   verbose=self.config.verbose == 2)
+-        except IOError, err:
+-            raise LGPException(err)
++        # note: don't forget the final slash!
++        export(osp.join(self.config.pkg_dir, 'debian'), osp.join(origpath, 'debian/'),
++               verbose=self.config.verbose == 2)
+ 
+-        debian_dir = self.get_debian_dir(current_distrib)
++        debian_dir = self.get_debian_dir(distrib)
+         if debian_dir != "debian":
+             logging.info("overriding files from '%s' directory..." % debian_dir)
+             # don't forget the final slash!
+-            export(osp.join(self.config.pkg_dir, debian_dir), osp.join(self.origpath, 'debian/'),
++            export(osp.join(self.config.pkg_dir, debian_dir), osp.join(origpath, 'debian/'),
+                    verbose=self.config.verbose)
+ 
+-        # substitute distribution string in file only if line not starting by
+-        # spaces (simple heuristic to prevent other changes in content)
+-        # FIXME use "from debian.changelog import Changelog" instead
+-        if current_distrib:
+-            cmd = ['sed', '-i', '/^[[:alpha:]]/s/\([[:alpha:]]\+\);/%s;/'
+-                   % current_distrib, osp.join(self.origpath, 'debian', 'changelog')]
+-            try:
+-                check_call(cmd, stdout=sys.stdout)
+-            except CalledProcessError, err:
+-                raise LGPCommandException("bad substitution for distribution field", err)
+-
+-        # substitute version string in appending timestamp and suffix
+-        # append suffix string (or timestamp if suffix is empty) to debian revision
+-        # FIXME use "from debian.changelog import Changelog" instead
++        changelog = osp.join(origpath, 'debian', 'changelog')
++        from logilab.devtools.lgp.check import (check_debian_env,
++                                                check_debian_changelog)
++        check_debian_env(self)
++        cmd = ("dch --release %s --changelog %s --force-distribution --distribution %s"
++               % (self.get_debian_version(), changelog, distrib))
+         if self.config.suffix is not None:
+-            suffix = self.config.suffix or '+%s' % int(time.time())
+-            logging.debug("suffix '%s' added to package names" % suffix)
+-            cmd = ['sed', '-i', '1s/(\(.*\))/(\\1%s)/' % suffix,
+-                   osp.join(self.origpath, 'debian', 'changelog')]
+-            try:
+-                check_call(cmd, stdout=sys.stdout)
+-            except CalledProcessError, err:
+-                raise LGPCommandException("bad substitution for version field", err)
+-
+-        return self.origpath
+-
++            cmd += " --local %s" % self.config.suffix
++        logging.debug("running dch command: %s ..." % cmd)
++        try:
++            check_call(['sed', '-i', '/^[[:alpha:]]/s/\([[:alpha:]]\+\);/UNRELEASED;/',
++                        changelog]) # needed by dch..
++            check_call(cmd, stderr=file(os.devnull), shell=True)
++            check_call(["dpkg-parsechangelog", "-l%s" % changelog], stdout=sys.stdout)
++        except CalledProcessError, err:
++            raise LGPCommandException("bad changelog substitution", err)
++        check_debian_changelog(self, osp.join(origpath, "debian"))

WIP-lgp-copyback.diff

+# HG changeset patch
+# Date 1304796219 -7200
+# User Julien Jehannet <julien@smaf.org>
+# Parent 144c32213431603ad8e502c3242f575e739b805e
+
+diff --git a/lgp/setupinfo.py b/lgp/setupinfo.py
+--- a/lgp/setupinfo.py
++++ b/lgp/setupinfo.py
+@@ -34,7 +34,6 @@ from multiprocessing import Lock
+ 
+ from debian import deb822
+ 
+-from logilab.common.shellutils import cp
+ from logilab.common import clcommands
+ from logilab.common.logging_ext import ColorFormatter
+ 
+@@ -432,20 +431,20 @@ class SetupInfo(clcommands.Command):
+         :see: dcmd command
+         :todo: add more checks: sizes, checksums, etc... (ex: _check_file)
+         :todo: support other source package formats
+-        :todo: define API and/or integrate software (dput, curl, scp) ?
++        :todo: define API and/or integrate software (dput, curl, scp, ldi) ?
+         """
+         assert isinstance(filelist, list), "must be a list to be able to extend"
+         self.packages = []
+ 
+-        def _check_file(filename):
++        def _check_file(fullpath, filename):
+             if os.path.isfile(filename):
+                 hash1 = hashlib.md5(open(fullpath).read()).hexdigest()
+                 hash2 = hashlib.md5(open(filename).read()).hexdigest()
+                 if hash1 == hash2:
+-                    self.logger.debug("overwrite same file file '%s'" % filename)
++                    self.logger.debug("overwrite same file '%s'" % filename)
+                 else:
+-                    self.logger.warn("theses files shouldn't be different:\n- %s (%s)\n- %s (%s)"
+-                                     % (fullpath, hash1, filename, hash2))
++                    self.logger.error("theses files shouldn't be different:\n- %s (%s)\n- %s (%s)"
++                                      % (fullpath, hash1, filename, hash2))
+                     os.system('diff -u %s %s' % (fullpath, filename))
+                     raise LGPException("bad md5 sums of source archives (tarball)")
+ 
+@@ -478,9 +477,11 @@ class SetupInfo(clcommands.Command):
+ 
+         while filelist:
+             fullpath = filelist.pop()
++            assert os.path.isfile(fullpath), "%s not found!" % fullpath
+             path, filename = osp.split(fullpath)
+-            assert os.path.isfile(fullpath), "%s not found!" % fullpath
+-            copied_filename = os.path.join(resultdir, osp.basename(filename))
++            copied_filename = os.path.join(resultdir, filename)
++            shutil.copy2(fullpath, copied_filename)
++            #_check_file(fullpath, copied_filename)
+ 
+             if filename.endswith(('.changes', '.dsc')):
+                 contents = deb822.Deb822(file(fullpath))
+@@ -488,21 +489,13 @@ class SetupInfo(clcommands.Command):
+                                  for f in contents['Files'].split('\n')
+                                  if f])
+             #self.logger.debug('copying: %s -> %s ... \npending: %s' % (filename, copied_filename, filelist))
+-
+             if filename.endswith('.dsc'):
+-                #_check_file(copied_filename)
+                 _check_pristine()
+             if filename.endswith(".log"):
+                 self.logger.info("available build logfile: %s" % copied_filename)
+             if filename.endswith('.changes'):
+                 self.logger.info("Debian changes file: %s" % copied_filename)
+-                #_check_file(copied_filename)
+                 self._sign_file(fullpath)
+-            #if filename.endswith('.diff.gz'):
+-            #    _check_file(copied_filename)
+-
+-            cp(fullpath, copied_filename)
+-            assert osp.exists(copied_filename)
+             self.packages.append(copied_filename)
+ 
+         # report files to the console

WIP-login-x11.diff

+# HG changeset patch
+# Parent 0489704c40ffd7723c694da5903eb9745a91eef7
+# Date 1303048526 -7200
+# User Julien Jehannet <julien@smaf.org>
+
+
+diff --git a/etc/lgp/hooks/F20x11 b/etc/lgp/hooks/F20x11
+--- a/etc/lgp/hooks/F20x11
++++ b/etc/lgp/hooks/F20x11
+@@ -1,5 +1,20 @@
+-#!/bin/sh
++#!/bin/bash
+ 
+-if [ -f "$RESULTDIR/.Xauthority" ]; then
+-	export XAUTHORITY="$RESULTDIR/.Xauthority"
+-fi
++. /tmp/hooks/lgp.functions
++
++apt-get install -y --force-yes xauth >/dev/null 2>&1 &
++
++echo
++read_input 'Put xauthority entry line (from "xauth list $DISPLAY" in another terminal) ?'
++read
++
++while ! which xauth >/dev/null; do sleep 1; echo -n "."; done
++echo
++xauth add $REPLY || {
++eprint_hook "wrong xauthority entry line: $REPLY"
++xauth list
++exit 1
++}
++
++DISPLAY=$(xauth list | cut -d' ' -f1)
++print_hook "To enable x11 forwarding, be sure to export correct display\n\n\texport DISPLAY=$DISPLAY\n"
+diff --git a/etc/lgp/hooks/lgp.functions b/etc/lgp/hooks/lgp.functions
+--- a/etc/lgp/hooks/lgp.functions
++++ b/etc/lgp/hooks/lgp.functions
+@@ -6,6 +6,9 @@ SCRIPT=$(basename $0)
+ 
+ shift
+ 
++read_input() {
++	echo -ne "\e[1;32m${SCRIPT}: $*\e[0;0m "
++}
+ print_hook() {
+ 	echo -e "\e[1;32m${SCRIPT}: $*\e[0;0m"
+ }
+diff --git a/lgp/login.py b/lgp/login.py
+--- a/lgp/login.py
++++ b/lgp/login.py
+@@ -29,6 +29,14 @@ class Login(SetupInfo):
+     """Log into a build image
+     """
+     name = "login"
++    options = [('x11',
++                {'action': 'store_true',
++                 'default': False,
++                 'dest' : "x11",
++                 'help': "import x11 environment in chroot",
++                 'hide': True # NOT IMPLEMENTED
++                }),
++              ]
+     cmd = "IMAGE=%s DIST=%s ARCH=%s %s %s --configfile %s --hookdir %s"
+     pbuilder_cmd = "/usr/sbin/pbuilder %s" % name
+     sudo_cmd = "/usr/bin/sudo -E"
+@@ -40,10 +48,18 @@ class Login(SetupInfo):
+         pass
+ 
+     def run(self, args):
++        xauthority = os.path.abspath(os.environ.get('XAUTHORITY',
++                                     os.path.expanduser('~/.Xauthority')))
+         for arch in self.architectures:
+             for distrib in self.distributions:
+                 image = self.get_basetgz(distrib, arch)
+ 
++                if self.config.x11 and os.path.isfile(xauthority):
++                    # file we be copied to $HOME in the chroot
++                    # XAUTHORITY copied to $HOME in the chroot
++                    self.cmd += ' --inputfile %s' % xauthority
++                    logging.info('copy XAUTHORITY file to enable x11 environment')
++
+                 cmd = self.cmd % (image, distrib, arch, self.sudo_cmd,
+                                   self.pbuilder_cmd, CONFIG_FILE, HOOKS_DIR)
+ 

build.diff

-# HG changeset patch
-# Date 1304802311 -7200
-# User Julien Jehannet <julien@smaf.org>
-# Parent 87b0743914ab16d444d95e2c73d90c050fccfd4a
-
-diff --git a/.hgtags b/.hgtags
---- a/.hgtags
-+++ b/.hgtags
-@@ -136,3 +136,4 @@ 16198ddfd78792688a5cd2a5209bcd29e1778b51
- 1a47e33c41c5dda8191b037449accfd20e87a464 logilab-devtools-version-0.17.4-3
- e48596ffcb3bcc9723bbf6ac01f9af95cfb0b414 logilab-devtools-version-0.17.5
- 98faa304f783c495f14dc3e53399d4f36c3f3bfe logilab-devtools-debian-version-0.17.5-1
-+a8f218adba20c6dfb51ee614c0512cad6ac9d78a AAAA
-diff --git a/lgp/build.py b/lgp/build.py
---- a/lgp/build.py
-+++ b/lgp/build.py
-@@ -46,22 +46,22 @@ class Builder(SetupInfo):
-     """
-     name = "build"
-     options = DscBuilder.options + [
--               ('debug',
-+               ('hooks',
-                 {'type': 'csv',
--                  'dest': 'debug_hooks',
--                  'short': 'D',
--                  'metavar': "<debug hook>",
--                  'help': "list of action hook to enable in debug mode"
--                          "(available action: autoshell)",
-+                  'dest': 'hooks',
-+                  'short': 'H',
-+                  'metavar': "<action hooks>",
-+                  'help': "use '*' to enable Lgp hooks. "
-+                          "You can tune them some by passing actions "
-+                          "(available: autoshell, lintian)",
-                  'group': 'Pbuilder',
-                 }),
--               # XXX should be enabled by default ?
--               ('hooks',
-+               ('hooks-dir',
-                 {'type': 'string',
--                 'default': '', # check if new HOOKS_DIR
--                 'dest' : "hooks",
--                 'help': ("use yes/no to run pbuilder hooks in given directory."
--                          "You can also pass your own hook directory (default: %s)" % HOOKS_DIR),
-+                 'default': HOOKS_DIR,
-+                 'dest' : "hooks_dir",
-+                 'help': ("directory from where pbuilder will run hooks "
-+                          "if enabled (default: %s)" % HOOKS_DIR),
-                  'group': 'Pbuilder'
-                 }),
-                ('post-treatments',
-@@ -99,6 +99,7 @@ class Builder(SetupInfo):
-             raise LGPException(msg)
- 
-     def guess_environment(self):
-+        self.config.hooks_dir = self._normpath(self.config.hooks_dir)
-         self.config.orig_tarball = self._normpath(self.config.orig_tarball)
-         self.config.dscfile = self._normpath(self.config.dscfile)
-         
-@@ -300,14 +301,17 @@ class Builder(SetupInfo):
-                 cmd.append('--debug')
-             if build_vars["buildopts"]:
-                 cmd.extend(['--debbuildopts', "%(buildopts)s"])
--            if self.config.hooks.lower() != "no":
--                cmd.extend(['--hookdir', self.config.hooks])
-+            if self.config.hooks:
-+                cmd.extend(['--hookdir', self.config.hooks_dir])
-             cmd.append('%(dscfile)s')
--            # manage extra hook parameters used in debug mode
--            if self.config.debug_hooks:
--                self.config.debug_hooks.insert(0, ' --')
--                actions = ' --'.join(self.config.debug_hooks)
--                cmd.append(actions)
-+            # manage extra hook actions
-+            if self.config.hooks:
-+                hooks = ['lintian', 'autoshell']
-+                if '*' in self.config.hooks:
-+                    self.config.hooks = hooks
-+                self.config.hooks.insert(0, ' --')
-+                hooks = ' --'.join(self.config.hooks)
-+                cmd.append(hooks)
-         # XXX abstract builder methods
-         elif debbuilder == 'debuild':
-             cmd = ['debuild', '--no-tgz-check', '--no-lintian',

clcommands-build-pristine.diff

 # HG changeset patch
-# Date 1304618588 -7200
+# Date 1304981447 -7200
 # User Julien Jehannet <julien@smaf.org>
-# Parent de33865e5184689726812ae0147f8c416d8e0be2
+# Parent f09ad7db775fa80fbbad5e8b0d819aa9be776bad
 [command] build-pristine: new lgp command for pristine tarball creation
 
 New:
 @@ -17,7 +17,7 @@
  # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  
- __all__ = ["clean", "build", "check", "project", "tag", "setup", "login",
--           "piuparts", "script"]
-+           "piuparts", "script", "build_pristine"]
+ __all__ = ["clean", "build", "check", "project", "tag", "setup", # piuparts
+-           "login", "script"]
++           "login", "script", "build_pristine"]
  
  import os, sys
  import logging
 new file mode 100644
 --- /dev/null
 +++ b/lgp/build_pristine.py
-@@ -0,0 +1,173 @@
+@@ -0,0 +1,172 @@
 +# -*- coding: utf-8 -*-
 +#
 +# Copyright (c) 2011 LOGILAB S.A. (Paris, FRANCE).
 +    """Create a reasonable upstream pristine tarball
 +    """
 +    name = "build-pristine"
-+    options = SetupInfo.options + [
-+               ('pristine-tarball',
++    options = [('pristine-tarball',
 +                {'type': 'string',
 +                 'default' : None,
 +                 'dest': 'orig_tarball',
 +                             self.config.orig_tarball)
 +
 +    def run(self, args):
-+        Cleaner().run(args)
++        Cleaner(config=self.config).run(args)
 +        with build_context(self.config.keep_tmpdir) as tmpdir:
 +            pristine_tarball = self.make_orig_tarball(tmpdir=tmpdir)
 +            fullpath = osp.join('..', osp.basename(pristine_tarball))
  import tempfile
  from string import Template
  from distutils.core import run_setup
-@@ -396,87 +395,6 @@ class SetupInfo(clcommands.Command):
+@@ -397,87 +396,6 @@ class SetupInfo(clcommands.Command):
              msg %= (upstream_version, debian_upstream_version)
              raise LGPException(msg)
  
      def prepare_source_archive(self):
          """prepare and extract the upstream tarball
  
-@@ -486,7 +404,7 @@ class SetupInfo(clcommands.Command):
+@@ -487,7 +405,7 @@ class SetupInfo(clcommands.Command):
          self.create_tmp_context()
  
          # Mandatory to be compatible with format 1.0

clcommands-build-source.diff

 # HG changeset patch
-# Date 1304636566 -7200
+# Date 1304990940 -7200
 # User Julien Jehannet <julien@smaf.org>
-# Parent 9da51840090b0c5af4652b28f069da50e6892005
+# Parent 35a0016306ddd05afec19707c9536d664fea90e7
 [command] add multiprocessing capabilities and new build-source command
 
 New:
 * new command build-source command:
   new lgp command for Debian source archive creation
 * new build option `-dscfile` to reuse Debian source control file (.dsc)
+* refactor _package object to be stored directly in config for better reusing
 
 Changes:
 * get rid of the 'current_distrib' instance variable which is not appropriate in parallel mode
 @@ -17,7 +17,7 @@
  # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  
- __all__ = ["clean", "build", "check", "project", "tag", "setup", "login",
--           "piuparts", "script", "build_pristine"]
-+           "piuparts", "script", "build_pristine", "build_source"]
+ __all__ = ["clean", "build", "check", "project", "tag", "setup", # piuparts
+-           "login", "script", "build_pristine"]
++           "login", "script", "build_pristine", "build_source"]
  
  import os, sys
  import logging
 diff --git a/lgp/build.py b/lgp/build.py
 --- a/lgp/build.py
 +++ b/lgp/build.py
-@@ -18,90 +18,42 @@
+@@ -18,91 +18,43 @@
  
  import os
  import sys
 -
  @LGP.register
  class Builder(SetupInfo):
-     """Provides functions to build a debian package for a python package
+     """Provides functions to build a debian package
+ 
 -    You can change options in /etc/lgp/lgprc inside the [LGP-BUILD] section
 +    You can change options in /etc/lgp/lgprc inside the [BUILD] section
 +
      name = "build"
 -    options = Pristine.options + [
 -               ('result',
--                {'type': 'string',
++    options = DscBuilder.options + [
++               ('hooks-dir',
+                 {'type': 'string',
 -                 'default' : '~/dists',
 -                 'dest' : "dist_dir",
 -                 'short': 'r',
 -                 'help': "obtain a debian source package without build",
 -                 'group': 'Debian'
 -                }),
-+    options = DscBuilder.options + [
-                ('hooks',
-                 {'type': 'string',
-                  'default': '', # check if new HOOKS_DIR
-                  'dest' : "hooks",
+-               ('hooks',
+-                {'type': 'string',
+-                 'default': '', # check if new HOOKS_DIR
+-                 'dest' : "hooks",
 -                 'help': "run pbuilder hooks in '%s'" % HOOKS_DIR,
-+                 'help': ("use yes/no to run pbuilder hooks in given directory."
-+                          "You can also pass your own hook directory (default: %s)" % HOOKS_DIR),
++                 'default': HOOKS_DIR,
++                 'dest' : "hooks_dir",
++                 'help': ("directory from where pbuilder will run hooks "
++                          "if enabled (default: %s)" % HOOKS_DIR),
                   'group': 'Pbuilder'
                  }),
 -               # use yes/no types here to configure globally
                 ('post-treatments',
                  {'type': 'yn',
                   'default': False,
-@@ -109,16 +61,25 @@ class Builder(SetupInfo):
+@@ -110,16 +62,25 @@ class Builder(SetupInfo):
                   'help': "run embedded post-treatments: add trivial repository",
                   'group': 'Debian'
                  }),
  
      def _prune_pkg_dir(self):
          super(Builder, self)._prune_pkg_dir()
-@@ -127,191 +88,147 @@ class Builder(SetupInfo):
+@@ -128,191 +89,148 @@ class Builder(SetupInfo):
                     "Lgp expects a Debian directory here.")
              raise LGPException(msg)
  
 -                logging.warn("temporary directory not deleted: %s (%s)"
 -                             % (t, ", ".join(c)))
 +    def guess_environment(self):
++        self.config.hooks_dir = self._normpath(self.config.hooks_dir)
 +        self.config.orig_tarball = self._normpath(self.config.orig_tarball)
 +        self.config.dscfile = self._normpath(self.config.dscfile)
 +        
  
      def run(self, args):
 -        Cleaner(None).run(args)
-+        Cleaner().run(args)
++        Cleaner(config=self.config).run(args)
 +        
 +        jobs = self.config.jobs > 0 and self.config.jobs or 1
 +        self.available_procs = BoundedSemaphore(jobs)
 +            self.joblist.append(p)
 +
 +    def _run_build_process(self, build, verbose=0):
-+        """internal (p)builder call
++        """create new (p)builder process in background
 +        
 +        This function have to be called as a new Process instance
 +        (see exit() method at the end)
 -                                    self.get_debian_version()))
 -                return False
 +        # format build command
-+        cmd = [c % build for c in self._builder_command(build)]
++        cmd = [c % build for c in self._make_builder_command(build)]
 +        logger.debug("running build command: %s ..." % ' '.join(cmd))
 +        try:
 +            p = Popen(cmd, stdout=stdout, env={'DIST':  build['distrib'],
          """create a series of binary build command
  
          Architecture is checked against the debian/control to detect
-@@ -320,7 +237,6 @@ class Builder(SetupInfo):
+@@ -321,7 +239,6 @@ class Builder(SetupInfo):
          You have the possiblity to add some dpkg-buildpackage options with the
          DEBBUILDOPTS environment variable.
          """
  
          def _build_options(arch=None, rank=0):
              optline = list()
-@@ -338,18 +254,18 @@ class Builder(SetupInfo):
+@@ -339,18 +256,20 @@ class Builder(SetupInfo):
          series = []
          if utils.is_architecture_independent():
              options = dict()
 -            options['distrib'] = self.current_distrib
++            options['debbuilder'] = self.builder_command()
 +            options['indep'] = True
 +            options['distrib'] = distrib
              options['buildopts'] = _build_options()
              for rank, arch in enumerate(self.architectures):
                  options = dict()
 -                options['distrib'] = self.current_distrib
++                options['debbuilder'] = self.builder_command()
 +                options['indep'] = False
 +                options['distrib'] = distrib
                  options['buildopts'] = _build_options(arch, rank)
                  options['arch'] = arch
                  options['image'] = self.get_basetgz(options['distrib'],
-@@ -357,124 +273,51 @@ class Builder(SetupInfo):
+@@ -358,124 +277,49 @@ class Builder(SetupInfo):
                  series.append(options)
          return series
  
 -    def move_package_files(self, filelist, verbose=True):
 -        """move package files from the temporary build area to the result directory
-+    def _builder_command(self, build_vars):
++    def _make_builder_command(self, build_vars):
 +        # TODO Manage DEB_BUILD_OPTIONS
 +        # http://www.debian.org/doc/debian-policy/ch-source.html
 +        # XXX manage handy --othermirror to use local mirror
 +        #cmd.append(['--othermirror', "deb file:///home/juj/dists %s/" % build['distrib']])
-+        debuilder = os.environ.get('DEBUILDER', 'pbuilder')
-+        self.logger.debug("package builder flavour: '%s'" % debuilder)
-+        if debuilder.startswith('pbuilder'): # pbuilder(-uml)
++        debbuilder = build_vars['debbuilder']
++        if debbuilder.startswith('pbuilder'): # pbuilder(-uml)
 +            # TODO encapsulate builder logic into specific InternalBuilder class
 +            cmd = ['sudo', 'IMAGE=%(image)s', 'DIST=%(distrib)s',
-+                   'ARCH=%(arch)s', debuilder, 'build',
++                   'ARCH=%(arch)s', debbuilder, 'build',
 +                   '--configfile', CONFIG_FILE,
 +                   '--buildresult', '%(builddir)s']
 +            if self.config.verbose == 3: # i.e. -vvv in command line
 +                cmd.append('--debug')
 +            if build_vars["buildopts"]:
 +                cmd.extend(['--debbuildopts', "%(buildopts)s"])
-+            if self.config.hooks.lower() != "no":
-+                cmd.extend(['--hookdir', self.config.hooks])
++            if self.config.hooks_dir:
++                cmd.extend(['--hookdir', self.config.hooks_dir])
 +            cmd.append('%(dscfile)s')
 +        # XXX abstract builder methods
-+        elif debuilder == 'debuild':
++        elif debbuilder == 'debuild':
 +            cmd = ['debuild', '--no-tgz-check', '--no-lintian',
 +                   '--clear-hooks', '-uc', '-us']
-+        elif debuilder == 'fakeroot':
++        elif debbuilder == 'fakeroot':
 +            cmd = ['fakeroot', 'debian/rules', 'binary']
 +        else:
-+            cmd = debuilder.split()
++            cmd = debbuilder.split()
 +        assert isinstance(cmd, list)
 +        return cmd
  
 -        we define here the self.packages variable used by post-treatment
 -        some tests are performed before copying to result directory
-+    def run_post_treatments(self, distrib):
-+        """ Run actions after package compiling """
-+        # dpkg-scanpackages i386 /dev/null | gzip -9c > 386/Packages.gz
-+        # dpkg-scanpackages amd64 /dev/null | gzip -9c > amd64/Packages.gz
-+        # dpkg-scansources source /dev/null | gzip -9c > source/Sources.gz
-+        resultdir = self.get_distrib_dir(distrib)
-+        packages_file = osp.join(resultdir, "Packages.gz")
-+        try:
-+            cmd = "cd %s && dpkg-scanpackages -m %s /dev/null 2>/dev/null | gzip -9c > %s/Packages.gz"
-+            os.system(cmd % (osp.dirname(resultdir), distrib, resultdir))
-+        except Exception, err:
-+            self.logger.warning("cannot update Debian trivial repository for '%s'"
-+                                % resultdir)
-+        else:
-+            self.logger.debug("Debian trivial repository in '%s' updated."
-+                              % packages_file)
- 
+-
 -        :see: dcmd command
 -        :todo: add more checks: sizes, checksums, etc... (ex: _check_file)
 -        :todo: support other source package formats
 -            distrib_dir = os.path.join(distrib_dir, self.current_distrib)
 -        # check if distribution directory exists, create it if necessary
 -        os.umask(0002)
--        try:
++    def run_post_treatments(self, distrib):
++        """ Run actions after package compiling """
++        # dpkg-scanpackages i386 /dev/null | gzip -9c > 386/Packages.gz
++        # dpkg-scanpackages amd64 /dev/null | gzip -9c > amd64/Packages.gz
++        # dpkg-scansources source /dev/null | gzip -9c > source/Sources.gz
++        resultdir = self.get_distrib_dir(distrib)
++        packages_file = osp.join(resultdir, "Packages.gz")
+         try:
 -            os.makedirs(distrib_dir, 0755)
 -        except OSError, exc:
 -            # It's not a problem here to pass silently if the directory
 -        if self.config.distrib is None:
 -            self.config.distrib = 'all'
 -        super(Builder, self).guess_environment()
++            cmd = "cd %s && dpkg-scanpackages -m %s /dev/null 2>/dev/null | gzip -9c > %s/Packages.gz"
++            os.system(cmd % (osp.dirname(resultdir), distrib, resultdir))
++        except Exception, err:
++            self.logger.warning("cannot update Debian trivial repository for '%s'"
++                                % resultdir)
++        else:
++            self.logger.debug("Debian trivial repository in '%s' updated."
++                              % packages_file)
 diff --git a/lgp/build_source.py b/lgp/build_source.py
 new file mode 100644
 --- /dev/null
 +
 +@LGP.register
 +class DscBuilder(SetupInfo):
-+    """Create Debian source archive (.dsc)
++    """Create Debian source archive
 +    """
 +    name = "build-source"
-+    options = Pristine.options + [
++    options = SetupInfo.options + Pristine.options + [
 +               ('result',
 +                {'type': 'string',
 +                 'default' : '~/dists',
 +        super(DscBuilder, self).guess_environment()
 +
 +    def run(self, args):
-+        Cleaner().run(args)
++        Cleaner(config=self.config).run(args)
 +
 +        with utils.build_context(self.config.keep_tmpdir) as tmpdir:
 +            pristine = Pristine(config=self.config)
 +
 +        return self.origpath
 +
-diff --git a/lgp/check.py b/lgp/check.py
---- a/lgp/check.py
-+++ b/lgp/check.py
-@@ -531,28 +531,34 @@ def check_debsign(checker):
-     import ConfigParser
-     from logilab.devtools.lgp import LGP_CONFIG_FILE
- 
-+    enabled = "no"
-     config = ConfigParser.ConfigParser()
-     try:
-         config.readfp(open(LGP_CONFIG_FILE))
-+        msg = 'retrieve sign option value from %s: "sign=%s"'
-+        if config.has_option("LGP-BUILD", "sign"):
-+            enabled = config.get("LGP-BUILD", "sign")
-+            import warnings
-+            warnmsg = "Please, move 'sign=%s' option into new [BUILD] section"
-+            warnings.warn(warnmsg % enabled, DeprecationWarning)
-+        if config.has_option("DEBIAN", "sign"):
-+            enabled = config.get("DEBIAN", "sign")
-+        checker.logger.debug(msg % (LGP_CONFIG_FILE, enabled))
-     except IOError:
-         return OK # no config file
- 
--    if config.has_option("LGP-BUILD", "sign"):
--        enabled = config.get("LGP-BUILD", "sign")
--        # workaround for build command
--        if not hasattr(checker, 'logger'):
--            checker.logger = logging
--        checker.logger.debug('retrieve sign option value from %s: "sign=%s"'
--                             % (LGP_CONFIG_FILE, enabled))
-+    enabled = enabled[0].lower() == "y"
-+    if getattr(checker.config, "sign", None):
-+        enabled = True
- 
--        if enabled == "yes":
--            if not os.path.exists(os.path.expanduser("~/.devscripts")):
--                msg = "please, export your DEBSIGN_KEYID in ~/.devscripts (read `debsign` manual)"
--                checker.logger.error(msg)
--                return NOK
--            if 'GPG_AGENT_INFO' not in os.environ:
--                checker.logger.error('enable your gpg-agent to sign packages automatically')
--                return NOK
-+    if enabled:
-+        if not os.path.exists(os.path.expanduser("~/.devscripts")):
-+            msg = "please, export your DEBSIGN_KEYID in ~/.devscripts (read `debsign` manual)"
-+            checker.logger.error(msg)
-+            return NOK
-+        if 'GPG_AGENT_INFO' not in os.environ:
-+            checker.logger.error('enable your gpg-agent to sign packages automatically')
-+            return NOK
-     return OK
- 
- def check_package_info(checker):
 diff --git a/lgp/login.py b/lgp/login.py
 --- a/lgp/login.py
 +++ b/lgp/login.py
 diff --git a/lgp/setupinfo.py b/lgp/setupinfo.py
 --- a/lgp/setupinfo.py
 +++ b/lgp/setupinfo.py
-@@ -20,25 +20,31 @@
+@@ -20,25 +20,30 @@
  import sys
  import os
  import stat
  import os.path as osp
  import logging
 -import time
- import tempfile
+-import tempfile
 +import hashlib
 +import shutil
  from string import Template
  COMMANDS = {
          "sdist" : {
              "file": './$setup dist-gzip -e DIST_DIR=$dist_dir',
-@@ -83,7 +89,6 @@ class SetupInfo(clcommands.Command):
-                 {'type': 'string',
-                  'hide': True,
-                  'dest': "pkg_dir",
--                 'short': 'p',
-                  'metavar' : "<root of the debian project directory>",
-                 }),
-                ('verbose',
-@@ -154,6 +159,7 @@ class SetupInfo(clcommands.Command):
+@@ -166,7 +171,6 @@ class SetupInfo(clcommands.Command):
+         super(SetupInfo, self).__init__(logger)
          if config:
              self.config._update(vars(config), mode="careful")
-         self._set_package_format()
-+        exitcode = os.EX_OK
+-        self._set_package_format()
  
      def main_run(self, arguments, rcfile):
          # Load the global settings for lgp
-@@ -168,7 +174,7 @@ class SetupInfo(clcommands.Command):
-             return os.EX_OK
+@@ -199,14 +203,21 @@ class SetupInfo(clcommands.Command):
  
-         # Set verbose level and output streams
--        if getattr(self.config, 'verbose', None):
-+        if self.config.verbose:
-             logging.getLogger().setLevel(logging.DEBUG)
-         elif getattr(self.config, 'quiet', None):
-             logging.getLogger().setLevel(logging.WARN)
-@@ -192,8 +198,14 @@ class SetupInfo(clcommands.Command):
+         self.check_args(arguments)
+         self.go_into_package_dir(arguments)
+-        # we set the package for the main object and in __init__
+         self._set_package_format()
+         self.guess_environment()
          # Some package formats expect a clean state with no troubling file
          # (ex: distutils)...
          self._prune_pkg_dir()
 -        self.run(arguments)
 -        return os.EX_OK
++        exitcode = os.EX_OK
 +        try:
-+            self.exitcode = self.run(arguments)
++            exitcode = self.run(arguments)
 +        except LGPException, exc:
 +            if self.config.verbose:
 +                import traceback
 +                logging.critical(traceback.format_exc())
 +            raise exc
-+        return self.exitcode
++        # None equals os.EX_OK
++        return exitcode
  
      def go_into_package_dir(self, arguments):
          """go into package directory
-@@ -274,20 +286,10 @@ class SetupInfo(clcommands.Command):
+@@ -258,24 +269,24 @@ class SetupInfo(clcommands.Command):
+             # Logilab's specific format
+             # FIXME Format is buggy if setup_file was set to 'setup.py'
+             from logilab.devtools.lib import TextReporter
+-            self._package = PackageInfo(reporter=TextReporter(file(os.devnull, "w+")),
+-                                        directory=self.config.pkg_dir)
++            self.config._package = PackageInfo(reporter=TextReporter(file(os.devnull, "w+")),
++                                               directory=self.config.pkg_dir)
+             assert osp.isfile('setup.py'), "setup.py is still mandatory"
+         # other script can be used if compatible with the expected targets in COMMANDS
+         elif osp.isfile(setup_file):
+             if setup_file == 'setup.py':
+                 # case for python project (distutils, setuptools)
+-                self._package = run_setup(setup_file, None, stop_after="init")
++                self.config._package = run_setup(setup_file, None, stop_after="init")
+             else:
+                 # generic case: the setup file should only honor targets as:
+                 # sdist, project, version, clean (see COMMANDS)
+-                self._package = file(setup_file)
++                self.config._package = file(setup_file)
+                 if not os.stat(setup_file).st_mode & stat.S_IEXEC:
+                     raise LGPException('setup file %s has no execute permission'
+                                        % setup_file)
+         else:
+             class debian(object): pass
+-            self._package = debian()
++            self.config._package = debian()
+         logging.debug("use setup package class format: %s" % self.package_format)
+ 
+     def _prune_pkg_dir(self):
+@@ -286,22 +297,12 @@ class SetupInfo(clcommands.Command):
                  os.unlink('MANIFEST')
              spurious = "%s-%s" % (self.get_upstream_name(), self.get_upstream_version())
              if os.path.isdir(spurious):
 -
 -    @property
      def package_format(self):
-         return self._package.__class__.__name__
+-        return self._package.__class__.__name__
++        return self.config._package.__class__.__name__
  
-@@ -315,7 +317,7 @@ class SetupInfo(clcommands.Command):
+     def _run_command(self, cmd, **args):
+         """run an internal declared command as new subprocess"""
+@@ -327,7 +328,7 @@ class SetupInfo(clcommands.Command):
                                        % cmdline, process)
          return pipe
  
          """get the dynamic debian directory for the configuration override
  
          The convention is :
-@@ -327,23 +329,18 @@ class SetupInfo(clcommands.Command):
+@@ -339,23 +340,18 @@ class SetupInfo(clcommands.Command):
          """
          # TODO Check the X-Vcs-* to fetch remote Debian configuration files
          debiandir = 'debian' # default debian config location
          return debiandir
  
      get_architectures = staticmethod(utils.get_architectures)
-@@ -397,121 +394,137 @@ class SetupInfo(clcommands.Command):
+@@ -409,121 +405,137 @@ class SetupInfo(clcommands.Command):
              msg %= (upstream_version, debian_upstream_version)
              raise LGPException(msg)
  
 +        return distrib_dir
  
      def _normpath(self, path):
-         """XXX could be coded and done directly by option checker (optparse)"""
+         """helper method to normalize filepath arguments before
+@@ -540,3 +552,13 @@ class SetupInfo(clcommands.Command):
+                 raise LGPException(msg % path)
+         return path
+ 
++    @utils.cached
++    def builder_command(self):
++        debuilder = os.environ.get('DEBUILDER')
++        if debuilder:
++            self.config.builder_flavour = debuilder
++        logging.info("package builder flavour: '%s'"
++                     % self.config.builder_flavour)
++        return self.config.builder_flavour
++
++
 diff --git a/lgp/utils.py b/lgp/utils.py
 --- a/lgp/utils.py
 +++ b/lgp/utils.py

clcommands-build.diff

 # HG changeset patch
-# Date 1304281112 -7200
+# Date 1304988067 -7200
 # User Julien Jehannet <julien@smaf.org>
-# Parent d3a5356ddd90cea993f0b0614aa10fbceb54645a
+# Parent 319d7f4208d996a413e3fec0758621e0bb23bfde
 [command] build: migrate command to clcommands api
 
 diff --git a/lgp/__init__.py b/lgp/__init__.py
  
  import os
  import sys
-@@ -34,83 +29,38 @@ from debian import deb822
+@@ -34,83 +29,39 @@ from debian import deb822
  
  from logilab.common.shellutils import cp
  
 +@LGP.register
  class Builder(SetupInfo):
 -    """Lgp builder class
--
++    """Provides functions to build a debian package
+ 
 -    Specific options are added. See lgp build --help
-+    """Provides functions to build a debian package for a python package
 +    You can change options in /etc/lgp/lgprc inside the [LGP-BUILD] section
      """
 -    name = "lgp-build"
                  {'type': 'string',
                   'default' : '~/dists',
                   'dest' : "dist_dir",
-@@ -123,71 +73,78 @@ class Builder(SetupInfo):
+@@ -123,71 +74,78 @@ class Builder(SetupInfo):
                   'default' : None,
                   'dest': 'orig_tarball',
                   'metavar' : "<tarball>",
  
      def clean_tmpdirs(self):
          if not self.config.keep_tmpdir:
-@@ -204,6 +161,43 @@ class Builder(SetupInfo):
+@@ -204,6 +162,43 @@ class Builder(SetupInfo):
                  logging.warn("temporary directory not deleted: %s (%s)"
                               % (t, ", ".join(c)))
  
      def make_debian_source_package(self):
          """create a debian source package
  
-@@ -500,3 +494,9 @@ class Builder(SetupInfo):
+@@ -500,3 +495,9 @@ class Builder(SetupInfo):
              # already exists
              pass
          return distrib_dir

clcommands-check.diff

 # HG changeset patch
-# Date 1304618156 -7200
+# Date 1304990576 -7200
 # User Julien Jehannet <julien@smaf.org>
-# Parent 28e5d8ab2422f1f430c2e1a544e44caac4ecb194
+# Parent e9ae0213525cd84d40e5aca43250e08808c67230
 [command] check: migrate command to clcommands api
 
+Add and refactor some Debian tests
+
+New check:
+* repository
+  basic detection with modified files only (no remote check for now)
+
+Fix checks:
+* debian_env
+* debian_changelog
+* debsign
+
+TODO
+* debian_pkginfo not implemented
+
 diff --git a/lgp/__init__.py b/lgp/__init__.py
 --- a/lgp/__init__.py
 +++ b/lgp/__init__.py
  # this program; if not, write to the Free Software Foundation, Inc.,
  # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  
--__all__ = ["clean", "build", "project", "tag", "setup", "login", "piuparts",
+-__all__ = ["clean", "build", "project", "tag", "setup", "login", # piuparts
 -           "script"]
-+__all__ = ["clean", "build", "check", "project", "tag", "setup", "login",
-+           "piuparts", "script"]
++__all__ = ["clean", "build", "check", "project", "tag", "setup", # piuparts
++           "login", "script"]
  
  import os, sys
  import logging
  
  import os
  import stat
-@@ -36,9 +27,9 @@ from subprocess import call
+@@ -34,11 +25,12 @@ import logging
+ import itertools
+ from subprocess import call
  from os.path import basename, join, exists, isdir, isfile
++from glob import glob
  
  from logilab.devtools import BASE_EXCLUDE, templates
 +from logilab.devtools.lgp import LGP, utils
  from logilab.devtools.lib.changelog import CHANGEFILE
  from logilab.devtools.lib.manifest import (get_manifest_files, read_manifest_in,
                                             match_extensions, JUNK_EXTENSIONS)
-@@ -59,6 +50,7 @@ CHECKS = {'debian'    : set(['debian_dir
+@@ -48,7 +40,7 @@ OK, NOK = 1, 0
+ CHECKS = {'debian'    : set(['debian_dir', 'debian_rules', 'debian_copying',
+                              'debian_source_value', 'debian_env',
+                              'debian_changelog', 'debian_homepage']),
+-          'default'   : set(['builder',  'bin', 'release_number']),
++          'default'   : set(['builder',  'bin', 'release_number', 'repository']),
+           'distutils' : set(['manifest_in', 'pydistutils',]),
+           'pkginfo'   : set(['debsign', 'package_info', 'announce',
+                              'pkginfo_copyright', 'tests_directory',
+@@ -59,6 +51,7 @@ CHECKS = {'debian'    : set(['debian_dir
  
  # avoid warning from continuous integration report
  if os.environ.get('APYCOT_ROOT'):
      CHECKS['debian'].remove("debian_env")
  
  REV_LINE = re.compile('__revision__.*')
-@@ -138,36 +130,17 @@ def _check_bat(checker, bat_file):
+@@ -138,36 +131,17 @@ def _check_bat(checker, bat_file):
      return status
  
  
 -            return 0
 +@LGP.register
 +class Checker(SetupInfo):
-+    """Provides functions to check a debian package for a python package
-+    depending of the setup format.
++    """Provides functions to check a the working project
  
 -        checker.start_checks()
 +    Just run: 'lgp check --list' for available checkers
 -    name = "lgp-check"
 -    options = (('include',
 +    name = "check"
-+    options = SetupInfo.options + [('include',
++    options = SetupInfo.options + [
++               ('include',
                  {'type': 'csv',
                   'dest': 'include_checks',
                   'short': 'i',
-@@ -197,11 +170,17 @@ class Checker(SetupInfo):
+@@ -197,11 +171,17 @@ class Checker(SetupInfo):
                   'short': 'l',
                   'help': "return a list of all available check functions"
                  }),
  
      def errors(self):
          return len(self.get_checklist())-self.counter
-@@ -405,7 +384,6 @@ def check_debian_changelog(checker, debi
-         else: # failback to debian/changelog
-             check_debian_changelog(checker, 'debian')
+@@ -217,6 +197,7 @@ class Checker(SetupInfo):
+                 checks.update(CHECKS['distutils'])
+             if os.path.exists('__pkginfo__.py'):
+                 checks.update(CHECKS['pkginfo'])
++                checks.update(CHECKS['pkginfo_debsrc'])
+             if os.path.exists('debian'):
+                 checks.update(CHECKS['debian'])
+             if self.config.set_checks:
+@@ -308,13 +289,19 @@ def check_keyrings(checker):
  
+ def check_debian_env(checker):
+     """check usefull DEBFULLNAME and DEBEMAIL env variables"""
+-    if not os.environ.get('DEBFULLNAME') or not os.environ.get('DEBEMAIL'):
+-        checker.logger.warn('you had better define DEBFULLNAME and DEBEMAIL in your shell rc file')
++    email = os.environ.get('DEBEMAIL', os.environ.get('EMAIL', ""))
++    name  = os.environ.get('DEBFULLNAME', os.environ.get('NAME'))
++    if not email:
++        checker.logger.warn('to update changelog, please define DEBEMAIL or EMAIL environ variable')
++    # If DEBEMAIL or MAIL has the form "name <email>", then the maintainer
++    # name will also be taken from here if neither DEBFULLNAME nor NAME is set.
++    if not name and '<' not in email:
++        checker.logger.warn('to update changelog, please define DEBFULLNAME or NAME environ variable')
+     return OK
+ 
+ def check_pydistutils(checker):
+     """check a .pydistutils.cfg file in home firectory"""
+-    if isfile(os.path.join(os.environ['HOME'], '.pydistutils.cfg')):
++    if isfile(join(os.environ['HOME'], '.pydistutils.cfg')):
+         checker.logger.warn('your ~/.pydistutils.cfg can conflict with distutils commands')
+     return OK
+ 
+@@ -325,15 +312,15 @@ def check_builder(checker):
+     return OK
+ 
+ def check_debian_dir(checker):
+-    """check the debian directory"""
+-    debian_dir = checker.get_debian_dir()
+-    return isdir(debian_dir)
++    """check the debian directory and debian/control file"""
++    return isdir("debian") and isfile(join("debian","control"))
+ 
+ def check_debian_rules(checker):
+     """check the debian*/rules file (filemode should be "+x")"""
+-    debian_dirs = [checker.get_debian_dir(), 'debian']
++    debian_dirs = glob("debian.*")
++    debian_dirs.append('debian')
+     for debian_dir in debian_dirs:
+-        rules = os.path.join(debian_dir, 'rules')
++        rules = join(debian_dir, 'rules')
+         if isfile(rules):
+             if not is_executable(rules):
+                 msg = "check the '%s' file (filemode should be '+x')"
+@@ -345,7 +332,13 @@ def check_debian_rules(checker):
+ 
+ def check_debian_copying(checker):
+     """check debian/copyright file"""
+-    return isfile(os.path.join('debian', 'copyright'))
++    return isfile(join('debian', 'copyright'))
++
++def check_pkginfo_debsrc(checker):
++    # XXX implement tests from check_debsrc.py here
++    status = OK
++    if os.path.isfile("__pkginfo__.py"):
++        pkginfo = __import__('__pkginfo__')
+ 
+ def check_debian_source_value(checker):
+     """check debian source field value"""
+@@ -359,47 +352,52 @@ def check_debian_source_value(checker):
+ 
+ def check_debian_changelog(checker, debian_dir=None):
+     """check debian changelog error cases"""
+-    debian_dir = debian_dir or checker.get_debian_dir()
+-    CHANGELOG = os.path.join(debian_dir, 'changelog')
+-    if isfile(CHANGELOG):
+-        # verify if changelog is closed
+-        cmd = "sed -ne '/^ -- $/p' %s" % CHANGELOG
+-        _, output = commands.getstatusoutput(cmd)
+-        if output:
+-            msg = "missing attribution trailer line. '%s' is not properly closed" % CHANGELOG
+-            checker.logger.warn(msg)
+-            return
+-        # consider UNRELEASED as problematic only if not on first line
+-        cmd = "sed -ne '2,${/UNRELEASED/p}' %s" % CHANGELOG
+-        _, output = commands.getstatusoutput(cmd)
+-        if output:
+-            checker.logger.warn('UNRELEASED keyword found in debian changelog')
+-        cmd = "sed -ne '/DISTRIBUTION/p' %s" % CHANGELOG
+-        _, output = commands.getstatusoutput(cmd)
+-        if output:
+-            checker.logger.warn("some occurences of DISTRIBUTION found in %s" % CHANGELOG)
+-        # check project name coherency
+-        if checker.get_debian_name() != utils._parse_deb_project():
+-            msg = "project name mismatch: debian/control says '%s' and debian/changelog says '%s'"
+-            checker.logger.error(msg % (checker.get_debian_name(), utils._parse_deb_project()))
+-        # check maintainer field
+-        cmd = "dpkg-parsechangelog | grep '^Maintainer' | cut -d' ' -f2- | tr -d '\n'"
+-        _, output = commands.getstatusoutput(cmd)
+-        cmd = 'grep "%s" debian/control' % output
+-        cmdstatus, _ = commands.getstatusoutput(cmd)
+-        if cmdstatus:
+-            checker.logger.warn("'%s' is not found in Uploaders field" % output)
+-        # final check with Debian utility
+-        cmd = "dpkg-parsechangelog >/dev/null"
+-        _, output = commands.getstatusoutput(cmd)
+-        if output:
+-            checker.logger.error(output)
++    if debian_dir is None:
++        debian_dirs = glob("debian.*")
++        debian_dirs.append('debian')
+     else:
+-        if debian_dir == "debian":
+-            checker.logger.error("no debian*/changelog file found")
+-        else: # failback to debian/changelog
+-            check_debian_changelog(checker, 'debian')
 -
++        debian_dirs = [debian_dir]
++    for debian_dir in debian_dirs:
++        CHANGELOG = join(debian_dir, 'changelog')
++        if isfile(CHANGELOG):
++            # verify if changelog is closed
++            cmd = "sed -ne '/^ -- $/p' %s" % CHANGELOG
++            _, output = commands.getstatusoutput(cmd)
++            if output:
++                msg = "missing attribution trailer line. '%s' is not properly closed" % CHANGELOG
++                checker.logger.warn(msg)
++                return
++            # consider UNRELEASED as problematic only if not on first line
++            cmd = "sed -ne '2,${/UNRELEASED/p}' %s" % CHANGELOG
++            _, output = commands.getstatusoutput(cmd)
++            if output:
++                checker.logger.debug('UNRELEASED keyword found in debian changelog')
++            cmd = "sed -ne '/DISTRIBUTION/p' %s" % CHANGELOG
++            _, output = commands.getstatusoutput(cmd)
++            if output:
++                checker.logger.error("old DISTRIBUTION keyword found in %s" % CHANGELOG)
++            # check project name coherency
++            if checker.get_debian_name() != utils._parse_deb_project():
++                msg = "project name mismatch: debian/control says '%s' and debian/changelog says '%s'"
++                checker.logger.error(msg % (checker.get_debian_name(), utils._parse_deb_project()))
++            # check maintainer field
++            cmd = "dpkg-parsechangelog -l%s | grep '^Maintainer' | cut -d' ' -f2- | tr -d '\n'"
++            _, maintainer = commands.getstatusoutput(cmd % CHANGELOG)
++            for d in [debian_dir, "debian"]:
++                cmd = 'grep "%s" %s' % (maintainer, join(d, "control"))
++                cmdstatus, _ = commands.getstatusoutput(cmd)
++                if not cmdstatus: break
++            else:
++                checker.logger.warn("'%s' not found in Uploaders field"
++                                    % maintainer)
++            # final check with Debian utility
++            cmd = "dpkg-parsechangelog -l%s >/dev/null" % CHANGELOG
++            _, output = commands.getstatusoutput(cmd)
++            if output:
++                checker.logger.error(output)
++        else:
++            if debian_dir == "debian":
++                checker.logger.error("no debian*/changelog file found")
+ 
  def check_debian_maintainer(checker):
      """check Maintainer field in debian/control file"""
-     status = OK
-@@ -605,3 +583,4 @@ def check_pkginfo_copyright(checker):
+@@ -548,28 +546,34 @@ def check_debsign(checker):
+     import ConfigParser
+     from logilab.devtools.lgp import LGP_CONFIG_FILE
+ 
++    enabled = "no"
+     config = ConfigParser.ConfigParser()
+     try:
+         config.readfp(open(LGP_CONFIG_FILE))
++        msg = 'retrieve sign option value from %s: "sign=%s"'
++        if config.has_option("LGP-BUILD", "sign"):
++            enabled = config.get("LGP-BUILD", "sign")
++            import warnings
++            warnmsg = "Please, move 'sign=%s' option into new [DEBIAN] section"
++            warnings.warn(warnmsg % enabled, DeprecationWarning)
++        if config.has_option("DEBIAN", "sign"):
++            enabled = config.get("DEBIAN", "sign")
++        checker.logger.debug(msg % (LGP_CONFIG_FILE, enabled))
+     except IOError:
+         return OK # no config file
+ 
+-    if config.has_option("LGP-BUILD", "sign"):
+-        enabled = config.get("LGP-BUILD", "sign")
+-        # workaround for build command
+-        if not hasattr(checker, 'logger'):
+-            checker.logger = logging
+-        checker.logger.debug('retrieve sign option value from %s: "sign=%s"'
+-                             % (LGP_CONFIG_FILE, enabled))
++    enabled = enabled[0].lower() == "y"
++    if getattr(checker.config, "sign", None):
++        enabled = True
+ 
+-        if enabled == "yes":
+-            if not os.path.exists(os.path.expanduser("~/.devscripts")):
+-                msg = "please, export your DEBSIGN_KEYID in ~/.devscripts (read `debsign` manual)"
+-                checker.logger.error(msg)
+-                return NOK
+-            if 'GPG_AGENT_INFO' not in os.environ:
+-                checker.logger.error('enable your gpg-agent to sign packages automatically')
+-                return NOK
++    if enabled:
++        if not os.path.exists(os.path.expanduser("~/.devscripts")):
++            msg = "please, export your DEBSIGN_KEYID in ~/.devscripts (read `debsign` manual)"
++            checker.logger.error(msg)
++            return NOK
++        if 'GPG_AGENT_INFO' not in os.environ:
++            checker.logger.error('enable your gpg-agent to sign packages automatically')
++            return NOK
+     return OK
+ 
+ def check_package_info(checker):
+@@ -600,3 +604,19 @@ def check_pkginfo_copyright(checker):
      if not call(cmd, shell=True):
          checker.logger.warn('check copyright header of these previous files')
      return OK
 +
++def check_repository(checker):
++    """modified files have been detected in repo"""
++    from mercurial import hg, ui, error
++    try:
++        u = ui.ui()
++        repo = hg.repository(u, checker.config.pkg_dir)
++        modified = repo.status()
++        if any(modified):
++            return NOK
++            checker.logger.warn("modified files have been detected in repo")
++    except error.RepoError:
++        checker.logger.debug("no repository found")
++    return OK
++
++

clcommands-piuparts.diff

 # HG changeset patch
-# Date 1302968269 -7200
+# Date 1304980908 -7200
 # User Julien Jehannet <julien@smaf.org>
-# Parent 5bc2ee5826fe7731528851442bd541f3560dea22
+# Parent f2684de87e9a2bc073fb519f91eb28472515f13f
 [command] piuparts: migrate command to clcommands api
 
+command will not be enabled by default (for now)
+
 diff --git a/lgp/__init__.py b/lgp/__init__.py
 --- a/lgp/__init__.py
 +++ b/lgp/__init__.py
  # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  
 -__all__ = ["clean", "build", "project", "tag", "setup", "login"]
-+__all__ = ["clean", "build", "project", "tag", "setup", "login", "piuparts"]
++__all__ = ["clean", "build", "project", "tag", "setup", "login"] # piuparts
  
  import os, sys
  import logging
  
  import os
  import sys
-@@ -26,19 +22,28 @@ import logging
+@@ -26,19 +22,32 @@ import logging
  from subprocess import check_call, CalledProcessError
  from debian import (deb822, debfile)
  
 -        piuparts = Piuparts(args)
 +@LGP.register
 +class Piuparts(SetupInfo):
-+    """Execute piuparts in a chrooted distribution"""
++    """Execute piuparts in a chrooted distribution
++    
++    This feature is *EXPERIMENTAL*
++    """
 +    name = "piuparts"
-+    options = (('use-apt',
++    options = SetupInfo.options + [
++               ('use-apt',
 +                {'action': 'store_true',
 +                 'default': False,
 +                 'dest' : "use_apt",
 +                 'short': 'A',
 +                 'help': "be treated as package names and installed via apt-get instead of dpkg -i"
 +                }),
-+              ),
++              ]
 +    arguments = "[options] CHANGES FILE [CHANGES FILES]..."
 +    min_args = 1
  
              if os.path.isfile(arg):
                  packages = list()
                  if arg.endswith('.changes'):
-@@ -61,9 +66,9 @@ def run(args):
+@@ -61,9 +70,9 @@ def run(args):
                      logging.debug("overwrite image information from .deb: %s/%s"
                                    % (distrib, arch))
  
                      cmd = ['sudo', 'piuparts', '--no-symlinks',
                             '--warn-on-others',
                             #'--skip-minimize',
-@@ -72,7 +77,7 @@ def run(args):
+@@ -72,7 +81,7 @@ def run(args):
                             '--skip-cronfiles-test',
                             # the development repository can be somewhat buggy...
                             '--no-upgrade-test',
                             # just violent but too many false positives otherwise
                             '-i', "/var/lib/dpkg/triggers/File",
                             '-i', "/etc/shadow",
-@@ -82,7 +87,7 @@ def run(args):
+@@ -82,7 +91,7 @@ def run(args):
                             '-I', "'/usr/local/lib/python.*'",
                            ] + packages
  
                          cmd.insert(3, '--apt')
  
                      logging.info("execute piuparts: %s" % ' '.join(cmd))
-@@ -92,26 +97,3 @@ def run(args):
+@@ -92,26 +101,3 @@ def run(args):
                      except CalledProcessError, err:
                          raise LGPCommandException('an error occured in piuparts execution', err)
  

clcommands-project.diff

 # HG changeset patch
 # Date 1302917862 -7200
 # User Julien Jehannet <julien@smaf.org>
-# Parent 9fccdea25645b6a97180e93d4793b86c4fb2a1bb
+# Parent cc05afb96df6d0bfeeeea9021bc3b509966ef32b
 [command] project: migrate command to clcommands api
 
 diff --git a/lgp/__init__.py b/lgp/__init__.py
  # http://www.logilab.fr/ -- mailto:contact@logilab.fr
  #
  # This program is free software; you can redistribute it and/or modify it under
-@@ -16,53 +15,46 @@
+@@ -16,53 +15,43 @@
  # 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.
 -    """Main function of lgp project command"""
 -    try :
 -        project = Project(args)
-+@LGP.register
-+class Project(SetupInfo):
-+    """Print the project name and/or release version of the project
-+    \tUseful in external build scripts to avoid raw parsing (error prone)
-+    """
-+    name = "project"
-+
-+    def __init__(self, logger):
-+        self.isatty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
-+        options = [('name',
-+                    {'action': 'store_true',
-+                     'dest' : "name",
-+                     'help': "print project name",
-+                     'group': 'project'
-+                    }),
-+                   ('release',
-+                    {'action': 'store_true',
-+                     'dest' : "release",
-+                     'help': "print project release",
-+                     'group': 'project'
-+                    }),
-+                  ]
-+        self.options.extend(options) # merge parent options
-+        super(SetupInfo, self).__init__(logger)
-+
-+    def run(self, args):
-         if not set(args).intersection(set(['--name','--release'])):
+-        if not set(args).intersection(set(['--name','--release'])):
 -            project.config.release = project.config.name = True
 -        if project.config.name:
 -            sys.__stdout__.write(project.get_upstream_name() + linesep)
 -    except LGPException, exc:
 -        logging.critical(exc)
 -        return exc.exitcode()
-+            self.config.release = self.config.name = True
-+        if self.config.name:
-+            sys.__stdout__.write(self.get_upstream_name() + linesep)
-+        if self.config.release:
-+            sys.__stdout__.write(self.get_upstream_version() + linesep)
++@LGP.register
++class Project(SetupInfo):
++    """Print the project name and/or release version of the project
  
 -
 -class Project(SetupInfo):
 -    """Lgp project class
 -
 -    Only used to print the current name and release of the project
--    """
++    Useful in external build scripts to avoid raw parsing
+     """
 -    name = "lgp-project"
 -    options = (('name',
--                {'action': 'store_true',
--                 'dest' : "name",
++    name = "project"
++    arguments = "[--name | --release]"
++    options = [('name',
+                 {'action': 'store_true',
+                  'dest' : "name",
 -                 'help': "print project name"
--                }),
--               ('release',
--                {'action': 'store_true',
--                 'dest' : "release",
++                 'help': "print project name",
++                 'group': 'project'
+                 }),
+                ('release',
+                 {'action': 'store_true',
+                  'dest' : "release",
 -                 'help': "print project release"
--                }),
++                 'help': "print project release",
++                 'group': 'project'
+                 }),
 -              ),
--
++              ]
+ 
 -    def __init__(self, args):
 -        # Retrieve upstream information
 -        super(Project, self).__init__(arguments=args, options=self.options, usage=__doc__)
++    def run(self, args):
++        if not set(args).intersection(set(['--name','--release'])):
++            self.config.release = self.config.name = True
++        if self.config.name:
++            sys.__stdout__.write(self.get_upstream_name() + linesep)
++        if self.config.release:
++            sys.__stdout__.write(self.get_upstream_version() + linesep)
++
 +    def guess_environment(self):
 +        pass

clcommands-script.diff

 # HG changeset patch
-# Date 1302968286 -7200
+# Date 1304988383 -7200
 # User Julien Jehannet <julien@smaf.org>
-# Parent be6961e322c486645ce45847c25391d18d433db1
+# Parent 133f3e16c0f44c442ce50bf4eba2fdb2511df96b
 [command] script: migrate command to clcommands api
 
 diff --git a/lgp/__init__.py b/lgp/__init__.py
  # this program; if not, write to the Free Software Foundation, Inc.,
  # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  
--__all__ = ["clean", "build", "project", "tag", "setup", "login", "piuparts"]
-+__all__ = ["clean", "build", "project", "tag", "setup", "login", "piuparts",
+-__all__ = ["clean", "build", "project", "tag", "setup", "login"] # piuparts
++__all__ = ["clean", "build", "project", "tag", "setup", "login", # piuparts
 +           "script"]
  
  import os, sys
  
  import os
  import sys
-@@ -27,65 +22,18 @@ import logging
+@@ -27,65 +22,19 @@ import logging
  import glob
  from subprocess import check_call, CalledProcessError
  
 -    """Main function of lgp script command"""
 -    try :
 -        script = Script(args)
--
++@LGP.register
++class Script(SetupInfo):
++    """Execute script in a chrooted distribution
+ 
 -        #  command, = glob.glob(os.path.join(SCRIPTS_DIR, script.config.command))
 -        if len(script.arguments)==0:
 -            commands = dict(script.options)['command']['choices']
 -    """Helper class for running scripts
 -
 -    Specific options are added. See lgp script --help
-+@LGP.register
-+class Script(SetupInfo):
-+    """Execute script in a chrooted distribution
 +    Full list of scripts is provided with no argument
      """
 -    name = "lgp-script"
                  {'type': 'choice',
                   'choices': [os.path.basename(x)
                               for x
-@@ -95,11 +43,51 @@ class Script(Setup):
+@@ -95,11 +44,51 @@ class Script(Setup):
                   'metavar': "<command>",
                   'help': "script command to run with pbuilder"
                  }),

clcommands-setup.diff

 # HG changeset patch
-# Date 1302975988 -7200
+# Date 1304989116 -7200
 # User Julien Jehannet <julien@smaf.org>
-# Parent f52e59dddc1457ab41d8569bd123c79bcc215eef
+# Parent 684882f15fb6f01c0dbd733acc4f1589ed18260f
 [command] setup: migrate command to clcommands api
 
 diff --git a/lgp/__init__.py b/lgp/__init__.py
 diff --git a/lgp/setup.py b/lgp/setup.py
 --- a/lgp/setup.py
 +++ b/lgp/setup.py
-@@ -1,120 +1,64 @@
+@@ -1,120 +1,65 @@
  # -*- coding: utf-8 -*-
 -# Copyright (c) 2003-2008 LOGILAB S.A. (Paris, FRANCE).
 +#
 +    """Set up and manage build images
 +    
 +    Available commands:
++        - list:        list actual images        (default)
 +        - create:      create new image
 +        - update:      update image
 +        - clean:       clean pbuilder caches
-+        - list:        list actual images        (default)
 +        - dumpconfig:  display pbuilder settings
  
 -        if os.geteuid() != 0:
 -    """ Environment setup checker class
 -
 -    Specific options are added. See lgp setup --help
-+    Chrooted distributions will be used to isolate builds
++    Note: chrooted images will be used to isolate builds (pbuilder)
      """
 -    name = "lgp-setup"
 -
 -    options = (('command',
 +    name = "setup"
-+    options = SetupInfo.options + [('command',
++    options = SetupInfo.options + [
++               ('command',
                  {'type': 'choice',
 -                 'choices': ('create', 'update', 'dumpconfig', 'clean',),
 +                 'choices': ('list', 'create', 'update', 'dumpconfig', 'clean'),
 -        self.arch = args[1] # used in build_cmd property later
 -        return super(Setup, self).get_basetgz(*args, **kwargs)
 +              ]
-+    arguments = "-c <command> -d <distrib>"
++    arguments = "-c <command> -d <distrib> -a <arch>"
 +    max_args = 0
 +    cmd = "IMAGE=%s DIST=%s ARCH=%s %s %s %s --configfile %s --hookdir %s"
  
  
      @property
      def setarch_cmd(self):
-@@ -133,3 +77,70 @@ class Setup(SetupInfo):
+@@ -133,3 +78,70 @@ class Setup(SetupInfo):
              logging.debug('lgp setup should be run as root. sudo is used internally.')
              sudo_cmd = "/usr/bin/sudo -E"
          return sudo_cmd
 +                        continue
 +                    logging.error('an error occured in setup process: %s' % cmd)
 +
-diff --git a/lgp/setupinfo.py b/lgp/setupinfo.py
---- a/lgp/setupinfo.py
-+++ b/lgp/setupinfo.py
-@@ -160,7 +160,7 @@ class SetupInfo(clcommands.Command):
-         # Manage arguments (project path essentialy)
-         arguments = self.load_command_line_configuration(arguments)
- 
--        if self.config.dump_config:
-+        if getattr(self.config, "dump_config", None):
-             self.generate_config()
-             return os.EX_OK
- 

clcommands-tag.diff

 # HG changeset patch
 # Date 1304132396 -7200
 # User Julien Jehannet <julien@smaf.org>
-# Parent 20c182d7a6225096bac5aecaafe59158f19928ff
+# Parent dedb1f35d6e636c1f49c8f91760c33309b22a10c
 [command] tag: migrate command to clcommands api + refactoring
 
 Changes:
  # http://www.logilab.fr/ -- mailto:contact@logilab.fr
  #
  # This program is free software; you can redistribute it and/or modify it under
-@@ -16,93 +15,66 @@
+@@ -16,93 +15,68 @@
  # 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.
 -    """ Main function of lgp tag command """
 -    try :
 -        tagger = Tagger(args)
--
++@LGP.register
++class Tagger(SetupInfo):
++    """Tag the project source repository
+ 
 -        # deprecates the old 'format' attribute in config file
 -        if getattr(tagger.config, "format", None):
 -            import warnings
 -            msg = '"format" field key definitions must be renamed to "default" in /etc/lgp/lgprc'
 -            warnings.warn(msg, DeprecationWarning)
 -            tagger.config.default = tagger.config.format
--
++    A basic template system can be used in configuration
++    (please refer to the documentation for this usage)
++    Some tag templates are already provided by Lgp:
+ 
 -        # FIXME tagger.config.default should not be None if defined in lgprc
 -        config = ConfigParser.ConfigParser()
 -        config.readfp(open(LGP_CONFIG_FILE))
 -        logging.critical(exc)
 -        return exc.exitcode()
 -
-+@LGP.register
- class Tagger(SetupInfo):
+-class Tagger(SetupInfo):
 -    """Lgp tagger class
 -
 -    Specific options are added. See lgp tag --help
-+    """Tag the project source repository
-+    \tA basic template system can be used in configuration
-+    \t(please refer to the documentation for this usage)
-+    \tSome tag templates are already provided by Lgp:
-+    \t$project, $version, $debian_version, $debian_revision, $distrib, $arch
++      $project, $version, $debian_version, $debian_revision, $distrib, $arch
      """
 -    name = "lgp-tag"
 -    options = (('template',
          self.vcs_agent = get_vcs_agent(self.config.pkg_dir)
          self.version = self.get_upstream_version()
          self.project = self.get_upstream_name()
-@@ -112,115 +84,42 @@ class Tagger(SetupInfo):
+@@ -112,115 +86,42 @@ class Tagger(SetupInfo):
              self.debian_revision = self.debian_version.rsplit('-', 1)[-1]