Fork me on GitHub
  ▖     
▃▀▄▄▖▄▄▖
 ▀▖     
        


#!/usr/bin/env python3
#
# usage: plash build --macro1 ar1 arg2 --macro2 arg1 ...
# Builds a container. Any command line options is evaluated as macro with
# `plash eval`. Use `plash help-macros` to list all available macros.
#
# Examples:
#
# $ plash build -f ubuntu --run 'touch a'
# --> touch a
# --:
# 66
#
# $ plash build -f ubuntu --run 'touch a' --layer --run 'touch b'
# --> touch b
# --:
# 67
#
# $ plash build -f ubuntu --apt nmap
# --> apt-get update
# Hit:1 http://security.ubuntu.com/ubuntu bionic-security InRelease
# Get:2 http://archive.ubuntu.com/ubuntu bionic InRelease [235 kB]
# <snip>
# Setting up nmap (7.60-1ubuntu2) ...
# Processing triggers for libc-bin (2.26-0ubuntu2) ...
# --:
# 68

import os
import re
import subprocess
import sys
from subprocess import DEVNULL, PIPE, CalledProcessError, Popen, check_output

from plash.eval import get_hint_values, hint, remove_hint_values
from plash.utils import (assert_initialized, catch_and_die, color, die,
                         handle_help_flag, hashstr, info, nodepath_or_die,
                         plash_map)

handle_help_flag()
assert_initialized()

with catch_and_die([CalledProcessError], silent=True):
    script = subprocess.check_output(
        ['plash-eval'] + sys.argv[1:], stdin=sys.stdin).decode()

hints = dict(get_hint_values(script))
image_hint = hints.get('image')
if not image_hint:
    die('no image specified', exit=2)

# split the script in its layers
layers = script.split(hint('layer') + '\n')
layers = [remove_hint_values(l) for l in layers]
layers = [l for l in layers if l]

current_container = image_hint
nodepath_or_die(current_container)
os.environ['PS4'] = color('--> ', 4)
for layer in layers:
    cache_key = hashstr(b':'.join([current_container.encode(),
                                   layer.encode()]))
    next_container = plash_map(cache_key.encode())
    if not next_container:

        # build and cache it
        p = Popen(
            ['plash-create', current_container, 'env', '-i', 'sh', '-l'],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE)

        # for some reason in ubuntu the path is not exported
        # in a way this is a hack and should be fixed in ubuntu
        p.stdin.write(b'export PATH\n')

        p.stdin.write(b'set -ex\n')
        p.stdin.write(layer.encode())
        p.stdin.close()
        next_container = p.stdout.read().decode().strip('\n')
        exit = p.wait()
        if exit:
            # plash-create already prints a nice error message
            sys.exit(1)
        plash_map(cache_key.encode(), next_container)
        info('--:')
    current_container = next_container
build_container = current_container

print(current_container)