tdockerfile_parse.py - libdevuansdk - common library for devuan's simple distro kits
 (HTM) git clone https://git.parazyd.org/libdevuansdk
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
       tdockerfile_parse.py (4427B)
       ---
            1 #!/usr/bin/env python3
            2 # Copyright (c) 2018-2021 Ivan J. <parazyd@dyne.org>
            3 # This file is part of libdevuansdk
            4 #
            5 # This source code is free software: you can redistribute it and/or modify
            6 # it under the terms of the GNU General Public License as published by
            7 # the Free Software Foundation, either version 3 of the License, or
            8 # (at your option) any later version.
            9 #
           10 # This software is distributed in the hope that it will be useful,
           11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
           12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
           13 # GNU General Public License for more details.
           14 #
           15 # You should have received a copy of the GNU General Public License
           16 # along with this source code. If not, see <http://www.gnu.org/licenses/>.
           17 """
           18 Dockerfile parser module
           19 """
           20 from argparse import ArgumentParser
           21 from sys import stdin
           22 import json
           23 import re
           24 
           25 
           26 def rstrip_backslash(line):
           27     """
           28     Strip backslashes from end of line
           29     """
           30     line = line.rstrip()
           31     if line.endswith('\\'):
           32         return line[:-1]
           33     return line
           34 
           35 
           36 def parse_instruction(inst):
           37     """
           38     Method for translating Dockerfile instructions to shell script
           39     """
           40     ins = inst['instruction'].upper()
           41     val = inst['value']
           42 
           43     # Valid Dockerfile instructions
           44     cmds = ['ADD', 'ARG', 'CMD', 'COPY', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'FROM',
           45             'HEALTHCHECK', 'LABEL', 'MAINTAINER', 'ONBUILD', 'RUN', 'SHELL',
           46             'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR']
           47 
           48     if ins == 'ADD':
           49         cmds.remove(ins)
           50         val = val.replace('$', '\\$')
           51         args = val.split(' ')
           52         return 'wget -O %s %s\n' % (args[1], args[0])
           53 
           54     elif ins == 'ARG':
           55         cmds.remove(ins)
           56         return '%s\n' % val
           57 
           58     elif ins == 'ENV':
           59         cmds.remove(ins)
           60         if '=' not in val:
           61             val = val.replace(' ', '=', 1)
           62         return 'export %s\n' % val
           63 
           64     elif ins == 'RUN':
           65         cmds.remove(ins)
           66         # Replace `` with $()
           67         while '`' in val:
           68             val = val.replace('`', '"$(', 1)
           69             val = val.replace('`', ')"', 1)
           70         return '%s\n' % val.replace('$', '\\$')
           71 
           72     elif ins == 'WORKDIR':
           73         cmds.remove(ins)
           74         return 'mkdir -p %s && cd %s\n' % (val, val)
           75 
           76     elif ins in cmds:
           77         # TODO: Look at CMD being added to /etc/rc.local
           78         return '#\n# %s not implemented\n# Instruction: %s %s\n#\n' % \
           79             (ins, ins, val)
           80 
           81 
           82 def main():
           83     """
           84     Main parsing routine
           85     """
           86     parser = ArgumentParser()
           87     parser.add_argument('-j', '--json', action='store_true',
           88                         help='output the data as a JSON structure')
           89     parser.add_argument('-s', '--shell', action='store_true',
           90                         help='output the data as a shell script (default)')
           91     parser.add_argument('--keeptabs', action='store_true',
           92                         help='do not replace \\t (tabs) in the strings')
           93     parser.add_argument('Dockerfile')
           94     args = parser.parse_args()
           95 
           96     if args.Dockerfile != '-':
           97         with open(args.Dockerfile) as file:
           98             data = file.read().splitlines()
           99     else:
          100         data = stdin.read().splitlines()
          101 
          102     instre = re.compile(r'^\s*(\w+)\s+(.*)$')
          103     contre = re.compile(r'^.*\\\s*$')
          104     commentre = re.compile(r'^\s*#')
          105 
          106     instructions = []
          107     lineno = -1
          108     in_continuation = False
          109     cur_inst = {}
          110 
          111     for line in data:
          112         lineno += 1
          113         if commentre.match(line):
          114             continue
          115         if not in_continuation:
          116             rematch = instre.match(line)
          117             if not rematch:
          118                 continue
          119             cur_inst = {
          120                 'instruction': rematch.groups()[0].upper(),
          121                 'value': rstrip_backslash(rematch.groups()[1]),
          122             }
          123         else:
          124             if cur_inst['value']:
          125                 cur_inst['value'] += rstrip_backslash(line)
          126             else:
          127                 cur_inst['value'] = rstrip_backslash(line.lstrip())
          128 
          129         in_continuation = contre.match(line)
          130         if not in_continuation and cur_inst is not None:
          131             if not args.keeptabs:
          132                 cur_inst['value'] = cur_inst['value'].replace('\t', '')
          133             instructions.append(cur_inst)
          134 
          135     if args.json:
          136         print(json.dumps(instructions))
          137         return
          138 
          139     # Default to shell script output
          140     script = '#!/bin/sh\n'
          141     for i in instructions:
          142         script += parse_instruction(i)
          143     print(script)
          144 
          145 
          146 if __name__ == '__main__':
          147     main()