launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #21831
[Merge] lp:~cjwatson/launchpad-buildd/translation-operation into lp:launchpad-buildd
Colin Watson has proposed merging lp:~cjwatson/launchpad-buildd/translation-operation into lp:launchpad-buildd with lp:~cjwatson/launchpad-buildd/translation-merge-states as a prerequisite.
Commit message:
Convert generate-translation-templates to the new Operation framework.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad-buildd/translation-operation/+merge/330437
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad-buildd/translation-operation into lp:launchpad-buildd.
=== modified file 'MANIFEST.in'
--- MANIFEST.in 2017-08-23 00:13:20 +0000
+++ MANIFEST.in 2017-09-08 15:58:48 +0000
@@ -1,7 +1,6 @@
include LICENSE
include Makefile
include bin/buildrecipe
-include bin/generate-translation-templates
include bin/in-target
include bin/sbuild-package
include bin/slave-prep
=== removed file 'bin/generate-translation-templates'
--- bin/generate-translation-templates 2017-09-08 15:58:48 +0000
+++ bin/generate-translation-templates 1970-01-01 00:00:00 +0000
@@ -1,69 +0,0 @@
-#!/bin/sh
-#
-# Copyright 2010-2017 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-# Buildd Slave tool to generate translation templates. Boiler plate code
-# copied from sbuild-package.
-
-# Expects build id as arg 1.
-# Expects branch url as arg 2.
-# Expects output tarball name as arg 3.
-
-# Must run as user with password-less sudo ability.
-
-exec 2>&1
-
-export LANG=C LC_ALL=C
-
-CHMOD=/bin/chmod
-CHROOT=/usr/sbin/chroot
-CP=/bin/cp
-INSTALL=/usr/bin/install
-MKDIR=/bin/mkdir
-SU=/bin/su
-SUDO=/usr/bin/sudo
-TOUCH=/usr/bin/touch
-
-BUILDID=$1
-BRANCH_URL=$2
-RESULT_NAME=$3
-
-BUILDD_HOME=/usr/share/launchpad-buildd
-BUILDD_LIB=/usr/lib/launchpad-buildd
-SLAVEBIN=$BUILDD_HOME/slavebin
-BUILD_CHROOT="$HOME/build-$BUILDID/chroot-autobuild"
-USER=$(whoami)
-
-# Debug output.
-echo "Running as $USER for build $BUILDID on $BRANCH_URL."
-echo "Results expected in $RESULT_NAME."
-
-BUILDD_PACKAGE=lpbuildd
-POTTERY=$BUILDD_PACKAGE/pottery
-# The script should be smarter about detecting the python version.
-PYMODULES=/usr/lib/pymodules/python2.7
-echo -n "Default Python in the chroot is: "
-$BUILD_CHROOT/usr/bin/python --version
-
-GENERATE_SCRIPT=$PYMODULES/$POTTERY/generate_translation_templates.py
-
-debug_exec() {
- echo "Executing '$1'..."
- $1 || echo "Got error $? from '$1'."
-}
-
-$SUDO $CHROOT $BUILD_CHROOT apt-get install -y bzr intltool || exit 200
-
-# Copy pottery files to chroot.
-debug_exec "$SUDO $MKDIR -vp $BUILD_CHROOT$PYMODULES/$BUILDD_PACKAGE"
-debug_exec "$SUDO $TOUCH $BUILD_CHROOT$PYMODULES/$BUILDD_PACKAGE/__init__.py"
-debug_exec "$SUDO $CP -vr $BUILDD_LIB/$POTTERY $BUILD_CHROOT$PYMODULES/$BUILDD_PACKAGE"
-debug_exec "$SUDO $CHMOD -v -R go+rX $BUILD_CHROOT$PYMODULES/$BUILDD_PACKAGE"
-debug_exec "$SUDO $CHMOD -v 755 $BUILD_CHROOT$GENERATE_SCRIPT"
-
-# Enter chroot, switch back to unprivileged user, execute the generate script.
-$SUDO $CHROOT $BUILD_CHROOT \
- $SU - $USER \
- -c "PYTHONPATH=$PYMODULES $GENERATE_SCRIPT $BRANCH_URL $RESULT_NAME" \
- || exit 201
=== modified file 'debian/changelog'
--- debian/changelog 2017-09-08 15:58:48 +0000
+++ debian/changelog 2017-09-08 15:58:48 +0000
@@ -3,6 +3,7 @@
* Refactor lpbuildd.pottery.intltool to avoid calling chdir.
* Merge TranslationTemplatesBuildState.{INSTALL,GENERATE} into a single
state.
+ * Convert generate-translation-templates to the new Operation framework.
-- Colin Watson <cjwatson@xxxxxxxxxx> Fri, 08 Sep 2017 13:42:17 +0100
=== modified file 'debian/launchpad-buildd.install'
--- debian/launchpad-buildd.install 2017-08-23 00:13:20 +0000
+++ debian/launchpad-buildd.install 2017-09-08 15:58:48 +0000
@@ -4,7 +4,6 @@
sbuildrc usr/share/launchpad-buildd
template-buildd-slave.conf usr/share/launchpad-buildd
bin/buildrecipe usr/share/launchpad-buildd/slavebin
-bin/generate-translation-templates usr/share/launchpad-buildd/slavebin
bin/in-target usr/share/launchpad-buildd/slavebin
bin/sbuild-package usr/share/launchpad-buildd/slavebin
bin/slave-prep usr/share/launchpad-buildd/slavebin
=== modified file 'lpbuildd/pottery/intltool.py'
--- lpbuildd/pottery/intltool.py 2017-09-08 15:58:48 +0000
+++ lpbuildd/pottery/intltool.py 2017-09-08 15:58:48 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Functions to build PO templates on the build slave."""
@@ -13,27 +13,26 @@
'find_potfiles_in',
]
-import errno
import os.path
import re
import subprocess
-
-
-def find_potfiles_in(package_dir):
- """Search the current directory and its subdirectories for POTFILES.in.
-
+import tempfile
+
+
+def find_potfiles_in(backend, package_dir):
+ """Search `package_dir` and its subdirectories for POTFILES.in.
+
+ :param backend: The `Backend` where work is done.
:param package_dir: The directory to search.
:returns: A list of names of directories that contain a file
POTFILES.in, relative to `package_dir`.
"""
- result_dirs = []
- for dirpath, dirnames, dirfiles in os.walk(package_dir):
- if "POTFILES.in" in dirfiles:
- result_dirs.append(os.path.relpath(dirpath, package_dir))
- return result_dirs
-
-
-def check_potfiles_in(path):
+ paths = backend.find(
+ package_dir, include_directories=False, name="POTFILES.in")
+ return [os.path.dirname(path) for path in paths]
+
+
+def check_potfiles_in(backend, path):
"""Check if the files listed in the POTFILES.in file exist.
Running 'intltool-update -m' will perform this check and also take a
@@ -46,6 +45,7 @@
all listed files exist. The presence of the 'notexist' file tells us
that.
+ :param backend: The `Backend` where work is done.
:param path: The directory where POTFILES.in resides.
:returns: False if the directory does not exist, if an error occurred
when executing intltool-update or if files are missing from
@@ -53,29 +53,24 @@
actually exist.
"""
# Abort nicely if the directory does not exist.
- if not os.path.isdir(path):
+ if not backend.isdir(path):
return False
# Remove stale files from a previous run of intltool-update -m.
- for unlink_name in ['missing', 'notexist']:
- try:
- os.unlink(os.path.join(path, unlink_name))
- except OSError as e:
- # It's ok if the files are missing.
- if e.errno != errno.ENOENT:
- raise
+ backend.run(
+ ["rm", "-f"] +
+ [os.path.join(path, name) for name in ("missing", "notexist")])
with open("/dev/null", "w") as devnull:
try:
- subprocess.check_call(
+ backend.run(
["/usr/bin/intltool-update", "-m"],
stdout=devnull, stderr=devnull, cwd=path)
except subprocess.CalledProcessError:
return False
- notexist = os.path.join(path, "notexist")
- return not os.access(notexist, os.R_OK)
-
-
-def find_intltool_dirs(package_dir):
+ return not backend.path_exists(os.path.join(path, "notexist"))
+
+
+def find_intltool_dirs(backend, package_dir):
"""Search for directories with intltool structure.
`package_dir` and its subdirectories are searched. An 'intltool
@@ -83,12 +78,13 @@
files listed in that POTFILES.in do actually exist. The latter
condition makes sure that the file is not stale.
+ :param backend: The `Backend` where work is done.
:param package_dir: The directory to search.
:returns: A list of directory names, relative to `package_dir`.
"""
return sorted(
- podir for podir in find_potfiles_in(package_dir)
- if check_potfiles_in(os.path.join(package_dir, podir)))
+ podir for podir in find_potfiles_in(backend, package_dir)
+ if check_potfiles_in(backend, os.path.join(package_dir, podir)))
def _get_AC_PACKAGE_NAME(config_file):
@@ -127,7 +123,7 @@
return substitution.replace(subst_value)
-def get_translation_domain(dirname):
+def get_translation_domain(backend, dirname):
"""Get the translation domain for this PO directory.
Imitates some of the behavior of intltool-update to find out which
@@ -156,10 +152,12 @@
config_files = []
for filename, varname, keep_trying in locations:
path = os.path.join(dirname, filename)
- if not os.access(path, os.R_OK):
+ if not backend.path_exists(path):
# Skip non-existent files.
continue
- config_files.append(ConfigFile(path))
+ with tempfile.NamedTemporaryFile() as local_file:
+ backend.copy_out(path, local_file.name)
+ config_files.append(ConfigFile(local_file.file))
new_value = config_files[-1].getVariable(varname)
if new_value is not None:
value = new_value
@@ -181,7 +179,7 @@
return value
-def generate_pot(podir, domain):
+def generate_pot(backend, podir, domain):
"""Generate one PO template using intltool.
Although 'intltool-update -p' can try to find out the translation domain
@@ -190,6 +188,7 @@
"has an additional effect: the name of current working directory is no
more limited to 'po' or 'po-*'." We don't want that limit either.
+ :param backend: The `Backend` where work is done.
:param podir: The PO directory in which to build template.
:param domain: The translation domain to use as the name of the template.
If it is None or empty, 'messages.pot' will be used.
@@ -199,7 +198,7 @@
domain = "messages"
with open("/dev/null", "w") as devnull:
try:
- subprocess.check_call(
+ backend.run(
["/usr/bin/intltool-update", "-p", "-g", domain],
stdout=devnull, stderr=devnull, cwd=podir)
return True
@@ -207,13 +206,13 @@
return False
-def generate_pots(package_dir):
+def generate_pots(backend, package_dir):
"""Top-level function to generate all PO templates in a package."""
potpaths = []
- for podir in find_intltool_dirs(package_dir):
+ for podir in find_intltool_dirs(backend, package_dir):
full_podir = os.path.join(package_dir, podir)
- domain = get_translation_domain(full_podir)
- if generate_pot(full_podir, domain):
+ domain = get_translation_domain(backend, full_podir)
+ if generate_pot(backend, full_podir, domain):
potpaths.append(os.path.join(podir, domain + ".pot"))
return potpaths
=== modified file 'lpbuildd/pottery/tests/test_intltool.py'
--- lpbuildd/pottery/tests/test_intltool.py 2017-09-08 15:58:48 +0000
+++ lpbuildd/pottery/tests/test_intltool.py 2017-09-08 15:58:48 +0000
@@ -25,7 +25,10 @@
generate_pots,
get_translation_domain,
)
-from lpbuildd.tests.fakeslave import FakeMethod
+from lpbuildd.tests.fakeslave import (
+ FakeMethod,
+ UncontainedBackend,
+ )
class SetupTestPackageMixin:
@@ -75,71 +78,86 @@
def test_detect_potfiles_in(self):
# Find POTFILES.in in a package with multiple dirs when only one has
# POTFILES.in.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_POTFILES_in_1")
- dirs = find_potfiles_in(package_dir)
+ dirs = find_potfiles_in(backend, package_dir)
self.assertThat(dirs, MatchesSetwise(Equals("po-intltool")))
def test_detect_potfiles_in_module(self):
# Find POTFILES.in in a package with POTFILES.in at different levels.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_POTFILES_in_2")
- dirs = find_potfiles_in(package_dir)
+ dirs = find_potfiles_in(backend, package_dir)
self.assertThat(
dirs, MatchesSetwise(Equals("po"), Equals("module1/po")))
def test_check_potfiles_in_content_ok(self):
# Ideally all files listed in POTFILES.in exist in the source package.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_single_ok")
- self.assertTrue(check_potfiles_in(os.path.join(package_dir, "po")))
+ self.assertTrue(
+ check_potfiles_in(backend, os.path.join(package_dir, "po")))
def test_check_potfiles_in_content_ok_file_added(self):
# If a file is not listed in POTFILES.in, the file is still good for
# our purposes.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_single_ok")
added_path = os.path.join(package_dir, "src/sourcefile_new.c")
with open(added_path, "w") as added_file:
added_file.write("/* Test file. */")
- self.assertTrue(check_potfiles_in(os.path.join(package_dir, "po")))
+ self.assertTrue(
+ check_potfiles_in(backend, os.path.join(package_dir, "po")))
def test_check_potfiles_in_content_not_ok_file_removed(self):
# If a file is missing that is listed in POTFILES.in, the file
# intltool structure is probably broken and cannot be used for
# our purposes.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_single_ok")
os.remove(os.path.join(package_dir, "src/sourcefile1.c"))
- self.assertFalse(check_potfiles_in(os.path.join(package_dir, "po")))
+ self.assertFalse(
+ check_potfiles_in(backend, os.path.join(package_dir, "po")))
def test_check_potfiles_in_wrong_directory(self):
# Passing in the wrong directory will cause the check to fail
# gracefully and return False.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_single_ok")
- self.assertFalse(check_potfiles_in(os.path.join(package_dir, "foo")))
+ self.assertFalse(
+ check_potfiles_in(backend, os.path.join(package_dir, "foo")))
def test_find_intltool_dirs(self):
# Complete run: find all directories with intltool structure.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_full_ok")
self.assertEqual(
- ["po-module1", "po-module2"], find_intltool_dirs(package_dir))
+ ["po-module1", "po-module2"],
+ find_intltool_dirs(backend, package_dir))
def test_find_intltool_dirs_broken(self):
# Complete run: part of the intltool structure is broken.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_full_ok")
os.remove(os.path.join(package_dir, "src/module1/sourcefile1.c"))
self.assertEqual(
- ["po-module2"], find_intltool_dirs(package_dir))
+ ["po-module2"], find_intltool_dirs(backend, package_dir))
class TestIntltoolDomain(TestCase, SetupTestPackageMixin):
def test_get_translation_domain_makevars(self):
# Find a translation domain in Makevars.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_domain_makevars")
self.assertEqual(
"translationdomain",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_makevars_subst_1(self):
# Find a translation domain in Makevars, substituted from
# Makefile.in.in.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package(
"intltool_domain_base",
{
@@ -148,11 +166,12 @@
})
self.assertEqual(
"packagename-in-in",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_makevars_subst_2(self):
# Find a translation domain in Makevars, substituted from
# configure.ac.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package(
"intltool_domain_base",
{
@@ -162,21 +181,23 @@
})
self.assertEqual(
"packagename-ac",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_makefile_in_in(self):
# Find a translation domain in Makefile.in.in.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_domain_makefile_in_in")
self.assertEqual(
"packagename-in-in",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_configure_ac(self):
# Find a translation domain in configure.ac.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_domain_configure_ac")
self.assertEqual(
"packagename-ac",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def prepare_ac_init(self, parameters):
# Prepare test for various permutations of AC_INIT parameters
@@ -192,113 +213,127 @@
def test_get_translation_domain_configure_ac_init(self):
# Find a translation domain in configure.ac in AC_INIT.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_ac_init(
"packagename-ac-init, 1.0, http://bug.org")
self.assertEqual(
"packagename-ac-init",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_configure_ac_init_single_param(self):
# Find a translation domain in configure.ac in AC_INIT.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_ac_init("[Just 1 param]")
self.assertIsNone(
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_configure_ac_init_brackets(self):
# Find a translation domain in configure.ac in AC_INIT with brackets.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_ac_init(
"[packagename-ac-init], 1.0, http://bug.org")
self.assertEqual(
"packagename-ac-init",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_configure_ac_init_tarname(self):
# Find a translation domain in configure.ac in AC_INIT tar name
# parameter.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_ac_init(
"[Package name], 1.0, http://bug.org, [package-tarname]")
self.assertEqual(
"package-tarname",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_configure_ac_init_multiline(self):
# Find a translation domain in configure.ac in AC_INIT when it
# spans multiple lines.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_ac_init(
"[packagename-ac-init],\n 1.0,\n http://bug.org")
self.assertEqual(
"packagename-ac-init",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_configure_ac_init_multiline_tarname(self):
# Find a translation domain in configure.ac in AC_INIT tar name
# parameter that is on a different line.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_ac_init(
"[Package name], 1.0,\n http://bug.org, [package-tarname]")
self.assertEqual(
"package-tarname",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_configure_in(self):
# Find a translation domain in configure.in.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_domain_configure_in")
self.assertEqual(
"packagename-in",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_makefile_in_in_substitute(self):
# Find a translation domain in Makefile.in.in with substitution from
# configure.ac.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package(
"intltool_domain_makefile_in_in_substitute")
self.assertEqual(
"domainname-ac-in-in",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_makefile_in_in_substitute_same_name(self):
# Find a translation domain in Makefile.in.in with substitution from
# configure.ac from a variable with the same name as in
# Makefile.in.in.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package(
"intltool_domain_makefile_in_in_substitute_same_name")
self.assertEqual(
"packagename-ac-in-in",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_makefile_in_in_substitute_same_file(self):
# Find a translation domain in Makefile.in.in with substitution from
# the same file.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package(
"intltool_domain_makefile_in_in_substitute_same_file")
self.assertEqual(
"domain-in-in-in-in",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_makefile_in_in_substitute_broken(self):
# Find no translation domain in Makefile.in.in when the substitution
# cannot be fulfilled.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package(
"intltool_domain_makefile_in_in_substitute_broken")
self.assertIsNone(
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
def test_get_translation_domain_configure_in_substitute_version(self):
# Find a translation domain in configure.in with Makefile-style
# substitution from the same file.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package(
"intltool_domain_configure_in_substitute_version")
self.assertEqual(
"domainname-in42",
- get_translation_domain(os.path.join(package_dir, "po")))
+ get_translation_domain(backend, os.path.join(package_dir, "po")))
class TestGenerateTemplates(TestCase, SetupTestPackageMixin):
def test_generate_pot(self):
# Generate a given PO template.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_full_ok")
self.assertTrue(
- generate_pot(os.path.join(package_dir, "po-module1"), "module1"),
+ generate_pot(
+ backend, os.path.join(package_dir, "po-module1"), "module1"),
"PO template generation failed.")
expected_path = "po-module1/module1.pot"
self.assertTrue(
@@ -307,9 +342,11 @@
def test_generate_pot_no_domain(self):
# Generate a generic PO template.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_full_ok")
self.assertTrue(
- generate_pot(os.path.join(package_dir, "po-module1"), None),
+ generate_pot(
+ backend, os.path.join(package_dir, "po-module1"), None),
"PO template generation failed.")
expected_path = "po-module1/messages.pot"
self.assertTrue(
@@ -318,9 +355,10 @@
def test_generate_pot_empty_domain(self):
# Generate a generic PO template.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_full_ok")
self.assertTrue(
- generate_pot(os.path.join(package_dir, "po-module1"), ""),
+ generate_pot(backend, os.path.join(package_dir, "po-module1"), ""),
"PO template generation failed.")
expected_path = "po-module1/messages.pot"
self.assertTrue(
@@ -329,11 +367,13 @@
def test_generate_pot_not_intltool(self):
# Fail when not an intltool setup.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_full_ok")
# Cripple the setup.
os.remove(os.path.join(package_dir, "po-module1/POTFILES.in"))
self.assertFalse(
- generate_pot(os.path.join(package_dir, "po-module1"), "nothing"),
+ generate_pot(
+ backend, os.path.join(package_dir, "po-module1"), "nothing"),
"PO template generation should have failed.")
not_expected_path = "po-module1/nothing.pot"
self.assertFalse(
@@ -342,12 +382,13 @@
def test_generate_pots(self):
# Generate all PO templates in the package.
+ backend = UncontainedBackend("1")
package_dir = self.prepare_package("intltool_full_ok")
expected_paths = [
'po-module1/packagename-module1.pot',
'po-module2/packagename-module2.pot',
]
- pots_list = generate_pots(package_dir)
+ pots_list = generate_pots(backend, package_dir)
self.assertEqual(expected_paths, pots_list)
for expected_path in expected_paths:
self.assertTrue(
=== modified file 'lpbuildd/target/backend.py'
--- lpbuildd/target/backend.py 2017-08-29 08:50:41 +0000
+++ lpbuildd/target/backend.py 2017-09-08 15:58:48 +0000
@@ -88,6 +88,18 @@
except subprocess.CalledProcessError:
return False
+ def isdir(self, path):
+ """Test whether a path is a directory in the target environment.
+
+ :param path: the path to test, relative to the target environment's
+ root.
+ """
+ try:
+ self.run(["test", "-d", path])
+ return True
+ except subprocess.CalledProcessError:
+ return False
+
def islink(self, path):
"""Test whether a file is a symbolic link in the target environment.
@@ -100,19 +112,37 @@
except subprocess.CalledProcessError:
return False
+ def find(self, path, max_depth=None, include_directories=True, name=None):
+ """Find entries in `path`.
+
+ :param path: the path to the directory to search.
+ :param max_depth: do not descend more than this number of directory
+ levels: as with find(1), 1 includes the contents of `path`, 2
+ includes the contents of its subdirectories, etc.
+ :param include_directories: include entries representing
+ directories.
+ :param name: only include entries whose name is equal to this.
+ """
+ cmd = ["find", path, "-mindepth", "1"]
+ if max_depth is not None:
+ cmd.extend(["-maxdepth", str(max_depth)])
+ if not include_directories:
+ cmd.extend(["!", "-type", "d"])
+ if name is not None:
+ cmd.extend(["-name", name])
+ cmd.extend(["-printf", "%P\\0"])
+ paths = self.run(cmd, get_output=True).rstrip(b"\0").split(b"\0")
+ # XXX cjwatson 2017-08-04: Use `os.fsdecode` instead once we're on
+ # Python 3.
+ return [p.decode("UTF-8") for p in paths]
+
def listdir(self, path):
"""List a directory in the target environment.
:param path: the path to the directory to list, relative to the
target environment's root.
"""
- paths = self.run(
- ["find", path, "-mindepth", "1", "-maxdepth", "1",
- "-printf", "%P\\0"],
- get_output=True).rstrip(b"\0").split(b"\0")
- # XXX cjwatson 2017-08-04: Use `os.fsdecode` instead once we're on
- # Python 3.
- return [p.decode("UTF-8") for p in paths]
+ return self.find(path, max_depth=1)
def is_package_available(self, package):
"""Test whether a package is available in the target environment.
@@ -158,6 +188,10 @@
# Only for use in tests.
from lpbuildd.tests.fakeslave import FakeBackend
backend_factory = FakeBackend
+ elif name == "uncontained":
+ # Only for use in tests.
+ from lpbuildd.tests.fakeslave import UncontainedBackend
+ backend_factory = UncontainedBackend
else:
raise KeyError("Unknown backend: %s" % name)
return backend_factory(build_id, series=series, arch=arch)
=== modified file 'lpbuildd/target/cli.py'
--- lpbuildd/target/cli.py 2017-08-23 00:13:20 +0000
+++ lpbuildd/target/cli.py 2017-09-08 15:58:48 +0000
@@ -16,6 +16,9 @@
)
from lpbuildd.target.build_livefs import BuildLiveFS
from lpbuildd.target.build_snap import BuildSnap
+from lpbuildd.target.generate_translation_templates import (
+ GenerateTranslationTemplates,
+ )
from lpbuildd.target.lifecycle import (
Create,
KillProcesses,
@@ -48,6 +51,7 @@
"add-trusted-keys": AddTrustedKeys,
"buildlivefs": BuildLiveFS,
"buildsnap": BuildSnap,
+ "generate-translation-templates": GenerateTranslationTemplates,
"override-sources-list": OverrideSourcesList,
"mount-chroot": Start,
"remove-build": Remove,
=== renamed file 'lpbuildd/pottery/generate_translation_templates.py' => 'lpbuildd/target/generate_translation_templates.py'
--- lpbuildd/pottery/generate_translation_templates.py 2017-07-27 16:12:45 +0000
+++ lpbuildd/target/generate_translation_templates.py 2017-09-08 15:58:48 +0000
@@ -1,4 +1,3 @@
-#! /usr/bin/python
# Copyright 2010-2017 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
@@ -6,59 +5,53 @@
__metaclass__ = type
+import logging
import os.path
-import subprocess
-import sys
-import tarfile
-
-import logging
from lpbuildd.pottery import intltool
-
-
-class GenerateTranslationTemplates:
+from lpbuildd.target.operation import Operation
+
+
+logger = logging.getLogger(__name__)
+
+
+RETCODE_FAILURE_INSTALL = 200
+RETCODE_FAILURE_BUILD = 201
+
+
+class GenerateTranslationTemplates(Operation):
"""Script to generate translation templates from a branch."""
- def __init__(self, branch_spec, result_name, work_dir, log_file=None):
- """Prepare to generate templates for a branch.
-
- :param branch_spec: Either a branch URL or the path of a local
- branch. URLs are recognized by the occurrence of ':'. In
- the case of a URL, this will make up a path for the branch
- and check out the branch to there.
- :param result_name: The name of the result tarball. Should end in
- .tar.gz.
- :param work_dir: The directory to work in. Must exist.
- :param log_file: File-like object to log to. If None, defaults to
- stderr.
- """
- self.work_dir = work_dir
- self.branch_spec = branch_spec
- self.result_name = result_name
- self.logger = self._setupLogger(log_file)
-
- def _setupLogger(self, log_file):
- """Sets up and returns a logger."""
- if log_file is None:
- log_file = sys.stderr
- logger = logging.getLogger("generate-templates")
- logger.setLevel(logging.DEBUG)
- ch = logging.StreamHandler(log_file)
- ch.setLevel(logging.DEBUG)
- logger.addHandler(ch)
- return logger
+ description = "Generate templates for a branch."
+
+ @classmethod
+ def add_arguments(cls, parser):
+ super(GenerateTranslationTemplates, cls).add_arguments(parser)
+ parser.add_argument(
+ "branch_spec", help=(
+ "A branch URL or the path of a local branch. URLs are "
+ "recognised by the occurrence of ':'. In the case of a URL, "
+ "this will make up a path for the branch and check out the "
+ "branch to there."))
+ parser.add_argument(
+ "result_name",
+ help="The name of the result tarball. Should end in '.tar.gz'.")
+
+ def __init__(self, args, parser):
+ super(GenerateTranslationTemplates, self).__init__(args, parser)
+ self.work_dir = os.environ["HOME"]
def _getBranch(self):
"""Set `self.branch_dir`, and check out branch if needed."""
- if ':' in self.branch_spec:
+ if ':' in self.args.branch_spec:
# This is a branch URL. Check out the branch.
self.branch_dir = os.path.join(self.work_dir, 'source-tree')
- self.logger.info("Getting remote branch %s..." % self.branch_spec)
- self._checkout(self.branch_spec)
+ logger.info("Getting remote branch %s..." % self.args.branch_spec)
+ self._checkout(self.args.branch_spec)
else:
# This is a local filesystem path. Use the branch in-place.
- self.logger.info("Using local branch %s..." % self.branch_spec)
- self.branch_dir = self.branch_spec
+ logger.info("Using local branch %s..." % self.args.branch_spec)
+ self.branch_dir = self.args.branch_spec
def _checkout(self, branch_url):
"""Check out a source branch to generate from.
@@ -66,47 +59,41 @@
The branch is checked out to the location specified by
`self.branch_dir`.
"""
- self.logger.info(
+ logger.info(
"Exporting branch %s to %s..." % (branch_url, self.branch_dir))
- subprocess.check_call(
+ self.backend.run(
["bzr", "export", "-q", "-d", branch_url, self.branch_dir])
- self.logger.info("Exporting branch done.")
+ logger.info("Exporting branch done.")
def _makeTarball(self, files):
"""Put the given files into a tarball in the working directory."""
- tarname = os.path.join(self.work_dir, self.result_name)
- self.logger.info("Making tarball with templates in %s..." % tarname)
- tarball = tarfile.open(tarname, 'w|gz')
+ tarname = os.path.join(self.work_dir, self.args.result_name)
+ logger.info("Making tarball with templates in %s..." % tarname)
+ cmd = ["tar", "-C", self.branch_dir, "-czf", tarname]
files = [name for name in files if not name.endswith('/')]
for path in files:
full_path = os.path.join(self.branch_dir, path)
- self.logger.info("Adding template %s..." % full_path)
- tarball.add(full_path, path)
- tarball.close()
- self.logger.info("Tarball generated.")
+ logger.info("Adding template %s..." % full_path)
+ cmd.append(path)
+ self.backend.run(cmd)
+ logger.info("Tarball generated.")
- def generate(self):
+ def run(self):
"""Do It. Generate templates."""
- self.logger.info("Generating templates for %s." % self.branch_spec)
- self._getBranch()
- pots = intltool.generate_pots(self.branch_dir)
- self.logger.info("Generated %d templates." % len(pots))
- if len(pots) > 0:
- self._makeTarball(pots)
+ try:
+ self.backend.run(["apt-get", "-y", "install", "bzr", "intltool"])
+ except Exception:
+ logger.exception("Install failed")
+ return RETCODE_FAILURE_INSTALL
+ try:
+ logger.info(
+ "Generating templates for %s." % self.args.branch_spec)
+ self._getBranch()
+ pots = intltool.generate_pots(self.backend, self.branch_dir)
+ logger.info("Generated %d templates." % len(pots))
+ if len(pots) > 0:
+ self._makeTarball(pots)
+ except Exception:
+ logger.exception("Build failed")
+ return RETCODE_FAILURE_BUILD
return 0
-
-
-if __name__ == '__main__':
- if len(sys.argv) < 3:
- print("Usage: %s branch resultname [workdir]" % sys.argv[0])
- print(" 'branch' is a branch URL or directory.")
- print(" 'resultname' is the name of the result tarball.")
- print(" 'workdir' is a directory, defaults to HOME.")
- sys.exit(1)
- if len(sys.argv) == 4:
- workdir = sys.argv[3]
- else:
- workdir = os.environ['HOME']
- script = GenerateTranslationTemplates(
- sys.argv[1], sys.argv[2], workdir)
- sys.exit(script.generate())
=== modified file 'lpbuildd/target/operation.py'
--- lpbuildd/target/operation.py 2017-08-23 00:22:11 +0000
+++ lpbuildd/target/operation.py 2017-09-08 15:58:48 +0000
@@ -16,7 +16,7 @@
@classmethod
def add_arguments(cls, parser):
parser.add_argument(
- "--backend", choices=["chroot", "lxd", "fake"],
+ "--backend", choices=["chroot", "lxd", "fake", "uncontained"],
help="use this type of backend")
parser.add_argument(
"--series", metavar="SERIES", help="operate on series SERIES")
=== renamed file 'lpbuildd/pottery/tests/dummy_templates.tar.gz' => 'lpbuildd/target/tests/dummy_templates.tar.gz'
=== modified file 'lpbuildd/target/tests/test_chroot.py'
--- lpbuildd/target/tests/test_chroot.py 2017-08-29 08:50:41 +0000
+++ lpbuildd/target/tests/test_chroot.py 2017-09-08 15:58:48 +0000
@@ -173,6 +173,24 @@
expected_args,
[proc._args["args"] for proc in processes_fixture.procs])
+ def test_isdir(self):
+ self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
+ processes_fixture = self.useFixture(FakeProcesses())
+ test_proc_infos = iter([{}, {"returncode": 1}])
+ processes_fixture.add(lambda _: next(test_proc_infos), name="sudo")
+ self.assertTrue(Chroot("1", "xenial", "amd64").isdir("/dir"))
+ self.assertFalse(Chroot("1", "xenial", "amd64").isdir("/file"))
+
+ expected_args = [
+ ["sudo", "/usr/sbin/chroot",
+ "/expected/home/build-1/chroot-autobuild",
+ "linux64", "test", "-d", path]
+ for path in ("/dir", "/file")
+ ]
+ self.assertEqual(
+ expected_args,
+ [proc._args["args"] for proc in processes_fixture.procs])
+
def test_islink(self):
self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
processes_fixture = self.useFixture(FakeProcesses())
@@ -191,6 +209,46 @@
expected_args,
[proc._args["args"] for proc in processes_fixture.procs])
+ def test_find(self):
+ self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
+ processes_fixture = self.useFixture(FakeProcesses())
+ test_proc_infos = iter([
+ {"stdout": io.BytesIO(b"foo\0bar\0bar/bar\0bar/baz\0")},
+ {"stdout": io.BytesIO(b"foo\0bar\0")},
+ {"stdout": io.BytesIO(b"foo\0bar/bar\0bar/baz\0")},
+ {"stdout": io.BytesIO(b"bar\0bar/bar\0")},
+ ])
+ processes_fixture.add(lambda _: next(test_proc_infos), name="sudo")
+ self.assertEqual(
+ ["foo", "bar", "bar/bar", "bar/baz"],
+ Chroot("1", "xenial", "amd64").find("/path"))
+ self.assertEqual(
+ ["foo", "bar"],
+ Chroot("1", "xenial", "amd64").find("/path", max_depth=1))
+ self.assertEqual(
+ ["foo", "bar/bar", "bar/baz"],
+ Chroot("1", "xenial", "amd64").find(
+ "/path", include_directories=False))
+ self.assertEqual(
+ ["bar", "bar/bar"],
+ Chroot("1", "xenial", "amd64").find("/path", name="bar"))
+
+ find_prefix = [
+ "sudo", "/usr/sbin/chroot",
+ "/expected/home/build-1/chroot-autobuild",
+ "linux64", "find", "/path", "-mindepth", "1",
+ ]
+ find_suffix = ["-printf", "%P\\0"]
+ expected_args = [
+ find_prefix + find_suffix,
+ find_prefix + ["-maxdepth", "1"] + find_suffix,
+ find_prefix + ["!", "-type", "d"] + find_suffix,
+ find_prefix + ["-name", "bar"] + find_suffix,
+ ]
+ self.assertEqual(
+ expected_args,
+ [proc._args["args"] for proc in processes_fixture.procs])
+
def test_listdir(self):
self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
processes_fixture = self.useFixture(FakeProcesses())
=== renamed file 'lpbuildd/pottery/tests/test_generate_translation_templates.py' => 'lpbuildd/target/tests/test_generate_translation_templates.py'
--- lpbuildd/pottery/tests/test_generate_translation_templates.py 2017-07-27 16:12:45 +0000
+++ lpbuildd/target/tests/test_generate_translation_templates.py 2017-09-08 15:58:48 +0000
@@ -4,55 +4,72 @@
__metaclass__ = type
import os
-from StringIO import StringIO
import subprocess
-import sys
import tarfile
from fixtures import (
EnvironmentVariable,
+ FakeLogger,
TempDir,
)
from testtools import TestCase
from testtools.matchers import (
+ ContainsDict,
+ EndsWith,
Equals,
+ MatchesListwise,
MatchesSetwise,
)
-from lpbuildd import pottery
-from lpbuildd.pottery.generate_translation_templates import (
- GenerateTranslationTemplates,
- )
+from lpbuildd.target.cli import parse_args
from lpbuildd.tests.fakeslave import FakeMethod
+class MatchesCall(MatchesListwise):
+
+ def __init__(self, *args, **kwargs):
+ super(MatchesCall, self).__init__([
+ Equals(args),
+ ContainsDict(
+ {name: Equals(value) for name, value in kwargs.items()})])
+
+
class TestGenerateTranslationTemplates(TestCase):
"""Test generate-translation-templates script."""
result_name = "translation-templates.tar.gz"
+ def setUp(self):
+ super(TestGenerateTranslationTemplates, self).setUp()
+ self.home_dir = self.useFixture(TempDir()).path
+ self.useFixture(EnvironmentVariable("HOME", self.home_dir))
+ self.logger = self.useFixture(FakeLogger())
+
def test_getBranch_url(self):
# If passed a branch URL, the template generation script will
# check out that branch into a directory called "source-tree."
- branch_url = 'lp://~my/translation/branch'
-
- generator = GenerateTranslationTemplates(
- branch_url, self.result_name, self.useFixture(TempDir()).path,
- log_file=StringIO())
+ args = [
+ "generate-translation-templates",
+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
+ "lp://~my/translation/branch", self.result_name,
+ ]
+ generator = parse_args(args=args).operation
generator._checkout = FakeMethod()
generator._getBranch()
self.assertEqual(1, generator._checkout.call_count)
- self.assertTrue(generator.branch_dir.endswith('source-tree'))
+ self.assertThat(generator.branch_dir, EndsWith("source-tree"))
def test_getBranch_dir(self):
# If passed a branch directory, the template generation script
# works directly in that directory.
- branch_dir = '/home/me/branch'
-
- generator = GenerateTranslationTemplates(
- branch_dir, self.result_name, self.useFixture(TempDir()).path,
- log_file=StringIO())
+ branch_dir = "/home/me/branch"
+ args = [
+ "generate-translation-templates",
+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
+ branch_dir, self.result_name,
+ ]
+ generator = parse_args(args=args).operation
generator._checkout = FakeMethod()
generator._getBranch()
@@ -97,9 +114,12 @@
marker_text = "Ceci n'est pas cet branch."
branch_url = self._createBranch({'marker.txt': marker_text})
- generator = GenerateTranslationTemplates(
- branch_url, self.result_name, self.useFixture(TempDir()).path,
- log_file=StringIO())
+ args = [
+ "generate-translation-templates",
+ "--backend=uncontained", "--series=xenial", "--arch=amd64", "1",
+ branch_url, self.result_name,
+ ]
+ generator = parse_args(args=args).operation
generator._getBranch()
marker_path = os.path.join(generator.branch_dir, 'marker.txt')
@@ -108,8 +128,7 @@
def test_templates_tarball(self):
# Create a tarball from pot files.
- workdir = self.useFixture(TempDir()).path
- branchdir = os.path.join(workdir, 'branchdir')
+ branchdir = os.path.join(self.home_dir, 'branchdir')
dummy_tar = os.path.join(
os.path.dirname(__file__), 'dummy_templates.tar.gz')
with tarfile.open(dummy_tar, 'r|*') as tar:
@@ -118,24 +137,47 @@
member.name
for member in tar.getmembers() if not member.isdir()]
- generator = GenerateTranslationTemplates(
- branchdir, self.result_name, workdir, log_file=StringIO())
+ args = [
+ "generate-translation-templates",
+ "--backend=uncontained", "--series=xenial", "--arch=amd64", "1",
+ branchdir, self.result_name,
+ ]
+ generator = parse_args(args=args).operation
generator._getBranch()
generator._makeTarball(potnames)
- result_path = os.path.join(workdir, self.result_name)
+ result_path = os.path.join(self.home_dir, self.result_name)
with tarfile.open(result_path, 'r|*') as tar:
tarnames = tar.getnames()
self.assertThat(tarnames, MatchesSetwise(*(map(Equals, potnames))))
- def test_script(self):
- tempdir = self.useFixture(TempDir()).path
- workdir = self.useFixture(TempDir()).path
- command = [
- sys.executable,
- os.path.join(
- os.path.dirname(pottery.__file__),
- 'generate_translation_templates.py'),
- tempdir, self.result_name, workdir]
- retval = subprocess.call(
- command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- self.assertEqual(0, retval)
+ def test_run(self):
+ # Install dependencies and generate a templates tarball.
+ branch_url = "lp:~my/branch"
+ branch_dir = os.path.join(self.home_dir, "source-tree")
+ po_dir = os.path.join(branch_dir, "po")
+ result_path = os.path.join(self.home_dir, self.result_name)
+
+ args = [
+ "generate-translation-templates",
+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
+ branch_url, self.result_name,
+ ]
+ generator = parse_args(args=args).operation
+ generator.backend.add_file(os.path.join(po_dir, "POTFILES.in"), "")
+ generator.backend.add_file(
+ os.path.join(po_dir, "Makevars"), "DOMAIN = test\n")
+ generator.run()
+ self.assertThat(generator.backend.run.calls, MatchesListwise([
+ MatchesCall(["apt-get", "-y", "install", "bzr", "intltool"]),
+ MatchesCall(
+ ["bzr", "export", "-q", "-d", "lp:~my/branch", branch_dir]),
+ MatchesCall(
+ ["rm", "-f",
+ os.path.join(po_dir, "missing"),
+ os.path.join(po_dir, "notexist")]),
+ MatchesCall(["/usr/bin/intltool-update", "-m"], cwd=po_dir),
+ MatchesCall(
+ ["/usr/bin/intltool-update", "-p", "-g", "test"], cwd=po_dir),
+ MatchesCall(
+ ["tar", "-C", branch_dir, "-czf", result_path, "po/test.pot"]),
+ ]))
=== modified file 'lpbuildd/target/tests/test_lxd.py'
--- lpbuildd/target/tests/test_lxd.py 2017-09-08 00:04:23 +0000
+++ lpbuildd/target/tests/test_lxd.py 2017-09-08 15:58:48 +0000
@@ -397,6 +397,22 @@
expected_args,
[proc._args["args"] for proc in processes_fixture.procs])
+ def test_isdir(self):
+ processes_fixture = self.useFixture(FakeProcesses())
+ test_proc_infos = iter([{}, {"returncode": 1}])
+ processes_fixture.add(lambda _: next(test_proc_infos), name="lxc")
+ self.assertTrue(LXD("1", "xenial", "amd64").isdir("/dir"))
+ self.assertFalse(LXD("1", "xenial", "amd64").isdir("/file"))
+
+ expected_args = [
+ ["lxc", "exec", "lp-xenial-amd64", "--",
+ "linux64", "test", "-d", path]
+ for path in ("/dir", "/file")
+ ]
+ self.assertEqual(
+ expected_args,
+ [proc._args["args"] for proc in processes_fixture.procs])
+
def test_islink(self):
processes_fixture = self.useFixture(FakeProcesses())
test_proc_infos = iter([{}, {"returncode": 1}])
@@ -413,6 +429,45 @@
expected_args,
[proc._args["args"] for proc in processes_fixture.procs])
+ def test_find(self):
+ self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
+ processes_fixture = self.useFixture(FakeProcesses())
+ test_proc_infos = iter([
+ {"stdout": io.BytesIO(b"foo\0bar\0bar/bar\0bar/baz\0")},
+ {"stdout": io.BytesIO(b"foo\0bar\0")},
+ {"stdout": io.BytesIO(b"foo\0bar/bar\0bar/baz\0")},
+ {"stdout": io.BytesIO(b"bar\0bar/bar\0")},
+ ])
+ processes_fixture.add(lambda _: next(test_proc_infos), name="lxc")
+ self.assertEqual(
+ ["foo", "bar", "bar/bar", "bar/baz"],
+ LXD("1", "xenial", "amd64").find("/path"))
+ self.assertEqual(
+ ["foo", "bar"],
+ LXD("1", "xenial", "amd64").find("/path", max_depth=1))
+ self.assertEqual(
+ ["foo", "bar/bar", "bar/baz"],
+ LXD("1", "xenial", "amd64").find(
+ "/path", include_directories=False))
+ self.assertEqual(
+ ["bar", "bar/bar"],
+ LXD("1", "xenial", "amd64").find("/path", name="bar"))
+
+ find_prefix = [
+ "lxc", "exec", "lp-xenial-amd64", "--",
+ "linux64", "find", "/path", "-mindepth", "1",
+ ]
+ find_suffix = ["-printf", "%P\\0"]
+ expected_args = [
+ find_prefix + find_suffix,
+ find_prefix + ["-maxdepth", "1"] + find_suffix,
+ find_prefix + ["!", "-type", "d"] + find_suffix,
+ find_prefix + ["-name", "bar"] + find_suffix,
+ ]
+ self.assertEqual(
+ expected_args,
+ [proc._args["args"] for proc in processes_fixture.procs])
+
def test_listdir(self):
processes_fixture = self.useFixture(FakeProcesses())
processes_fixture.add(
=== modified file 'lpbuildd/tests/fakeslave.py'
--- lpbuildd/tests/fakeslave.py 2017-08-05 09:43:43 +0000
+++ lpbuildd/tests/fakeslave.py 2017-09-08 15:58:48 +0000
@@ -3,16 +3,23 @@
__metaclass__ = type
__all__ = [
+ 'FakeBackend',
'FakeMethod',
'FakeSlave',
+ 'UncontainedBackend',
]
import hashlib
import os
import shutil
import stat
+import subprocess
from lpbuildd.target.backend import Backend
+from lpbuildd.util import (
+ set_personality,
+ shell_escape,
+ )
class FakeMethod:
@@ -162,11 +169,75 @@
except KeyError:
return False
+ def isdir(self, path):
+ _, mode = self.backend_fs.get(path, (b"", 0))
+ return stat.S_ISDIR(mode)
+
def islink(self, path):
_, mode = self.backend_fs.get(path, (b"", 0))
return stat.S_ISLNK(mode)
- def listdir(self, path):
+ def find(self, path, max_depth=None, include_directories=True, name=None):
+ def match(backend_path, mode):
+ rel_path = os.path.relpath(backend_path, path)
+ if rel_path == os.sep or os.path.dirname(rel_path) == os.pardir:
+ return False
+ if max_depth is not None:
+ if rel_path.count(os.sep) + 1 > max_depth:
+ return False
+ if not include_directories:
+ if stat.S_ISDIR(mode):
+ return False
+ if name is not None:
+ if os.path.basename(rel_path) != name:
+ return False
+ return True
+
return [
- os.path.basename(p) for p in self.backend_fs
- if os.path.dirname(p) == path]
+ os.path.relpath(backend_path, path)
+ for backend_path, (_, mode) in self.backend_fs.items()
+ if match(backend_path, mode)]
+
+
+class UncontainedBackend(Backend):
+ """A partial backend implementation with no containment."""
+
+ def run(self, args, env=None, input_text=None, get_output=False,
+ echo=False, **kwargs):
+ """See `Backend`."""
+ if env:
+ args = ["env"] + [
+ "%s=%s" % (key, shell_escape(value))
+ for key, value in env.items()] + args
+ if self.arch is not None:
+ args = set_personality(args, self.arch, series=self.series)
+ if input_text is None and not get_output:
+ subprocess.check_call(args, **kwargs)
+ else:
+ if get_output:
+ kwargs["stdout"] = subprocess.PIPE
+ proc = subprocess.Popen(args, stdin=subprocess.PIPE, **kwargs)
+ output, _ = proc.communicate(input_text)
+ if proc.returncode:
+ raise subprocess.CalledProcessError(proc.returncode, args)
+ if get_output:
+ return output
+
+ def _copy(self, source_path, target_path):
+ if source_path == target_path:
+ raise Exception(
+ "TrivialBackend copy operations require source_path and "
+ "target_path to differ.")
+ subprocess.check_call(
+ ["cp", "--preserve=timestamps", source_path, target_path])
+
+ def copy_in(self, source_path, target_path):
+ """See `Backend`."""
+ self._copy(source_path, target_path)
+
+ def copy_out(self, source_path, target_path):
+ """See `Backend`."""
+ self._copy(source_path, target_path)
+
+ def remove(self):
+ raise NotImplementedError
=== modified file 'lpbuildd/tests/test_translationtemplatesbuildmanager.py'
--- lpbuildd/tests/test_translationtemplatesbuildmanager.py 2017-09-08 15:58:48 +0000
+++ lpbuildd/tests/test_translationtemplatesbuildmanager.py 2017-09-08 15:58:48 +0000
@@ -11,11 +11,13 @@
)
from testtools import TestCase
+from lpbuildd.target.generate_translation_templates import (
+ RETCODE_FAILURE_BUILD,
+ RETCODE_FAILURE_INSTALL,
+ )
from lpbuildd.tests.fakeslave import FakeSlave
from lpbuildd.tests.matchers import HasWaitingFiles
from lpbuildd.translationtemplates import (
- RETCODE_FAILURE_BUILD,
- RETCODE_FAILURE_INSTALL,
TranslationTemplatesBuildManager,
TranslationTemplatesBuildState,
)
@@ -76,9 +78,11 @@
self.assertEqual(
TranslationTemplatesBuildState.GENERATE, self.getState())
expected_command = [
- 'sharepath/slavebin/generate-translation-templates',
- 'sharepath/slavebin/generate-translation-templates',
- self.buildid, url, 'resultarchive'
+ 'sharepath/slavebin/in-target', 'in-target',
+ 'generate-translation-templates',
+ '--backend=chroot', '--series=xenial', '--arch=i386',
+ self.buildid,
+ url, 'resultarchive',
]
self.assertEqual(expected_command, self.buildmanager.commands[-1])
self.assertEqual(
=== modified file 'lpbuildd/translationtemplates.py'
--- lpbuildd/translationtemplates.py 2017-09-08 15:58:48 +0000
+++ lpbuildd/translationtemplates.py 2017-09-08 15:58:48 +0000
@@ -9,10 +9,10 @@
DebianBuildManager,
DebianBuildState,
)
-
-
-RETCODE_FAILURE_INSTALL = 200
-RETCODE_FAILURE_BUILD = 201
+from lpbuildd.target.generate_translation_templates import (
+ RETCODE_FAILURE_BUILD,
+ RETCODE_FAILURE_INSTALL,
+ )
class TranslationTemplatesBuildState(DebianBuildState):
@@ -31,8 +31,6 @@
def __init__(self, slave, buildid):
super(TranslationTemplatesBuildManager, self).__init__(slave, buildid)
- self._generatepath = os.path.join(
- self._slavebin, "generate-translation-templates")
self._resultname = slave._config.get(
"translationtemplatesmanager", "resultarchive")
@@ -45,10 +43,9 @@
def doGenerate(self):
"""Generate templates."""
- command = [
- self._generatepath,
- self._buildid, self._branch_url, self._resultname]
- self.runSubProcess(self._generatepath, command)
+ self.runTargetSubProcess(
+ "generate-translation-templates",
+ self._branch_url, self._resultname)
# Satisfy DebianPackageManager's needs without having a misleading
# method name here.