tdockerfile_parse.py - libdevuansdk - common library for devuan's simple distro kits
HTML 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()