2525
2626
2727def _expand_env (s , * args ):
28- # repo2docker uses PATH when expanding PATH
29- env = {"PATH" : "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" }
28+ env = {}
3029 for e in reversed (args ):
3130 env .update (e )
3231 return Template (s ).substitute (env )
@@ -78,7 +77,7 @@ def _docker_copy(copy, chown):
7877 return statement
7978
8079
81- def dockerfile_to_bash (dockerfile , buildargs ):
80+ def dockerfile_to_bash (dockerfile , buildargs , parentenv ):
8281 """
8382 Convert a Dockerfile to a bash script
8483
@@ -94,16 +93,24 @@ def dockerfile_to_bash(dockerfile, buildargs):
9493 bash = []
9594 cmd = ""
9695 entrypoint = ""
96+
9797 # Needed so we know which directory to run the start command from
9898 currentdir = ""
99- # Runtime environment
100- runtimeenv = {}
101- # Build and runtime environment
102- currentenv = {}
103- parser = DockerfileParser (dockerfile )
99+
100+ # The final runtime environment (expanded ENV only)
101+ runtimeenv = parentenv .copy ()
102+
103+ # Combined build and runtime environments (since during the build we need
104+ # to take ARG and ENV into account)
105+ currentenv = parentenv .copy ()
106+
107+ parser = DockerfileParser (
108+ dockerfile , env_replace = True , build_args = buildargs , parent_env = parentenv
109+ )
104110 user = "root"
105111
106- for d in parser .structure :
112+ assert len (parser .structure ) == len (parser .context_structure )
113+ for (d , ctx ) in zip (parser .structure , parser .context_structure ):
107114 statement = ""
108115 instruction = d ["instruction" ]
109116 for line in d ["content" ].splitlines ():
@@ -120,18 +127,12 @@ def dockerfile_to_bash(dockerfile, buildargs):
120127 raise NotImplementedError (f"Base image { d ['value' ]} not supported" )
121128 statement += base_setup
122129 elif instruction == "ARG" :
123- argname = d ["value" ].split ("=" , 1 )[0 ]
124- try :
125- argvalue = shlex .quote (buildargs [d ["value" ]])
126- except KeyError :
127- if "=" in d ["value" ]:
128- argvalue = d ["value" ].split ("=" , 1 )[1 ]
129- else :
130- raise
131- # Expand because this may eventually end up as a runtime env
132- argvalue = _expand_env (argvalue , currentenv )
133- currentenv [argname ] = argvalue
134- statement += f"export { argname } ={ argvalue } \n "
130+ for argname , argvalue in ctx .line_args .items ():
131+ # Expand because this may eventually end up as a runtime env
132+ argvalue = _expand_env (argvalue , currentenv )
133+ currentenv [argname ] = argvalue
134+ escaped_argvalue = shlex .quote (argvalue )
135+ statement += f"export { argname } ={ escaped_argvalue } \n "
135136 elif instruction == "CMD" :
136137 cmd = " " .join (shlex .quote (p ) for p in json .loads (d ["value" ]))
137138 elif instruction == "COPY" :
@@ -143,20 +144,17 @@ def dockerfile_to_bash(dockerfile, buildargs):
143144 elif instruction == "ENTRYPOINT" :
144145 entrypoint = " " .join (shlex .quote (p ) for p in json .loads (d ["value" ]))
145146 elif instruction == "ENV" :
146- # repodocker is inconsistent in how it uses ENV
147- try :
148- k , v = d ["value" ].split ("=" , 1 )
149- except ValueError :
150- k , v = d ["value" ].split (" " , 1 )
151- argvalue = _expand_env (v , currentenv )
152- currentenv [k ] = argvalue
153- statement += f"export { k } ={ argvalue } \n "
154- runtimeenv [k ] = argvalue
147+ for envname , envvalue in ctx .line_envs .items ():
148+ envvalue = _expand_env (envvalue , currentenv )
149+ currentenv [envname ] = envvalue
150+ runtimeenv [envname ] = envvalue
151+ escaped_envvalue = shlex .quote (envvalue )
152+ statement += f"export { envname } ={ escaped_envvalue } \n "
155153 elif instruction == "RUN" :
156154 run = _sudo_user (user , d ["value" ], currentenv )
157155 statement += f"{ run } \n "
158156 elif instruction == "USER" :
159- user = d ["value" ]
157+ user = _expand_env ( d ["value" ], currentenv )
160158 elif instruction == "WORKDIR" :
161159 statement += f"cd { d ['value' ]} \n "
162160 currentdir = _expand_env (d ["value" ], currentenv )
@@ -175,8 +173,7 @@ def dockerfile_to_bash(dockerfile, buildargs):
175173 "dir" : currentdir ,
176174 "env" : runtimeenv ,
177175 "start" : f"{ entrypoint } { cmd } " ,
178- # Expand ${NB_USER}
179- "user" : _expand_env (user , currentenv ),
176+ "user" : user ,
180177 }
181178 return r
182179
@@ -233,6 +230,10 @@ def build(
233230 ):
234231
235232 buildargs = buildargs or {}
233+ # repo2docker uses PATH when expanding PATH
234+ parentenv = {
235+ "PATH" : "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
236+ }
236237
237238 if not tag :
238239 tag = str (uuid4 ())
@@ -260,7 +261,7 @@ def build(
260261 else :
261262 dockerfile = os .path .join (builddir )
262263
263- r = dockerfile_to_bash (dockerfile , buildargs )
264+ r = dockerfile_to_bash (dockerfile , buildargs , parentenv )
264265 build_file = os .path .join (builddir , "repo2shellscript-build.bash" )
265266 start_file = os .path .join (builddir , "repo2shellscript-start.bash" )
266267 systemd_file = os .path .join (builddir , "repo2shellscript.service" )
0 commit comments