aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@arrcus.com>2018-06-29 22:13:51 -0400
committerRob Austein <sra@arrcus.com>2018-06-29 22:13:51 -0400
commit744b079a96510b468f2cc641b39167e1aeee32e3 (patch)
tree0c8c599087a104bf1817dbe6a9a93caf17b3b209
parent2c4af3f12cb83d746552cab8abf9d25f9722bb49 (diff)
Context managers
-rwxr-xr-xbaiji118
1 files changed, 93 insertions, 25 deletions
diff --git a/baiji b/baiji
index fb1c2df..45b6822 100755
--- a/baiji
+++ b/baiji
@@ -1,11 +1,13 @@
#!/usr/bin/env python
+import debian.deb822
import subprocess
import argparse
import tempfile
import tarfile
import shutil
import sys
+import os
# Python decorator voodoo to simplify argparse subparser setup.
@@ -23,6 +25,46 @@ def cmd(*args):
return wrapper
+# Context manager for temporary directories.
+
+class tempdir(object):
+
+ def __enter__(self):
+ self.dn = tempfile.mkdtemp()
+ return self.dn
+
+ def __exit__(self, *oops):
+ shutil.rmtree(self.dn)
+
+# Docker process, mostly a context manager around subprocess.Popen.
+# We could use the native Python Docker interface, but the packaged
+# Debian version of that has a wildly different API than the version
+# on GitHub.
+
+class DockerError(Exception):
+ "Docker returned failure."
+
+class Docker(subprocess.Popen):
+
+ def __init__(self, *args, **kwargs):
+ super(Docker, self).__init__(("docker",) + args, **kwargs)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *oops):
+ status = self.wait()
+ if status and all(o is None for o in oops):
+ raise DockerError()
+
+# Filter which acts like fakeroot for tarfile.TarFile.add()
+
+def fakeroot_filter(info):
+ info.uname = info.gname = "root"
+ info.uid = info.gid = 0
+ return info
+
+
# Commands
@cmd(arg("--dist", default = "jessie", help = "distribution for base docker image"),
@@ -36,25 +78,20 @@ def create_base(args):
setup to include git, build-essentials, and fakeroot.
"""
- dn = None
- try:
- dn = tempfile.mkdtemp()
+ with tempdir() as dn:
subprocess.check_call(("fakeroot", "/usr/sbin/debootstrap", "--foreign", "--variant=buildd", args.dist, dn))
- tar = subprocess.Popen(("fakeroot", "tar", "-C", dn, "-c", "."), stdout = subprocess.PIPE)
- docker = subprocess.Popen(("docker", "import", "-", args.tag), stdin = tar.stdout)
- if tar.wait() or docker.wait():
- sys.exit("Couldn't construct stage 1 base image")
- finally:
- if dn is not None:
- shutil.rmtree(dn)
- docker = subprocess.Popen(("docker", "build", "-t", args.tag, "-"), stdin = subprocess.PIPE)
- docker.communicate('''\
- FROM {args.tag}
- RUN sed -i '/mount -t proc /d; /mount -t sysfs /d' /debootstrap/functions && /debootstrap/debootstrap --second-stage
- RUN apt-get update && apt-get install -y --no-install-recommends build-essential fakeroot git
- '''.format(args = args))
- if docker.wait():
- sys.exit("Couldn't construct stage 2 base image")
+ with Docker("import", "-", args.tag, stdin = subprocess.PIPE) as docker:
+ tar = tarfile.open(mode = "w|", fileobj = docker.stdin)
+ tar.add(dn, ".", filter = fakeroot_filter)
+ tar.close()
+ docker.stdin.close()
+
+ with Docker("build", "-t", args.tag, "-", stdin = subprocess.PIPE) as docker:
+ docker.communicate('''\
+ FROM {args.tag}
+ RUN sed -i '/mount -t proc /d; /mount -t sysfs /d' /debootstrap/functions && /debootstrap/debootstrap --second-stage
+ RUN apt-get update && apt-get install -y --no-install-recommends build-essential fakeroot git
+ '''.format(args = args))
@cmd(arg("--tag", default = "baiji:jessie", help = "tag of base docker image to update"),
@@ -64,13 +101,44 @@ def update_base(args):
Update a base Docker image.
"""
- docker = subprocess.Popen(("docker", "build", "-t", args.tag, "-"), stdin = subprocess.PIPE)
- docker.communicate('''\
- FROM {args.tag}
- RUN apt-get update && apt-get upgrade -y --with-new-pkgs --no-install-recommends && apt-get autoremove && apt-get clean
- '''.format(args = args))
- if docker.wait():
- sys.exit("Couldn't update image base image")
+ with Docker("build", "-t", args.tag, "-", stdin = subprocess.PIPE) as docker:
+ docker.communicate('''\
+ FROM {args.tag}
+ RUN apt-get update && apt-get upgrade -y --with-new-pkgs --no-install-recommends && apt-get autoremove && apt-get clean
+ '''.format(args = args))
+
+
+@cmd(arg("--tag", default = "baiji:jessie", help = "tag of base docker image to use"),
+ arg("--dsc", required = True, type = argparse.FileType("r"), help = ".dsc file to build"),
+ arg("--local-package", nargs = "+", help = "local packages to make available to build"),
+)
+def build(args):
+ """
+ Build a binary package given a source package.
+
+ In the long run we may want --dsc to be optional, with the implied
+ action of building a source package from the current directory if
+ --dsc isn't specified. Later.
+ """
+
+ dsc = debian.deb822.Dsc(args.dsc)
+ files = [os.path.join(os.path.dirname(args.dsc.name), f["name"]) for f in dsc["Files"]]
+ dummy = debian.deb822.Deb822()
+ dummy_name = dsc["Source"] + "-build-depends"
+ dummy_fn = "{}_{}_all.deb".format(dummy_name, dsc["Version"])
+ dummy["Depends"] = dsc["Build-Depends"]
+ dummy["Package"] = dummy_name
+ for tag in ("Version", "Maintainer", "Homepage"):
+ dummy[tag] = dsc[tag]
+
+ with tempdir() as dn:
+ equivs = subprocess.Popen(("equivs-build", "/dev/stdin"), stdin = subprocess.PIPE, stdout = subprocess.PIPE, cwd = dn)
+ equivs.communicate(str(dummy))
+ if equivs.wait():
+ sys.exit("Couldn't generate dummy dependency package")
+
+ # Do something useful with generated file here
+ subprocess.check_call(("dpkg", "-c", os.path.join(dn, dummy_fn)))
# Parse arguments and dispatch to one of the commands above.