Merge ~cjwatson/launchpad-buildd:py3-fix-buildrecipe into launchpad-buildd:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: b61f70289cccfde7585d99abe736b2a00217f331
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad-buildd:py3-fix-buildrecipe
Merge into: launchpad-buildd:master
Diff against target: 281 lines (+128/-33)
3 files modified
bin/buildrecipe (+21/-32)
debian/changelog (+1/-0)
lpbuildd/tests/test_buildrecipe.py (+106/-1)
Reviewer Review Type Date Requested Status
Tom Wardill (community) Approve
Review via email: mp+391378@code.launchpad.net

Commit message

Fix bytes/text handling in RecipeBuilder.buildTree

To post a comment you must log in.
Revision history for this message
Tom Wardill (twom) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/bin/buildrecipe b/bin/buildrecipe
index f3034e7..b447f01 100755
--- a/bin/buildrecipe
+++ b/bin/buildrecipe
@@ -14,13 +14,7 @@ import os
14import pwd14import pwd
15import socket15import socket
16import stat16import stat
17from subprocess import (17import subprocess
18 PIPE,
19 Popen,
20 call,
21 check_call,
22 check_output,
23 )
24import sys18import sys
25import tempfile19import tempfile
26from textwrap import dedent20from textwrap import dedent
@@ -35,18 +29,15 @@ RETCODE_FAILURE_INSTALL_BUILD_DEPS = 202
35RETCODE_FAILURE_BUILD_SOURCE_PACKAGE = 20329RETCODE_FAILURE_BUILD_SOURCE_PACKAGE = 203
3630
3731
38def call_report_rusage(args, env):32def call_report(args, env):
39 """Run a subprocess.33 """Run a subprocess.
4034
41 Report that it was run, and the resources used, and complain if it fails.35 Report that it was run and complain if it fails.
4236
43 :return: The process wait status.37 :return: The process exit status.
44 """38 """
45 print('RUN %r' % args)39 print('RUN %r' % args)
46 proc = Popen(args, env=env)40 return subprocess.call(args, env=env)
47 pid, status, rusage = os.wait4(proc.pid, 0)
48 print(rusage)
49 return status
5041
5142
52class RecipeBuilder:43class RecipeBuilder:
@@ -102,31 +93,28 @@ class RecipeBuilder:
102 assert not os.path.exists(self.tree_path)93 assert not os.path.exists(self.tree_path)
103 recipe_path = os.path.join(self.work_dir, 'recipe')94 recipe_path = os.path.join(self.work_dir, 'recipe')
104 manifest_path = os.path.join(self.tree_path, 'manifest')95 manifest_path = os.path.join(self.tree_path, 'manifest')
105 recipe_file = open(recipe_path, 'rb')96 with open(recipe_path) as recipe_file:
106 try:
107 recipe = recipe_file.read()97 recipe = recipe_file.read()
108 finally:
109 recipe_file.close()
110 # As of bzr 2.2, a defined identity is needed. In this case, we're98 # As of bzr 2.2, a defined identity is needed. In this case, we're
111 # using buildd@<hostname>.99 # using buildd@<hostname>.
112 hostname = socket.gethostname()100 hostname = socket.gethostname()
113 email = 'buildd@%s' % hostname101 email = 'buildd@%s' % hostname
114 lsb_release = Popen(['/usr/bin/sudo',102 lsb_release = subprocess.Popen(['sudo',
115 '/usr/sbin/chroot', self.chroot_path, 'lsb_release',103 '/usr/sbin/chroot', self.chroot_path, 'lsb_release',
116 '-r', '-s'], stdout=PIPE)104 '-r', '-s'], stdout=subprocess.PIPE, universal_newlines=True)
117 distroseries_version = lsb_release.communicate()[0].rstrip()105 distroseries_version = lsb_release.communicate()[0].rstrip()
118 assert lsb_release.returncode == 0106 assert lsb_release.returncode == 0
119107
120 if self.git:108 if self.git:
121 print('Git version:')109 print('Git version:')
122 check_call(['git', '--version'])110 subprocess.check_call(['git', '--version'])
123 print(check_output(111 print(subprocess.check_output(
124 ['dpkg-query', '-W', 'git-build-recipe']).rstrip(112 ['dpkg-query', '-W', 'git-build-recipe'],
125 '\n').replace('\t', ' '))113 universal_newlines=True).rstrip('\n').replace('\t', ' '))
126 else:114 else:
127 print('Bazaar versions:')115 print('Bazaar versions:')
128 check_call(['bzr', 'version'])116 subprocess.check_call(['bzr', 'version'])
129 check_call(['bzr', 'plugins'])117 subprocess.check_call(['bzr', 'plugins'])
130118
131 print('Building recipe:')119 print('Building recipe:')
132 print(recipe)120 print(recipe)
@@ -149,7 +137,7 @@ class RecipeBuilder:
149 '--append-version', '~ubuntu%s.1' % distroseries_version,137 '--append-version', '~ubuntu%s.1' % distroseries_version,
150 recipe_path, self.tree_path,138 recipe_path, self.tree_path,
151 ])139 ])
152 retcode = call_report_rusage(cmd, env=env)140 retcode = call_report(cmd, env=env)
153 if retcode != 0:141 if retcode != 0:
154 return retcode142 return retcode
155 (source,) = [name for name in os.listdir(self.tree_path)143 (source,) = [name for name in os.listdir(self.tree_path)
@@ -217,14 +205,14 @@ class RecipeBuilder:
217 file=conf)205 file=conf)
218 ftparchive_env = dict(os.environ)206 ftparchive_env = dict(os.environ)
219 ftparchive_env.pop("APT_CONFIG", None)207 ftparchive_env.pop("APT_CONFIG", None)
220 ret = call(208 ret = subprocess.call(
221 ["apt-ftparchive", "-q=2", "generate", conf_path],209 ["apt-ftparchive", "-q=2", "generate", conf_path],
222 env=ftparchive_env)210 env=ftparchive_env)
223 if ret != 0:211 if ret != 0:
224 return ret212 return ret
225213
226 with open(os.path.join(self.apt_dir, "Release"), "w") as release:214 with open(os.path.join(self.apt_dir, "Release"), "w") as release:
227 return call(215 return subprocess.call(
228 ["apt-ftparchive", "-q=2", "-c", conf_path, "release",216 ["apt-ftparchive", "-q=2", "-c", conf_path, "release",
229 self.apt_dir],217 self.apt_dir],
230 stdout=release, env=ftparchive_env)218 stdout=release, env=ftparchive_env)
@@ -252,7 +240,7 @@ class RecipeBuilder:
252 if ret == 0:240 if ret == 0:
253 list_path = os.path.join(241 list_path = os.path.join(
254 self.apt_sources_list_dir, "buildrecipe-archive.list")242 self.apt_sources_list_dir, "buildrecipe-archive.list")
255 return call(['sudo', 'mv', tmp_list_path, list_path])243 return subprocess.call(['sudo', 'mv', tmp_list_path, list_path])
256 return ret244 return ret
257245
258 def setUpAptArchive(self, package):246 def setUpAptArchive(self, package):
@@ -296,7 +284,8 @@ class RecipeBuilder:
296 print("Running in chroot: %s" %284 print("Running in chroot: %s" %
297 ' '.join("'%s'" % arg for arg in args))285 ' '.join("'%s'" % arg for arg in args))
298 sys.stdout.flush()286 sys.stdout.flush()
299 return call(['sudo', '/usr/sbin/chroot', self.chroot_path] + args)287 return subprocess.call(
288 ['sudo', '/usr/sbin/chroot', self.chroot_path] + args)
300289
301 def copy_in(self, source_path, target_path):290 def copy_in(self, source_path, target_path):
302 """Copy a file into the target environment.291 """Copy a file into the target environment.
@@ -316,7 +305,7 @@ class RecipeBuilder:
316 mode = stat.S_IMODE(os.stat(source_path).st_mode)305 mode = stat.S_IMODE(os.stat(source_path).st_mode)
317 full_target_path = os.path.join(306 full_target_path = os.path.join(
318 self.chroot_path, target_path.lstrip("/"))307 self.chroot_path, target_path.lstrip("/"))
319 check_call(308 subprocess.check_call(
320 ["sudo", "install", "-o", "root", "-g", "root", "-m", "%o" % mode,309 ["sudo", "install", "-o", "root", "-g", "root", "-m", "%o" % mode,
321 source_path, full_target_path])310 source_path, full_target_path])
322311
diff --git a/debian/changelog b/debian/changelog
index e46111d..547c167 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,6 +1,7 @@
1launchpad-buildd (193) UNRELEASED; urgency=medium1launchpad-buildd (193) UNRELEASED; urgency=medium
22
3 * Fix handling of bytes arguments passed to BuildManager.runSubProcess.3 * Fix handling of bytes arguments passed to BuildManager.runSubProcess.
4 * Fix bytes/text handling in RecipeBuilder.buildTree.
45
5 -- Colin Watson <cjwatson@ubuntu.com> Thu, 24 Sep 2020 16:45:27 +01006 -- Colin Watson <cjwatson@ubuntu.com> Thu, 24 Sep 2020 16:45:27 +0100
67
diff --git a/lpbuildd/tests/test_buildrecipe.py b/lpbuildd/tests/test_buildrecipe.py
index ba93dfe..a4da180 100644
--- a/lpbuildd/tests/test_buildrecipe.py
+++ b/lpbuildd/tests/test_buildrecipe.py
@@ -7,6 +7,7 @@ __metaclass__ = type
77
8from contextlib import contextmanager8from contextlib import contextmanager
9import imp9import imp
10import io
10import os11import os
11import shutil12import shutil
12import stat13import stat
@@ -14,7 +15,10 @@ import sys
14import tempfile15import tempfile
15from textwrap import dedent16from textwrap import dedent
1617
17from fixtures import MockPatchObject18from fixtures import (
19 MockPatch,
20 MockPatchObject,
21 )
18import six22import six
19from systemfixtures import FakeProcesses23from systemfixtures import FakeProcesses
20from testtools import TestCase24from testtools import TestCase
@@ -82,6 +86,107 @@ class TestRecipeBuilder(TestCase):
82 self.resetEnvironment()86 self.resetEnvironment()
83 super(TestRecipeBuilder, self).tearDown()87 super(TestRecipeBuilder, self).tearDown()
8488
89 def test_buildTree_git(self):
90 def fake_git(args):
91 if args["args"][1] == "--version":
92 print("git version x.y.z")
93 return {}
94 else:
95 return {"returncode": 1}
96
97 def fake_git_build_recipe(args):
98 print("dummy recipe build")
99 os.makedirs(os.path.join(self.builder.tree_path, "foo"))
100 return {}
101
102 processes_fixture = self.useFixture(FakeProcesses())
103 processes_fixture.add(
104 lambda _: {"stdout": io.StringIO(u"5.10\n")}, name="sudo")
105 processes_fixture.add(fake_git, name="git")
106 processes_fixture.add(
107 lambda _: {"stdout": io.StringIO(u"git-build-recipe\tx.y.z\n")},
108 name="dpkg-query")
109 processes_fixture.add(fake_git_build_recipe, name="git-build-recipe")
110 self.builder = RecipeBuilder(
111 self.build_id, "Recipe Builder", "builder@example.org>", "grumpy",
112 "grumpy", "main", "PPA", git=True)
113 with open(os.path.join(self.builder.work_dir, "recipe"), "w") as f:
114 f.write("dummy recipe contents\n")
115 mock_stdout = six.StringIO()
116 self.useFixture(MockPatch("sys.stdout", mock_stdout))
117 self.assertEqual(0, self.builder.buildTree())
118 self.assertEqual(
119 os.path.join(self.builder.work_dir_relative, "tree", "foo"),
120 self.builder.source_dir_relative)
121 expected_recipe_command = [
122 "git-build-recipe", "--safe", "--no-build",
123 "--manifest", os.path.join(self.builder.tree_path, "manifest"),
124 "--distribution", "grumpy", "--allow-fallback-to-native",
125 "--append-version", u"~ubuntu5.10.1",
126 os.path.join(self.builder.work_dir, "recipe"),
127 self.builder.tree_path,
128 ]
129 self.assertEqual(
130 dedent("""\
131 Git version:
132 git version x.y.z
133 git-build-recipe x.y.z
134 Building recipe:
135 dummy recipe contents
136
137 RUN %s
138 dummy recipe build
139 """) % repr(expected_recipe_command),
140 mock_stdout.getvalue())
141
142 def test_buildTree_bzr(self):
143 def fake_bzr(args):
144 if args["args"][1] == "version":
145 print("bzr version x.y.z")
146 return {}
147 elif args["args"][1] == "plugins":
148 print("bzr-plugin x.y.z")
149 return {}
150 elif "dailydeb" in args["args"][1:]:
151 print("dummy recipe build")
152 os.makedirs(os.path.join(self.builder.tree_path, "foo"))
153 return {}
154 else:
155 return {"returncode": 1}
156
157 processes_fixture = self.useFixture(FakeProcesses())
158 processes_fixture.add(
159 lambda _: {"stdout": io.StringIO(u"5.10\n")}, name="sudo")
160 processes_fixture.add(fake_bzr, name="bzr")
161 with open(os.path.join(self.builder.work_dir, "recipe"), "w") as f:
162 f.write("dummy recipe contents\n")
163 mock_stdout = six.StringIO()
164 self.useFixture(MockPatch("sys.stdout", mock_stdout))
165 self.assertEqual(0, self.builder.buildTree())
166 self.assertEqual(
167 os.path.join(self.builder.work_dir_relative, "tree", "foo"),
168 self.builder.source_dir_relative)
169 expected_recipe_command = [
170 "bzr", "-Derror", "dailydeb", "--safe", "--no-build",
171 "--manifest", os.path.join(self.builder.tree_path, "manifest"),
172 "--distribution", "grumpy", "--allow-fallback-to-native",
173 "--append-version", u"~ubuntu5.10.1",
174 os.path.join(self.builder.work_dir, "recipe"),
175 self.builder.tree_path,
176 ]
177 self.assertEqual(
178 dedent("""\
179 Bazaar versions:
180 bzr version x.y.z
181 bzr-plugin x.y.z
182 Building recipe:
183 dummy recipe contents
184
185 RUN %s
186 dummy recipe build
187 """) % repr(expected_recipe_command),
188 mock_stdout.getvalue())
189
85 def test_makeDummyDsc(self):190 def test_makeDummyDsc(self):
86 self.builder.source_dir_relative = os.path.join(191 self.builder.source_dir_relative = os.path.join(
87 self.builder.work_dir_relative, "tree", "foo")192 self.builder.work_dir_relative, "tree", "foo")

Subscribers

People subscribed via source and target branches