Merge lp:~cjwatson/launchpad-buildd/snapcraft into lp:launchpad-buildd
- snapcraft
- Merge into trunk
| Status: | Merged | ||||
|---|---|---|---|---|---|
| Merged at revision: | 166 | ||||
| Proposed branch: | lp:~cjwatson/launchpad-buildd/snapcraft | ||||
| Merge into: | lp:launchpad-buildd | ||||
| Diff against target: |
573 lines (+434/-41) 9 files modified
Makefile (+1/-0) buildd-slave.tac (+2/-0) buildlivefs (+6/-40) buildsnap (+157/-0) debian/changelog (+1/-0) debian/rules (+1/-1) lpbuildd/snap.py (+95/-0) lpbuildd/tests/test_snap.py (+121/-0) lpbuildd/util.py (+50/-0) |
||||
| To merge this branch: | bzr merge lp:~cjwatson/launchpad-buildd/snapcraft | ||||
| Related bugs: |
|
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| William Grant | code | 2015-07-31 | Approve on 2015-08-04 |
|
Review via email:
|
|||
Commit Message
Add support for building snaps (LP: #1476405).
Description of the Change
Add support for building snaps.
I've successfully tested this end-to-end locally. Doing that requires fetching plainbox, ubuntu-snappy, and snapcraft from an assortment of locations (https:/
You may want to review this alongside https:/
Preview Diff
| 1 | === modified file 'Makefile' |
| 2 | --- Makefile 2015-06-15 16:48:51 +0000 |
| 3 | +++ Makefile 2015-07-31 11:57:12 +0000 |
| 4 | @@ -33,5 +33,6 @@ |
| 5 | lpbuildd.tests.test_check_implicit_pointer_functions \ |
| 6 | lpbuildd.tests.test_harness \ |
| 7 | lpbuildd.tests.test_livefs \ |
| 8 | + lpbuildd.tests.test_snap \ |
| 9 | lpbuildd.tests.test_sourcepackagerecipe \ |
| 10 | lpbuildd.tests.test_translationtemplatesbuildmanager |
| 11 | |
| 12 | === modified file 'buildd-slave.tac' |
| 13 | --- buildd-slave.tac 2015-05-11 14:37:01 +0000 |
| 14 | +++ buildd-slave.tac 2015-07-31 11:57:12 +0000 |
| 15 | @@ -23,6 +23,7 @@ |
| 16 | from lpbuildd.livefs import LiveFilesystemBuildManager |
| 17 | from lpbuildd.log import RotatableFileLogObserver |
| 18 | from lpbuildd.slave import XMLRPCBuildDSlave |
| 19 | +from lpbuildd.snap import SnapBuildManager |
| 20 | from lpbuildd.sourcepackagerecipe import SourcePackageRecipeBuildManager |
| 21 | from lpbuildd.translationtemplates import TranslationTemplatesBuildManager |
| 22 | |
| 23 | @@ -41,6 +42,7 @@ |
| 24 | slave.registerBuilder( |
| 25 | TranslationTemplatesBuildManager, 'translation-templates') |
| 26 | slave.registerBuilder(LiveFilesystemBuildManager, "livefs") |
| 27 | +slave.registerBuilder(SnapBuildManager, "snap") |
| 28 | |
| 29 | application = service.Application('BuildDSlave') |
| 30 | application.addComponent( |
| 31 | |
| 32 | === modified file 'buildlivefs' |
| 33 | --- buildlivefs 2014-06-24 14:59:08 +0000 |
| 34 | +++ buildlivefs 2015-07-31 11:57:12 +0000 |
| 35 | @@ -8,11 +8,15 @@ |
| 36 | |
| 37 | from optparse import OptionParser |
| 38 | import os |
| 39 | -import re |
| 40 | import subprocess |
| 41 | import sys |
| 42 | import traceback |
| 43 | |
| 44 | +from lpbuildd.util import ( |
| 45 | + set_personality, |
| 46 | + shell_escape, |
| 47 | + ) |
| 48 | + |
| 49 | |
| 50 | RETCODE_SUCCESS = 0 |
| 51 | RETCODE_FAILURE_INSTALL = 200 |
| 52 | @@ -29,41 +33,6 @@ |
| 53 | return os.path.join(os.environ["HOME"], "build-" + build_id, *extra) |
| 54 | |
| 55 | |
| 56 | -non_meta_re = re.compile(r'^[a-zA-Z0-9+,./:=@_-]+$') |
| 57 | - |
| 58 | -def shell_escape(arg): |
| 59 | - if non_meta_re.match(arg): |
| 60 | - return arg |
| 61 | - else: |
| 62 | - return "'%s'" % arg.replace("'", "'\\''") |
| 63 | - |
| 64 | - |
| 65 | -linux32_arches = [ |
| 66 | - "armel", |
| 67 | - "armhf", |
| 68 | - "hppa", |
| 69 | - "i386", |
| 70 | - "lpia", |
| 71 | - "mips", |
| 72 | - "mipsel", |
| 73 | - "powerpc", |
| 74 | - "s390", |
| 75 | - "sparc", |
| 76 | - ] |
| 77 | -linux64_arches = [ |
| 78 | - "alpha", |
| 79 | - "amd64", |
| 80 | - "arm64", |
| 81 | - "hppa64", |
| 82 | - "ia64", |
| 83 | - "ppc64", |
| 84 | - "ppc64el", |
| 85 | - "s390x", |
| 86 | - "sparc64", |
| 87 | - "x32", |
| 88 | - ] |
| 89 | - |
| 90 | - |
| 91 | class LiveFSBuilder: |
| 92 | """Builds a live file system.""" |
| 93 | |
| 94 | @@ -77,10 +46,7 @@ |
| 95 | |
| 96 | :param args: the command and arguments to run. |
| 97 | """ |
| 98 | - if self.options.arch in linux32_arches: |
| 99 | - args = ["linux32"] + args |
| 100 | - elif self.options.arch in linux64_arches: |
| 101 | - args = ["linux64"] + args |
| 102 | + args = set_personality(self.options.arch, args) |
| 103 | if echo: |
| 104 | print "Running in chroot: %s" % ' '.join( |
| 105 | "'%s'" % arg for arg in args) |
| 106 | |
| 107 | === added file 'buildsnap' |
| 108 | --- buildsnap 1970-01-01 00:00:00 +0000 |
| 109 | +++ buildsnap 2015-07-31 11:57:12 +0000 |
| 110 | @@ -0,0 +1,157 @@ |
| 111 | +#! /usr/bin/python -u |
| 112 | +# Copyright 2015 Canonical Ltd. This software is licensed under the |
| 113 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
| 114 | + |
| 115 | +"""A script that builds a snap.""" |
| 116 | + |
| 117 | +from __future__ import print_function |
| 118 | + |
| 119 | +__metaclass__ = type |
| 120 | + |
| 121 | +from optparse import OptionParser |
| 122 | +import os |
| 123 | +import subprocess |
| 124 | +import sys |
| 125 | +import traceback |
| 126 | + |
| 127 | +from lpbuildd.util import ( |
| 128 | + set_personality, |
| 129 | + shell_escape, |
| 130 | + ) |
| 131 | + |
| 132 | + |
| 133 | +RETCODE_SUCCESS = 0 |
| 134 | +RETCODE_FAILURE_INSTALL = 200 |
| 135 | +RETCODE_FAILURE_BUILD = 201 |
| 136 | + |
| 137 | + |
| 138 | +def get_build_path(build_id, *extra): |
| 139 | + """Generate a path within the build directory. |
| 140 | + |
| 141 | + :param build_id: the build id to use. |
| 142 | + :param extra: the extra path segments within the build directory. |
| 143 | + :return: the generated path. |
| 144 | + """ |
| 145 | + return os.path.join(os.environ["HOME"], "build-" + build_id, *extra) |
| 146 | + |
| 147 | + |
| 148 | +class SnapBuilder: |
| 149 | + """Builds a snap.""" |
| 150 | + |
| 151 | + def __init__(self, options, name): |
| 152 | + self.options = options |
| 153 | + self.name = name |
| 154 | + self.chroot_path = get_build_path( |
| 155 | + self.options.build_id, 'chroot-autobuild') |
| 156 | + # Set to False for local testing if your chroot doesn't have an |
| 157 | + # appropriate certificate for your codehosting system. |
| 158 | + self.ssl_verify = True |
| 159 | + |
| 160 | + def chroot(self, args, echo=False): |
| 161 | + """Run a command in the chroot. |
| 162 | + |
| 163 | + :param args: the command and arguments to run. |
| 164 | + :param echo: if True, print the command before executing it. |
| 165 | + """ |
| 166 | + args = set_personality(self.options.arch, args) |
| 167 | + if echo: |
| 168 | + print( |
| 169 | + "Running in chroot: %s" % ' '.join( |
| 170 | + "'%s'" % arg for arg in args)) |
| 171 | + sys.stdout.flush() |
| 172 | + subprocess.check_call([ |
| 173 | + "/usr/bin/sudo", "/usr/sbin/chroot", self.chroot_path] + args) |
| 174 | + |
| 175 | + def run_build_command(self, args, path="/build", env=None, echo=False): |
| 176 | + """Run a build command in the chroot. |
| 177 | + |
| 178 | + This is unpleasant because we need to run it in /build under sudo |
| 179 | + chroot, and there's no way to do this without either a helper |
| 180 | + program in the chroot or unpleasant quoting. We go for the |
| 181 | + unpleasant quoting. |
| 182 | + |
| 183 | + :param args: the command and arguments to run. |
| 184 | + :param path: the working directory to use in the chroot. |
| 185 | + :param env: dictionary of additional environment variables to set. |
| 186 | + :param echo: if True, print the command before executing it. |
| 187 | + """ |
| 188 | + args = [shell_escape(arg) for arg in args] |
| 189 | + path = shell_escape(path) |
| 190 | + if env: |
| 191 | + args = ["env"] + [ |
| 192 | + "%s=%s" % (key, shell_escape(value)) |
| 193 | + for key, value in env.items()] + args |
| 194 | + command = "cd %s && %s" % (path, " ".join(args)) |
| 195 | + self.chroot(["/bin/sh", "-c", command], echo=echo) |
| 196 | + |
| 197 | + def install(self): |
| 198 | + deps = ["snapcraft"] |
| 199 | + if self.options.branch is not None: |
| 200 | + deps.append("bzr") |
| 201 | + else: |
| 202 | + deps.append("git") |
| 203 | + # Fixed better by |
| 204 | + # https://code.launchpad.net/~cjwatson/snapcraft/depend-sudo/+merge/266533; |
| 205 | + # drop this when that has been merged. |
| 206 | + deps.append("sudo") |
| 207 | + self.chroot(["apt-get", "-y", "install"] + deps) |
| 208 | + |
| 209 | + def build(self): |
| 210 | + if self.options.branch is not None: |
| 211 | + self.run_build_command([ |
| 212 | + "bzr", "branch", self.options.branch, self.name]) |
| 213 | + else: |
| 214 | + assert self.options.git_repository is not None |
| 215 | + assert self.options.git_path is not None |
| 216 | + if not self.ssl_verify: |
| 217 | + env = {"GIT_SSL_NO_VERIFY": "1"} |
| 218 | + else: |
| 219 | + env = None |
| 220 | + self.run_build_command([ |
| 221 | + "git", "clone", "-b", self.options.git_path, |
| 222 | + self.options.git_repository, self.name], |
| 223 | + env=env) |
| 224 | + self.run_build_command( |
| 225 | + ["snapcraft", "assemble"], path=os.path.join("/build", self.name)) |
| 226 | + |
| 227 | + |
| 228 | +def main(): |
| 229 | + parser = OptionParser("%prog [options] NAME") |
| 230 | + parser.add_option("--build-id", help="build identifier") |
| 231 | + parser.add_option( |
| 232 | + "--arch", metavar="ARCH", help="build for architecture ARCH") |
| 233 | + parser.add_option( |
| 234 | + "--branch", metavar="BRANCH", help="build from this Bazaar branch") |
| 235 | + parser.add_option( |
| 236 | + "--git-repository", metavar="REPOSITORY", |
| 237 | + help="build from this Git repository") |
| 238 | + parser.add_option( |
| 239 | + "--git-path", metavar="REF-PATH", |
| 240 | + help="build from this ref path in REPOSITORY") |
| 241 | + options, args = parser.parse_args() |
| 242 | + if (options.git_repository is None) != (options.git_path is None): |
| 243 | + parser.error( |
| 244 | + "must provide both --git-repository and --git-path or neither") |
| 245 | + if (options.branch is None) == (options.git_repository is None): |
| 246 | + parser.error( |
| 247 | + "must provide exactly one of --branch and --git-repository") |
| 248 | + if len(args) != 1: |
| 249 | + parser.error( |
| 250 | + "must provide a package name and no other positional arguments") |
| 251 | + [name] = args |
| 252 | + builder = SnapBuilder(options, name) |
| 253 | + try: |
| 254 | + builder.install() |
| 255 | + except Exception: |
| 256 | + traceback.print_exc() |
| 257 | + return RETCODE_FAILURE_INSTALL |
| 258 | + try: |
| 259 | + builder.build() |
| 260 | + except Exception: |
| 261 | + traceback.print_exc() |
| 262 | + return RETCODE_FAILURE_BUILD |
| 263 | + return RETCODE_SUCCESS |
| 264 | + |
| 265 | + |
| 266 | +if __name__ == "__main__": |
| 267 | + sys.exit(main()) |
| 268 | |
| 269 | === modified file 'debian/changelog' |
| 270 | --- debian/changelog 2015-07-23 12:23:33 +0000 |
| 271 | +++ debian/changelog 2015-07-31 11:57:12 +0000 |
| 272 | @@ -7,6 +7,7 @@ |
| 273 | the situation rather than trusting just the definite information. |
| 274 | * Handle architecture restrictions, architecture qualifications, and |
| 275 | restriction formulas (build profiles) in build-dependencies. |
| 276 | + * Add support for building snaps (LP: #1476405). |
| 277 | |
| 278 | -- Colin Watson <cjwatson@ubuntu.com> Thu, 16 Jul 2015 14:00:16 +0100 |
| 279 | |
| 280 | |
| 281 | === modified file 'debian/rules' |
| 282 | --- debian/rules 2015-07-05 12:43:54 +0000 |
| 283 | +++ debian/rules 2015-07-31 11:57:12 +0000 |
| 284 | @@ -19,7 +19,7 @@ |
| 285 | slavebins = unpack-chroot mount-chroot update-debian-chroot sbuild-package \ |
| 286 | scan-for-processes umount-chroot remove-build override-sources-list \ |
| 287 | buildrecipe generate-translation-templates slave-prep buildlivefs \ |
| 288 | - sudo-wrapper |
| 289 | + sudo-wrapper buildsnap |
| 290 | |
| 291 | BUILDDUID=65500 |
| 292 | BUILDDGID=65500 |
| 293 | |
| 294 | === added file 'lpbuildd/snap.py' |
| 295 | --- lpbuildd/snap.py 1970-01-01 00:00:00 +0000 |
| 296 | +++ lpbuildd/snap.py 2015-07-31 11:57:12 +0000 |
| 297 | @@ -0,0 +1,95 @@ |
| 298 | +# Copyright 2015 Canonical Ltd. This software is licensed under the |
| 299 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
| 300 | + |
| 301 | +__metaclass__ = type |
| 302 | + |
| 303 | +import os |
| 304 | +import shutil |
| 305 | + |
| 306 | +from lpbuildd.debian import ( |
| 307 | + DebianBuildManager, |
| 308 | + DebianBuildState, |
| 309 | + get_build_path, |
| 310 | + ) |
| 311 | + |
| 312 | + |
| 313 | +RETCODE_SUCCESS = 0 |
| 314 | +RETCODE_FAILURE_INSTALL = 200 |
| 315 | +RETCODE_FAILURE_BUILD = 201 |
| 316 | + |
| 317 | + |
| 318 | +class SnapBuildState(DebianBuildState): |
| 319 | + BUILD_SNAP = "BUILD_SNAP" |
| 320 | + |
| 321 | + |
| 322 | +class SnapBuildManager(DebianBuildManager): |
| 323 | + """Build a snap.""" |
| 324 | + |
| 325 | + initial_build_state = SnapBuildState.BUILD_SNAP |
| 326 | + |
| 327 | + def __init__(self, slave, buildid, **kwargs): |
| 328 | + super(SnapBuildManager, self).__init__(slave, buildid, **kwargs) |
| 329 | + self.build_snap_path = os.path.join(self._slavebin, "buildsnap") |
| 330 | + |
| 331 | + def initiate(self, files, chroot, extra_args): |
| 332 | + """Initiate a build with a given set of files and chroot.""" |
| 333 | + self.build_path = get_build_path( |
| 334 | + self.home, self._buildid, "chroot-autobuild", "build") |
| 335 | + if os.path.isdir(self.build_path): |
| 336 | + shutil.rmtree(self.build_path) |
| 337 | + |
| 338 | + self.name = extra_args["name"] |
| 339 | + self.branch = extra_args.get("branch") |
| 340 | + self.git_repository = extra_args.get("git_repository") |
| 341 | + self.git_path = extra_args.get("git_path") |
| 342 | + |
| 343 | + super(SnapBuildManager, self).initiate(files, chroot, extra_args) |
| 344 | + |
| 345 | + def doRunBuild(self): |
| 346 | + """Run the process to build the snap.""" |
| 347 | + args = [ |
| 348 | + "buildsnap", |
| 349 | + "--build-id", self._buildid, |
| 350 | + "--arch", self.arch_tag, |
| 351 | + ] |
| 352 | + if self.branch is not None: |
| 353 | + args.extend(["--branch", self.branch]) |
| 354 | + if self.git_repository is not None: |
| 355 | + args.extend(["--git-repository", self.git_repository]) |
| 356 | + if self.git_path is not None: |
| 357 | + args.extend(["--git-path", self.git_path]) |
| 358 | + args.append(self.name) |
| 359 | + self.runSubProcess(self.build_snap_path, args) |
| 360 | + |
| 361 | + def iterate_BUILD_SNAP(self, retcode): |
| 362 | + """Finished building the snap.""" |
| 363 | + if retcode == RETCODE_SUCCESS: |
| 364 | + self.gatherResults() |
| 365 | + print("Returning build status: OK") |
| 366 | + elif (retcode >= RETCODE_FAILURE_INSTALL and |
| 367 | + retcode <= RETCODE_FAILURE_BUILD): |
| 368 | + if not self.alreadyfailed: |
| 369 | + self._slave.buildFail() |
| 370 | + print("Returning build status: Build failed.") |
| 371 | + self.alreadyfailed = True |
| 372 | + else: |
| 373 | + if not self.alreadyfailed: |
| 374 | + self._slave.builderFail() |
| 375 | + print("Returning build status: Builder failed.") |
| 376 | + self.alreadyfailed = True |
| 377 | + self.doReapProcesses(self._state) |
| 378 | + |
| 379 | + def iterateReap_BUILD_SNAP(self, retcode): |
| 380 | + """Finished reaping after building the snap.""" |
| 381 | + self._state = DebianBuildState.UMOUNT |
| 382 | + self.doUnmounting() |
| 383 | + |
| 384 | + def gatherResults(self): |
| 385 | + """Gather the results of the build and add them to the file cache.""" |
| 386 | + output_path = os.path.join(self.build_path, self.name) |
| 387 | + if not os.path.exists(output_path): |
| 388 | + return |
| 389 | + for entry in sorted(os.listdir(output_path)): |
| 390 | + path = os.path.join(output_path, entry) |
| 391 | + if entry.endswith(".snap") and not os.path.islink(path): |
| 392 | + self._slave.addWaitingFile(path) |
| 393 | |
| 394 | === added file 'lpbuildd/tests/test_snap.py' |
| 395 | --- lpbuildd/tests/test_snap.py 1970-01-01 00:00:00 +0000 |
| 396 | +++ lpbuildd/tests/test_snap.py 2015-07-31 11:57:12 +0000 |
| 397 | @@ -0,0 +1,121 @@ |
| 398 | +# Copyright 2015 Canonical Ltd. This software is licensed under the |
| 399 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
| 400 | + |
| 401 | +__metaclass__ = type |
| 402 | + |
| 403 | +import os |
| 404 | +import shutil |
| 405 | +import tempfile |
| 406 | + |
| 407 | +from testtools import TestCase |
| 408 | + |
| 409 | +from lpbuildd.snap import ( |
| 410 | + SnapBuildManager, |
| 411 | + SnapBuildState, |
| 412 | + ) |
| 413 | +from lpbuildd.tests.fakeslave import FakeSlave |
| 414 | + |
| 415 | + |
| 416 | +class MockBuildManager(SnapBuildManager): |
| 417 | + def __init__(self, *args, **kwargs): |
| 418 | + super(MockBuildManager, self).__init__(*args, **kwargs) |
| 419 | + self.commands = [] |
| 420 | + self.iterators = [] |
| 421 | + |
| 422 | + def runSubProcess(self, path, command, iterate=None): |
| 423 | + self.commands.append([path] + command) |
| 424 | + if iterate is None: |
| 425 | + iterate = self.iterate |
| 426 | + self.iterators.append(iterate) |
| 427 | + return 0 |
| 428 | + |
| 429 | + |
| 430 | +class TestSnapBuildManagerIteration(TestCase): |
| 431 | + """Run SnapBuildManager through its iteration steps.""" |
| 432 | + def setUp(self): |
| 433 | + super(TestSnapBuildManagerIteration, self).setUp() |
| 434 | + self.working_dir = tempfile.mkdtemp() |
| 435 | + self.addCleanup(lambda: shutil.rmtree(self.working_dir)) |
| 436 | + slave_dir = os.path.join(self.working_dir, "slave") |
| 437 | + home_dir = os.path.join(self.working_dir, "home") |
| 438 | + for dir in (slave_dir, home_dir): |
| 439 | + os.mkdir(dir) |
| 440 | + self.slave = FakeSlave(slave_dir) |
| 441 | + self.buildid = "123" |
| 442 | + self.buildmanager = MockBuildManager(self.slave, self.buildid) |
| 443 | + self.buildmanager.home = home_dir |
| 444 | + self.buildmanager._cachepath = self.slave._cachepath |
| 445 | + self.build_dir = os.path.join( |
| 446 | + home_dir, "build-%s" % self.buildid, "chroot-autobuild", "build") |
| 447 | + |
| 448 | + def getState(self): |
| 449 | + """Retrieve build manager's state.""" |
| 450 | + return self.buildmanager._state |
| 451 | + |
| 452 | + def startBuild(self): |
| 453 | + # The build manager's iterate() kicks off the consecutive states |
| 454 | + # after INIT. |
| 455 | + extra_args = { |
| 456 | + "arch_tag": "i386", |
| 457 | + "name": "test-snap", |
| 458 | + "git_repository": "https://git.launchpad.dev/~example/+git/snap", |
| 459 | + "git_path": "master", |
| 460 | + } |
| 461 | + self.buildmanager.initiate({}, "chroot.tar.gz", extra_args) |
| 462 | + |
| 463 | + # Skip states that are done in DebianBuildManager to the state |
| 464 | + # directly before BUILD_SNAP. |
| 465 | + self.buildmanager._state = SnapBuildState.UPDATE |
| 466 | + |
| 467 | + # BUILD_SNAP: Run the slave's payload to build the snap package. |
| 468 | + self.buildmanager.iterate(0) |
| 469 | + self.assertEqual(SnapBuildState.BUILD_SNAP, self.getState()) |
| 470 | + expected_command = [ |
| 471 | + "sharepath/slavebin/buildsnap", "buildsnap", |
| 472 | + "--build-id", self.buildid, "--arch", "i386", |
| 473 | + "--git-repository", "https://git.launchpad.dev/~example/+git/snap", |
| 474 | + "--git-path", "master", |
| 475 | + "test-snap", |
| 476 | + ] |
| 477 | + self.assertEqual(expected_command, self.buildmanager.commands[-1]) |
| 478 | + self.assertEqual( |
| 479 | + self.buildmanager.iterate, self.buildmanager.iterators[-1]) |
| 480 | + self.assertFalse(self.slave.wasCalled("chrootFail")) |
| 481 | + |
| 482 | + def test_iterate(self): |
| 483 | + # The build manager iterates a normal build from start to finish. |
| 484 | + self.startBuild() |
| 485 | + |
| 486 | + log_path = os.path.join(self.buildmanager._cachepath, "buildlog") |
| 487 | + log = open(log_path, "w") |
| 488 | + log.write("I am a build log.") |
| 489 | + log.close() |
| 490 | + |
| 491 | + output_dir = os.path.join(self.build_dir, "test-snap") |
| 492 | + os.makedirs(output_dir) |
| 493 | + snap_path = os.path.join(output_dir, "test-snap_0_all.snap") |
| 494 | + with open(snap_path, "w") as snap: |
| 495 | + snap.write("I am a snap package.") |
| 496 | + |
| 497 | + # After building the package, reap processes. |
| 498 | + self.buildmanager.iterate(0) |
| 499 | + expected_command = [ |
| 500 | + "sharepath/slavebin/scan-for-processes", "scan-for-processes", |
| 501 | + self.buildid, |
| 502 | + ] |
| 503 | + self.assertEqual(SnapBuildState.BUILD_SNAP, self.getState()) |
| 504 | + self.assertEqual(expected_command, self.buildmanager.commands[-1]) |
| 505 | + self.assertNotEqual( |
| 506 | + self.buildmanager.iterate, self.buildmanager.iterators[-1]) |
| 507 | + self.assertFalse(self.slave.wasCalled("buildFail")) |
| 508 | + self.assertEqual([((snap_path,), {})], self.slave.addWaitingFile.calls) |
| 509 | + |
| 510 | + # Control returns to the DebianBuildManager in the UMOUNT state. |
| 511 | + self.buildmanager.iterateReap(self.getState(), 0) |
| 512 | + expected_command = [ |
| 513 | + "sharepath/slavebin/umount-chroot", "umount-chroot", self.buildid] |
| 514 | + self.assertEqual(SnapBuildState.UMOUNT, self.getState()) |
| 515 | + self.assertEqual(expected_command, self.buildmanager.commands[-1]) |
| 516 | + self.assertEqual( |
| 517 | + self.buildmanager.iterate, self.buildmanager.iterators[-1]) |
| 518 | + self.assertFalse(self.slave.wasCalled("buildFail")) |
| 519 | |
| 520 | === added file 'lpbuildd/util.py' |
| 521 | --- lpbuildd/util.py 1970-01-01 00:00:00 +0000 |
| 522 | +++ lpbuildd/util.py 2015-07-31 11:57:12 +0000 |
| 523 | @@ -0,0 +1,50 @@ |
| 524 | +# Copyright 2015 Canonical Ltd. This software is licensed under the |
| 525 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
| 526 | + |
| 527 | +__metaclass__ = type |
| 528 | + |
| 529 | +import re |
| 530 | + |
| 531 | + |
| 532 | +non_meta_re = re.compile(r'^[a-zA-Z0-9+,./:=@_-]+$') |
| 533 | + |
| 534 | +def shell_escape(arg): |
| 535 | + if non_meta_re.match(arg): |
| 536 | + return arg |
| 537 | + else: |
| 538 | + return "'%s'" % arg.replace("'", "'\\''") |
| 539 | + |
| 540 | + |
| 541 | +linux32_arches = [ |
| 542 | + "armel", |
| 543 | + "armhf", |
| 544 | + "hppa", |
| 545 | + "i386", |
| 546 | + "lpia", |
| 547 | + "mips", |
| 548 | + "mipsel", |
| 549 | + "powerpc", |
| 550 | + "s390", |
| 551 | + "sparc", |
| 552 | + ] |
| 553 | +linux64_arches = [ |
| 554 | + "alpha", |
| 555 | + "amd64", |
| 556 | + "arm64", |
| 557 | + "hppa64", |
| 558 | + "ia64", |
| 559 | + "ppc64", |
| 560 | + "ppc64el", |
| 561 | + "s390x", |
| 562 | + "sparc64", |
| 563 | + "x32", |
| 564 | + ] |
| 565 | + |
| 566 | + |
| 567 | +def set_personality(arch, args): |
| 568 | + if arch in linux32_arches: |
| 569 | + return ["linux32"] + args |
| 570 | + elif arch in linux64_arches: |
| 571 | + return ["linux64"] + args |
| 572 | + else: |
| 573 | + return args |
