Skip to content

Commit 9d6d8e6

Browse files
sylaneziopio
authored andcommitted
Fix command construction to support spaces
1 parent 9dfd1ba commit 9d6d8e6

5 files changed

Lines changed: 78 additions & 31 deletions

File tree

src/grisp_tools_build.erl

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,15 @@ clone(#{repo_state := _State, otp_version := {_,_,_,Ver}} = S0) ->
8383
Branch = ["OTP-", Ver],
8484
% See https://github.com/erlang/rebar3/pull/2660
8585
S2 = mapz:deep_put([shell, env, "GIT_TERMINAL_PROMPT"], "0", S1),
86-
{{ok, Output}, S3} = shell(S2,
87-
"git clone "
88-
"-b " ++ Branch ++ " "
89-
"--depth 1 " ++
90-
"--single-branch " ++
91-
URL ++ " \"" ++ binary_to_list(BuildPath) ++ "\""
92-
),
86+
Cmd = [
87+
"git clone ",
88+
"-b ", Branch, " ",
89+
"--depth 1 ",
90+
"--single-branch ",
91+
URL, " ",
92+
grisp_tools_util:shell_quote(BuildPath)
93+
],
94+
{{ok, Output}, S3} = shell(S2, Cmd),
9395
event(mapz:deep_put([build, download], true, S3), [{output, Output}]).
9496

9597
prepare(S0) ->
@@ -208,10 +210,12 @@ install(S0) ->
208210
grisp_tools_util:ensure_dir(filename:join(InstallPath, ".")),
209211
S1 = grisp_tools_util:pipe(S0, [
210212
fun(S) -> shell_ok(S,
211-
["rm -rf ", filename:join(InstallPath, "*")],
213+
["rm -rf ",
214+
grisp_tools_util:shell_quote(InstallPath), "/*"],
212215
[{cd, InstallPath}]) end,
213216
fun(S) -> shell_ok(S,
214-
["make install DESTDIR=", $", InstallPath, $"],
217+
["make install DESTDIR=",
218+
grisp_tools_util:shell_quote(InstallPath)],
215219
[{cd, BuildPath}]) end
216220
]),
217221

@@ -254,7 +258,8 @@ tar(#{build := #{flags := #{tar := true}}} = S0) ->
254258
Name = grisp_tools_util:package_name(S0),
255259
Package = filename:join(PackagePath, Name),
256260
grisp_tools_util:ensure_dir(Package),
257-
shell_ok(S0, ["tar -zcf ", Package, " ."], [{cd, InstallPath}]),
261+
shell_ok(S0, ["tar -zcf ", grisp_tools_util:shell_quote(Package), " ."],
262+
[{cd, InstallPath}]),
258263
event(S0, [{file, relative(Package)}]);
259264
tar(S0) ->
260265
event(S0, ['_skip']).
@@ -276,30 +281,40 @@ dockerize_command(Cmd, S0) ->
276281
BuidPath = binary_to_list(mapz:deep_get([paths, build], S0)),
277282
{docker, Image} = mapz:deep_get([paths, toolchain], S0),
278283
BuildSubdir = string:prefix(BuidPath, Cwd),
279-
["docker run",
280-
" --rm ",
281-
[" -e " ++ K ++ "=" ++ io_lib:format("~s",[V])|| {K,V} <- maps:to_list(Env)],
282-
" --volume " ++ Cwd ++ ":" ++ Cwd,
283-
" " ++ Image ++ " sh -c \"cd " ++ Cwd ++ BuildSubdir,
284-
" && " , Cmd, "\""].
284+
VolumeArg = grisp_tools_util:shell_quote([Cwd, ":", Cwd]),
285+
EnvArgs = [
286+
[" -e ", grisp_tools_util:shell_quote([K, "=", V])]
287+
|| {K, V} <- maps:to_list(Env)
288+
],
289+
Script = ["cd ", grisp_tools_util:shell_quote([Cwd, BuildSubdir]), " && ", Cmd],
290+
[
291+
"docker run --rm",
292+
EnvArgs,
293+
" --volume ", VolumeArg,
294+
" ", grisp_tools_util:shell_quote(Image),
295+
" sh -c ", grisp_tools_util:shell_quote(Script)
296+
].
285297

286298
apply_patch({Name, Patch}, State0) ->
287299
Dir = mapz:deep_get([paths, build], State0),
288300
Context = mapz:deep_get([build, context], State0),
289301
grisp_tools_util:copy_file(Dir, Patch, Context),
290302
State4 = case shell(State0,
291-
["git apply ", Name, " --ignore-whitespace --reverse --check"],
303+
["git apply ", grisp_tools_util:shell_quote(Name),
304+
" --ignore-whitespace --reverse --check"],
292305
[{cd, Dir}, return_on_error]) of
293306
{{ok, _Output}, State1} ->
294307
event(State1, [{skip, Patch}]);
295308
{{error, {1, _}}, State1} ->
296309
State2 = event(State1, [{apply, Patch}]),
297310
{{ok, _}, State3} = shell(State2,
298-
"git apply --ignore-whitespace " ++ Name,
311+
["git apply --ignore-whitespace ",
312+
grisp_tools_util:shell_quote(Name)],
299313
[{cd, Dir}]),
300314
State3
301315
end,
302-
{{ok, _}, State5} = shell(State4, "rm " ++ Name, [{cd, Dir}]),
316+
{{ok, _}, State5} = shell(State4, ["rm ", grisp_tools_util:shell_quote(Name)],
317+
[{cd, Dir}]),
303318
State5.
304319

305320
sort_files(Apps, Files) ->
@@ -337,7 +352,7 @@ run_hooks(#{paths := #{toolchain := {ToolchainType, _}}} = S0, Type, Opts) ->
337352

338353
delete_directory(BuildPath, S0) ->
339354
case filelib:is_dir(BuildPath) of
340-
true -> shell_ok(S0, "rm -rf \"" ++ binary_to_list(BuildPath) ++ "\"", []);
355+
true -> shell_ok(S0, ["rm -rf ", grisp_tools_util:shell_quote(BuildPath)], []);
341356
false -> S0
342357
end.
343358

@@ -362,11 +377,13 @@ check_git_repository(BuildPath, S0) ->
362377
repo_sanity_check(Repo, S0) ->
363378
PlumbingTests = [
364379
{
365-
"git --git-dir "++ Repo ++" fetch --dry-run",
380+
["git --git-dir ", grisp_tools_util:shell_quote(Repo),
381+
" fetch --dry-run"],
366382
fun("") -> true; (_) -> false end
367383
},
368384
{
369-
"git --git-dir "++ Repo ++" describe --tags",
385+
["git --git-dir ", grisp_tools_util:shell_quote(Repo),
386+
" describe --tags"],
370387
fun(Out) ->
371388
{_,_,_,V} = maps:get(otp_version, S0),
372389
VersionTag = "OTP-"++binary_to_list(V)++"\n",

src/grisp_tools_deploy.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ validate_manifest(Data, Expected) ->
311311
{internal_manifest_error, Reason, Data}
312312
end
313313
after
314-
os:cmd("rm -f " ++ TempFilePath)
314+
_ = file:delete(TempFilePath)
315315
end.
316316

317317
dist_write_manifest(State0 = #{platform := Platform,

src/grisp_tools_firmware.erl

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ cleanup_image(State) ->
250250
State.
251251

252252
cleanup_temp_dir(State = #{temp_dir := TempDir, cleanup_temp_dir := true}) ->
253-
{_, State2} = shell(State, ["rm -rf '", TempDir, "'"]),
253+
{_, State2} = shell(State, ["rm -rf ", grisp_tools_util:shell_quote(TempDir)]),
254254
State2#{cleanup_temp_dir => false};
255255
cleanup_temp_dir(State) ->
256256
State.
@@ -336,17 +336,24 @@ select_bootloader(State, BaseDir, [Filename | Rest], CheckFun) ->
336336
end.
337337

338338
docker_check_image(State, ImageName) ->
339-
Command = ["docker manifest inspect '", ImageName, "'"],
339+
Command = ["docker manifest inspect ", grisp_tools_util:shell_quote(ImageName)],
340340
case shell(State, Command, [return_on_error]) of
341341
{{ok, _}, State2} -> {ok, State2};
342342
{{error, _}, State2} -> {error, State2}
343343
end.
344344

345345
docker_export(State, ImageName, InPath, OutDir) ->
346346
ok = grisp_tools_util:ensure_dir(OutDir),
347-
Command = ["docker run --rm --volume ", OutDir, ":", OutDir,
348-
" ", ImageName, " sh -c \"cd ", OutDir,
349-
" && cp -rf '", InPath, "' .\""],
347+
VolumeArg = grisp_tools_util:shell_quote([OutDir, ":", OutDir]),
348+
Script = [
349+
"cd ", grisp_tools_util:shell_quote(OutDir),
350+
" && cp -rf ", grisp_tools_util:shell_quote(InPath), " ."
351+
],
352+
Command = [
353+
"docker run --rm --volume ", VolumeArg,
354+
" ", grisp_tools_util:shell_quote(ImageName),
355+
" sh -c ", grisp_tools_util:shell_quote(Script)
356+
],
350357
case shell(State, Command, [return_on_error]) of
351358
{{ok, _}, State2} -> {ok, State2};
352359
{{error, _}, State2} -> {error, State2}
@@ -370,7 +377,10 @@ deploy_bundle(State = #{edifa_pid := Pid, bundle := BundleFile}, PartId) ->
370377
{error, Reason, State2} ->
371378
event(State2, [{error, Reason}]);
372379
{ok, MountPoint, State2} ->
373-
ExpandCmd = ["tar -C ", MountPoint, " -xzf ", BundleFile],
380+
ExpandCmd = [
381+
"tar -C ", grisp_tools_util:shell_quote(MountPoint),
382+
" -xzf ", grisp_tools_util:shell_quote(BundleFile)
383+
],
374384
{{ok, _}, State3} = shell(State2, ExpandCmd),
375385
Opts2 = edifa_opts(State2),
376386
case edifa:unmount(Pid, PartId, Opts2) of

src/grisp_tools_report.erl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ run(State) ->
3030

3131
clean(#{flags := #{tar := true}} = S0) -> S0;
3232
clean(#{report_dir := ReportDir} = S0) ->
33-
Cmd = lists:append(["rm -r ", ReportDir]),
33+
Cmd = ["rm -r ", grisp_tools_util:shell_quote(ReportDir)],
3434
{_, S1} = shell(S0, Cmd, [return_on_error]),
3535
S1.
3636

@@ -55,7 +55,11 @@ tar(#{report_dir := ReportDir, flags := #{tar := true}} = S0) ->
5555
Dir = filename:dirname(ReportDir),
5656
TarFilename = "grisp-report_" ++ format_datetime() ++ ".tar.gz",
5757
TarPath = filename:join(Dir, TarFilename),
58-
Cmd = lists:append(["tar -czvf ", TarPath, " -C ", ReportDir, " ."]),
58+
Cmd = [
59+
"tar -czvf ", grisp_tools_util:shell_quote(TarPath),
60+
" -C ", grisp_tools_util:shell_quote(ReportDir),
61+
" ."
62+
],
5963
{{ok, _Res}, S1} = shell(S0, Cmd),
6064
event(S1, [TarPath])
6165
end.

src/grisp_tools_util.erl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
-export([maybe_relative/3]).
4545
-export([filelib_ensure_path/1]).
4646
-export([format_term/1]).
47+
-export([shell_quote/1]).
4748

4849

4950
%--- Macros --------------------------------------------------------------------
@@ -363,6 +364,21 @@ format_term(Term) ->
363364
Bin = unicode:characters_to_binary(IoData, utf8),
364365
<<"%% coding: utf-8\n", Bin/binary>>.
365366

367+
%% @doc Shell-quotes a value so it can be safely used as a single argument in
368+
%% POSIX shells (sh/bash/zsh). Uses single quotes and escapes embedded single
369+
%% quotes using the standard `'"'"'` sequence.
370+
-spec shell_quote(iodata()) -> iolist().
371+
shell_quote(Value) ->
372+
Bin = unicode:characters_to_binary(iolist_to_binary(Value), utf8, utf8),
373+
["'", shell_quote_bin(Bin), "'"].
374+
375+
shell_quote_bin(<<>>) ->
376+
[];
377+
shell_quote_bin(<<$', Rest/binary>>) ->
378+
["'\"'\"'", shell_quote_bin(Rest)];
379+
shell_quote_bin(<<C/utf8, Rest/binary>>) ->
380+
[C | shell_quote_bin(Rest)].
381+
366382

367383
%--- Internal ------------------------------------------------------------------
368384

0 commit comments

Comments
 (0)