tAdd docker2sh.py. - docker2sh - Convert Dockerfiles into shell scripts
 (HTM) git clone https://git.parazyd.org/docker2sh
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 0c6fe99b9d8031c830abbab1a05d18757fd37d32
 (DIR) parent 8a0b794dd328b411a64ba250be772ad675ed8f12
 (HTM) Author: parazyd <parazyd@dyne.org>
       Date:   Wed, 20 Nov 2019 14:11:08 +0100
       
       Add docker2sh.py.
       
       Diffstat:
         A docker2sh.py                        |     176 +++++++++++++++++++++++++++++++
       
       1 file changed, 176 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/docker2sh.py b/docker2sh.py
       t@@ -0,0 +1,176 @@
       +#!/usr/bin/env python3
       +# Copyright (c) 2018-2019 Dyne.org Foundation
       +# docker2sh is maintained by Ivan J. <parazyd@dyne.org>
       +#
       +# This source code is free software: you can redistribute it and/or modify
       +# it under the terms of the GNU General Public License as published by
       +# the Free Software Foundation, either version 3 of the License, or
       +# (at your option) any later version.
       +#
       +# This software is distributed in the hope that it will be useful,
       +# but WITHOUT ANY WARRANTY; without even the implied warranty of
       +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       +# GNU General Public License for more details.
       +#
       +# You should have received a copy of the GNU General Public License
       +# along with this source code. If not, see <http://www.gnu.org/licenses/>.
       +
       +"""
       +Dockerfile parser module
       +"""
       +from argparse import ArgumentParser
       +from base64 import b64encode
       +from bz2 import compress
       +from os.path import dirname, join
       +from sys import stdin
       +import json
       +import re
       +
       +
       +def rstrip_backslash(line):
       +    """
       +    Strip backslashes from end of line
       +    """
       +    line = line.rstrip()
       +    if line.endswith('\\'):
       +        return line[:-1]
       +    return line
       +
       +
       +def compress_and_b64(file, basepath=None):
       +    """
       +    Compress a file and turn it to base64 for output
       +    """
       +    spl = file.split()
       +    if basepath:
       +        file = open(join(basepath, spl[0])).read()
       +    else:
       +        file = open(spl[0]).read()
       +
       +    comp = compress(file.encode())
       +    b64 = b64encode(comp)
       +
       +    cat = 'cat << __EOFF__ | base64 -d | bunzip2 | sudo tee %s >/dev/null' % (spl[1])
       +    return '\n'.join([cat, b64.decode(), '__EOFF__']) + '\n'
       +
       +
       +def parse_instruction(inst, dfile=None):
       +    """
       +    Method for translating Dockerfile instructions to shell script
       +    """
       +    ins = inst['instruction'].upper()
       +    val = inst['value']
       +
       +    # Valid Dockerfile instructions
       +    cmds = ['ADD', 'ARG', 'CMD', 'COPY', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'FROM',
       +            'HEALTHCHECK', 'LABEL', 'MAINTAINER', 'ONBUILD', 'RUN', 'SHELL',
       +            'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR']
       +
       +    if ins == 'ADD':
       +        cmds.remove(ins)
       +        val = val.replace('$', '\\$')
       +        args = val.split(' ')
       +        return 'wget -O %s %s\n' % (args[1], args[0])
       +
       +    if ins == 'ARG':
       +        cmds.remove(ins)
       +        return '%s\n' % val
       +
       +    if ins == 'ENV':
       +        cmds.remove(ins)
       +        if '=' not in val:
       +            val = val.replace(' ', '=', 1)
       +        return 'export %s\n' % val
       +
       +    if ins == 'RUN':
       +        cmds.remove(ins)
       +        # Replace `` with $()
       +        while '`' in val:
       +            val = val.replace('`', '"$(', 1)
       +            val = val.replace('`', ')"', 1)
       +        return '%s\n' % val.replace('$', '\\$')
       +
       +    if ins == 'WORKDIR':
       +        cmds.remove(ins)
       +        return 'mkdir -p %s && cd %s\n' % (val, val)
       +
       +    if ins == 'COPY':
       +        cmds.remove(ins)
       +        if '/' in dfile:
       +            return compress_and_b64(val, basepath=dirname(dfile))
       +        return compress_and_b64(val)
       +
       +    if ins in cmds:
       +        # TODO: Look at CMD being added to /etc/rc.local
       +        return '#\n# %s not implemented\n# Instruction: %s %s\n#\n' % \
       +            (ins, ins, val)
       +
       +    return ''
       +
       +
       +def main():
       +    """
       +    Main parsing routine
       +    """
       +    parser = ArgumentParser()
       +    parser.add_argument('-j', '--json', action='store_true',
       +                        help='output the data as a JSON structure')
       +    parser.add_argument('-s', '--shell', action='store_true',
       +                        help='output the data as a shell script (default)')
       +    parser.add_argument('--keeptabs', action='store_true',
       +                        help='do not replace \\t (tabs) in the strings')
       +    parser.add_argument('Dockerfile')
       +    args = parser.parse_args()
       +
       +    if args.Dockerfile != '-':
       +        with open(args.Dockerfile) as file:
       +            data = file.read().splitlines()
       +    else:
       +        data = stdin.read().splitlines()
       +
       +    instre = re.compile(r'^\s*(\w+)\s+(.*)$')
       +    contre = re.compile(r'^.*\\\s*$')
       +    commentre = re.compile(r'^\s*#')
       +
       +    instructions = []
       +    lineno = -1
       +    in_continuation = False
       +    cur_inst = {}
       +
       +    for line in data:
       +        lineno += 1
       +        if commentre.match(line):
       +            continue
       +        if not in_continuation:
       +            rematch = instre.match(line)
       +            if not rematch:
       +                continue
       +            cur_inst = {
       +                'instruction': rematch.groups()[0].upper(),
       +                'value': rstrip_backslash(rematch.groups()[1]),
       +            }
       +        else:
       +            if cur_inst['value']:
       +                cur_inst['value'] += rstrip_backslash(line)
       +            else:
       +                cur_inst['value'] = rstrip_backslash(line.lstrip())
       +
       +        in_continuation = contre.match(line)
       +        if not in_continuation and cur_inst is not None:
       +            if not args.keeptabs:
       +                cur_inst['value'] = cur_inst['value'].replace('\t', '')
       +            instructions.append(cur_inst)
       +
       +    if args.json:
       +        print(json.dumps(instructions))
       +        return
       +
       +    # Default to shell script output
       +    script = '#!/bin/sh\n'
       +    for i in instructions:
       +        script += parse_instruction(i, dfile=args.Dockerfile)
       +    print(script)
       +
       +
       +if __name__ == '__main__':
       +    main()