tdocker2sh - docker2sh - Convert Dockerfiles into shell scripts
 (HTM) git clone https://git.parazyd.org/docker2sh
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tdocker2sh (4369B)
       ---
            1 #!/usr/bin/env python3
            2 """
            3 Dockerfile parser module
            4 """
            5 from argparse import ArgumentParser
            6 from base64 import b64encode
            7 from bz2 import compress
            8 from os.path import dirname, join
            9 from sys import stdin
           10 import json
           11 import re
           12 
           13 
           14 def rstrip_backslash(line):
           15     """
           16     Strip backslashes from end of line
           17     """
           18     line = line.rstrip()
           19     if line.endswith('\\'):
           20         return line[:-1]
           21     return line
           22 
           23 
           24 def compress_and_b64(file, basepath=None):
           25     """
           26     Compress a file and turn it to base64 for output
           27     """
           28     spl = file.split()
           29     if basepath:
           30         file = open(join(basepath, spl[0])).read()
           31     else:
           32         file = open(spl[0]).read()
           33 
           34     comp = compress(file.encode())
           35     b64 = b64encode(comp)
           36 
           37     cat = 'cat << __EOFF__ | base64 -d | bunzip2 > %s' % (spl[1])
           38     return '\n'.join([cat, b64.decode(), '__EOFF__']) + '\n'
           39 
           40 
           41 def parse_instruction(inst, dfile=None):
           42     """
           43     Method for translating Dockerfile instructions to shell script
           44     """
           45     ins = inst['instruction'].upper()
           46     val = inst['value']
           47 
           48     # Valid Dockerfile instructions
           49     cmds = ['ADD', 'ARG', 'CMD', 'COPY', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'FROM',
           50             'HEALTHCHECK', 'LABEL', 'MAINTAINER', 'ONBUILD', 'RUN', 'SHELL',
           51             'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR']
           52 
           53     if ins == 'ADD':
           54         val = val.replace('$', '\\$')
           55         args = val.split(' ')
           56         return 'wget -O %s %s\n' % (args[1], args[0])
           57 
           58     if ins == 'ARG':
           59         return '%s\n' % val
           60 
           61     if ins == 'ENV':
           62         if '=' not in val:
           63             val = val.replace(' ', '=', 1)
           64         val = val.replace('$', '\\$')
           65         return 'export %s\n' % val
           66 
           67     if ins == 'RUN':
           68         # Replace `` with $()
           69         while '`' in val:
           70             val = val.replace('`', '"$(', 1)
           71             val = val.replace('`', ')"', 1)
           72         return '%s\n' % val.replace('$', '\\$')
           73 
           74     if ins == 'WORKDIR':
           75         return 'mkdir -p %s && cd %s\n' % (val, val)
           76 
           77     if ins == 'COPY':
           78         if '/' in dfile:
           79             return compress_and_b64(val, basepath=dirname(dfile))
           80         return compress_and_b64(val)
           81 
           82     if ins in cmds:
           83         # TODO: Look at CMD being added to /etc/rc.local
           84         return '#\n# %s not implemented\n# Instruction: %s %s\n#\n' % \
           85             (ins, ins, val)
           86 
           87     # Silently ignore unknown instructions
           88     return ''
           89 
           90 
           91 def main():
           92     """
           93     Main parsing routine
           94     """
           95     parser = ArgumentParser()
           96     parser.add_argument('-j', '--json', action='store_true',
           97                         help='output the data as a JSON structure')
           98     parser.add_argument('-s', '--shell', action='store_true',
           99                         help='output the data as a shell script (default)')
          100     parser.add_argument('--keeptabs', action='store_true',
          101                         help='do not replace \\t (tabs) in the strings')
          102     parser.add_argument('Dockerfile')
          103     args = parser.parse_args()
          104 
          105     if args.Dockerfile != '-':
          106         with open(args.Dockerfile) as file:
          107             data = file.read().splitlines()
          108     else:
          109         data = stdin.read().splitlines()
          110 
          111     instre = re.compile(r'^\s*(\w+)\s+(.*)$')
          112     contre = re.compile(r'^.*\\\s*$')
          113     commentre = re.compile(r'^\s*#')
          114 
          115     instructions = []
          116     lineno = -1
          117     in_continuation = False
          118     cur_inst = {}
          119 
          120     for line in data:
          121         lineno += 1
          122         if commentre.match(line):
          123             continue
          124         if not in_continuation:
          125             rematch = instre.match(line)
          126             if not rematch:
          127                 continue
          128             cur_inst = {
          129                 'instruction': rematch.groups()[0].upper(),
          130                 'value': rstrip_backslash(rematch.groups()[1]),
          131             }
          132         else:
          133             if cur_inst['value']:
          134                 cur_inst['value'] += rstrip_backslash(line)
          135             else:
          136                 cur_inst['value'] = rstrip_backslash(line.lstrip())
          137 
          138         in_continuation = contre.match(line)
          139         if not in_continuation and cur_inst is not None:
          140             if not args.keeptabs:
          141                 cur_inst['value'] = cur_inst['value'].replace('\t', '')
          142             instructions.append(cur_inst)
          143 
          144     if args.json:
          145         print(json.dumps(instructions))
          146         return
          147 
          148     # Default to shell script output
          149     script = '#!/bin/sh\n'
          150     for i in instructions:
          151         script += parse_instruction(i, dfile=args.Dockerfile)
          152     print(script)
          153 
          154 
          155 if __name__ == '__main__':
          156     main()