Skip to content

Commit fd0a537

Browse files
committed
Add grisp pack command
1 parent 6410ce7 commit fd0a537

File tree

5 files changed

+215
-22
lines changed

5 files changed

+215
-22
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ and this project adheres to
2323
actions will **NOT** be called if the actions do not raise any exception.
2424
[#25](https://github.com/grisp/grisp_tools/pull/25)
2525
- Add a firmware command to generate GRiSP 2 binary firmwares: [#26](https://github.com/grisp/grisp_tools/pull/26)
26+
- Add a pack command to generate a GRiSP 2 software update package: [#28](https://github.com/grisp/grisp_tools/pull/28)
2627

2728
### Changed
2829

rebar.config

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
{mapz, "~> 2.2"},
44
bbmustache,
55
hackney,
6-
edifa
6+
edifa,
7+
grisp_update_packager
78
]}.

rebar.lock

+33-21
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,53 @@
11
{"1.2.0",
22
[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.12.2">>},0},
3-
{<<"certifi">>,{pkg,<<"certifi">>,<<"2.9.0">>},1},
3+
{<<"certifi">>,{pkg,<<"certifi">>,<<"2.12.0">>},1},
44
{<<"edifa">>,{pkg,<<"edifa">>,<<"1.0.0">>},0},
55
{<<"erlexec">>,{pkg,<<"erlexec">>,<<"2.0.7">>},1},
6-
{<<"hackney">>,{pkg,<<"hackney">>,<<"1.18.1">>},0},
6+
{<<"grisp_update_packager">>,{pkg,<<"grisp_update_packager">>,<<"1.0.0">>},0},
7+
{<<"hackney">>,{pkg,<<"hackney">>,<<"1.20.1">>},0},
78
{<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},1},
8-
{<<"mapz">>,{pkg,<<"mapz">>,<<"2.2.0">>},0},
9+
{<<"mapz">>,{pkg,<<"mapz">>,<<"2.4.0">>},0},
910
{<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},1},
10-
{<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.2.0">>},1},
11-
{<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.1">>},1},
12-
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.6">>},1},
13-
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}]}.
11+
{<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.3.0">>},1},
12+
{<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.4.1">>},1},
13+
{<<"quickrand">>,{pkg,<<"quickrand">>,<<"2.0.7">>},2},
14+
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.7">>},1},
15+
{<<"termseal">>,{pkg,<<"termseal">>,<<"0.1.1">>},1},
16+
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1},
17+
{<<"uuid">>,{pkg,<<"uuid_erl">>,<<"2.0.7">>},1}]}.
1418
[
1519
{pkg_hash,[
1620
{<<"bbmustache">>, <<"0CABDCE0DB9FE6D3318131174B9F2B351328A4C0AFBEB3E6E99BB0E02E9B621D">>},
17-
{<<"certifi">>, <<"6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21">>},
21+
{<<"certifi">>, <<"2D1CCA2EC95F59643862AF91F001478C9863C2AC9CB6E2F89780BFD8DE987329">>},
1822
{<<"edifa">>, <<"0F1A01A0C79B7135F334B3FCEEB624F0574C5ED3E4554B06C8664AADA6A339C8">>},
1923
{<<"erlexec">>, <<"76D0BC7487929741B5BB9F74DA2AF5DAF1492134733CF9A05C7AAA278B6934C5">>},
20-
{<<"hackney">>, <<"F48BF88F521F2A229FC7BAE88CF4F85ADC9CD9BCF23B5DC8EB6A1788C662C4F6">>},
24+
{<<"grisp_update_packager">>, <<"0532CCD0955398FAC4E1DE90FE85DB941CA609A2F4E066CFFE01ECE41DCCE119">>},
25+
{<<"hackney">>, <<"8D97AEC62DDDDD757D128BFD1DF6C5861093419F8F7A4223823537BAD5D064E2">>},
2126
{<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>},
22-
{<<"mapz">>, <<"81EE5AD249BBC9427DAA6C3EE5166F88A448C5B0B2F5BBEE0495266308AFF2BA">>},
27+
{<<"mapz">>, <<"77A8E38B69BAB16C5D3EBD44E6C619F8AF1F1598B0CAAE301D266605A0865756">>},
2328
{<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>},
24-
{<<"mimerl">>, <<"67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3">>},
25-
{<<"parse_trans">>, <<"16328AB840CC09919BD10DAB29E431DA3AF9E9E7E7E6F0089DD5A2D2820011D8">>},
26-
{<<"ssl_verify_fun">>, <<"CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0">>},
27-
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]},
29+
{<<"mimerl">>, <<"D0CD9FC04B9061F82490F6581E0128379830E78535E017F7780F37FEA7545726">>},
30+
{<<"parse_trans">>, <<"6E6AA8167CB44CC8F39441D05193BE6E6F4E7C2946CB2759F015F8C56B76E5FF">>},
31+
{<<"quickrand">>, <<"D2BD76676A446E6A058D678444B7FDA1387B813710D1AF6D6E29BB92186C8820">>},
32+
{<<"ssl_verify_fun">>, <<"354C321CF377240C7B8716899E182CE4890C5938111A1296ADD3EC74CF1715DF">>},
33+
{<<"termseal">>, <<"C9D93D4FF638EE99F9377D3438FC7AD132D2901EBBAF10C54F8DEA1D7E24D61C">>},
34+
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>},
35+
{<<"uuid">>, <<"B2078D2CC814F53AFA52D36C91E08962C7E7373585C623F4C0EA6DFB04B2AF94">>}]},
2836
{pkg_hash_ext,[
2937
{<<"bbmustache">>, <<"688B33A4D5CC2D51F575ADF0B3683FC40A38314A2F150906EDCFC77F5B577B3B">>},
30-
{<<"certifi">>, <<"266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641">>},
38+
{<<"certifi">>, <<"EE68D85DF22E554040CDB4BE100F33873AC6051387BAF6A8F6CE82272340FF1C">>},
3139
{<<"edifa">>, <<"A1E010561E7D236A24C668D95626BE2BFE082ED0331CE1E6798BE0CD43F59A7B">>},
3240
{<<"erlexec">>, <<"AF2DD940BB8E32F5AA40A65CB455DCAA18F5334FD3507E9BFD14A021E9630897">>},
33-
{<<"hackney">>, <<"A4ECDAFF44297E9B5894AE499E9A070EA1888C84AFDD1FD9B7B2BC384950128E">>},
41+
{<<"grisp_update_packager">>, <<"47BFDF6FADBED4B8342205A812198CF913E0223A98A775CAAE5D2FB5D5CF751C">>},
42+
{<<"hackney">>, <<"FE9094E5F1A2A2C0A7D10918FEE36BFEC0EC2A979994CFF8CFE8058CD9AF38E3">>},
3443
{<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>},
35-
{<<"mapz">>, <<"861A878A86AB7E5897A9B57B3CA0B188FB53CE2935B153872C61F6641E709AA8">>},
44+
{<<"mapz">>, <<"4B68DF5CF0522E0D6545DF7B681BC052865CDB78405AD4CC9C55FE45EE7B25BE">>},
3645
{<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>},
37-
{<<"mimerl">>, <<"F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323">>},
38-
{<<"parse_trans">>, <<"07CD9577885F56362D414E8C4C4E6BDF10D43A8767ABB92D24CBE8B24C54888B">>},
39-
{<<"ssl_verify_fun">>, <<"BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680">>},
40-
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]}
46+
{<<"mimerl">>, <<"A1E15A50D1887217DE95F0B9B0793E32853F7C258A5CD227650889B38839FE9D">>},
47+
{<<"parse_trans">>, <<"620A406CE75DADA827B82E453C19CF06776BE266F5A67CFF34E1EF2CBB60E49A">>},
48+
{<<"quickrand">>, <<"B8ACBF89A224BC217C3070CA8BEBC6EB236DBE7F9767993B274084EA044D35F0">>},
49+
{<<"ssl_verify_fun">>, <<"FE4C190E8F37401D30167C8C405EDA19469F34577987C76DDE613E838BBC67F8">>},
50+
{<<"termseal">>, <<"466280936214AF1894FC431642E83341B7D13580A3F3485820A2D300C5CAEB49">>},
51+
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>},
52+
{<<"uuid">>, <<"4E4C5CA3461DC47C5E157ED42AA3981A053B7A186792AF972A27B14A9489324E">>}]}
4153
].

src/grisp_tools.erl

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
-export([report/1]).
1010
-export([configure/1]).
1111
-export([firmware/1]).
12+
-export([pack/1]).
1213

1314
%--- API -----------------------------------------------------------------------
1415

@@ -33,3 +34,5 @@ report(Opts) -> grisp_tools_report:run(Opts).
3334
configure(Opts) -> grisp_tools_configure:run(Opts).
3435

3536
firmware(Opts) -> grisp_tools_firmware:run(Opts).
37+
38+
pack(Opts) -> grisp_tools_pack:run(Opts).

src/grisp_tools_pack.erl

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
-module(grisp_tools_pack).
2+
3+
-include("grisp_tools.hrl").
4+
-include_lib("kernel/include/file.hrl").
5+
6+
% API
7+
-export([run/1]).
8+
9+
-import(grisp_tools_util, [event/2]).
10+
11+
12+
%--- API -----------------------------------------------------------------------
13+
14+
run(State) ->
15+
grisp_tools_util:weave(State, [
16+
fun grisp_tools_step:config/1,
17+
{pack, [
18+
fun prepare/1,
19+
fun package/1,
20+
fun cleanup/1
21+
]}
22+
]).
23+
24+
%--- Tasks ---------------------------------------------------------------------
25+
26+
prepare(State) ->
27+
Force = maps:get(force, State, false),
28+
State2 = State#{force => Force},
29+
grisp_tools_util:weave(State2, [
30+
fun validate_temp_dir/1,
31+
fun validate_system/1,
32+
fun validate_bootloader/1,
33+
fun validate_package/1
34+
]).
35+
36+
validate_temp_dir(State) ->
37+
case maps:find(temp_dir, State) of
38+
{ok, TempDir} when TempDir =/= undefined ->
39+
case filelib:is_dir(TempDir) of
40+
true -> State;
41+
false -> event(State, [{error, directory_not_found, TempDir}])
42+
end;
43+
_ ->
44+
{Output, State2} = shell(State, "mktemp -d", []),
45+
TempDir = string:trim(Output),
46+
case filelib:is_dir(TempDir) of
47+
true ->
48+
State2#{temp_dir => TempDir,
49+
cleanup_temp_dir => true};
50+
false ->
51+
event(State2, [{error, directory_not_found, TempDir}])
52+
end
53+
end.
54+
55+
validate_system(State) ->
56+
case maps:find(system, State) of
57+
{ok, SysPath} when SysPath =/= undefined ->
58+
case filelib:is_file(SysPath) of
59+
true -> State;
60+
false ->
61+
event(State, [{error, system_not_found, SysPath}])
62+
end;
63+
_ ->
64+
event(State, [{error, missing_parameter, system}])
65+
end.
66+
67+
validate_bootloader(State = #{bootloader := BootPath})
68+
when BootPath =/= undefined ->
69+
case filelib:is_file(BootPath) of
70+
true -> State;
71+
false ->
72+
event(State, [{error, bootloader_not_found, BootPath}])
73+
end;
74+
validate_bootloader(State) ->
75+
State#{bootloader => undefined}.
76+
77+
validate_package(State) ->
78+
case maps:find(package, State) of
79+
{ok, PackageFile} when PackageFile =/= undefined ->
80+
prepare_output_file(State, PackageFile);
81+
_ ->
82+
event(State, [{error, missing_parameter, package}])
83+
end.
84+
85+
package(State) ->
86+
grisp_tools_util:weave(State, [
87+
fun expand_bootloader/1,
88+
fun expand_system/1,
89+
fun build_package/1,
90+
fun cleanup/1
91+
], [
92+
fun cleanup/1
93+
]).
94+
95+
expand_bootloader(State = #{bootloader := BootPath})
96+
when BootPath =/= undefined ->
97+
{ExpPath, State2} = maybe_expand(State, BootPath),
98+
State2#{bootloader => ExpPath};
99+
expand_bootloader(State) ->
100+
State.
101+
102+
expand_system(State = #{system := SysPath})
103+
when SysPath =/= undefined ->
104+
{ExpPath, State2} = maybe_expand(State, SysPath),
105+
State2#{system => ExpPath};
106+
expand_system(State) ->
107+
State.
108+
109+
build_package(State = #{package := PackageFile}) ->
110+
PackagerOpts1 = maps:with([name, version, block_size,
111+
key_file, system, bootloader], State),
112+
PackagerOpts2 = PackagerOpts1#{
113+
tarball => true,
114+
mbr => ?GRISP2_PARTITIONS
115+
},
116+
case grisp_update_packager:package(PackageFile, PackagerOpts2) of
117+
ok -> event(State, [{done, PackageFile}]);
118+
{error, Reason} -> event(State, [{error, Reason}])
119+
end.
120+
121+
cleanup(State) ->
122+
cleanup_temp_dir(State).
123+
124+
cleanup_temp_dir(State = #{temp_dir := TempDir, cleanup_temp_dir := true}) ->
125+
{_, State2} = shell(State, "rm -rf '~s'", [TempDir]),
126+
State2#{cleanup_temp_dir => false};
127+
cleanup_temp_dir(State) ->
128+
State.
129+
130+
131+
%--- Internal ------------------------------------------------------------------
132+
133+
shell(State, Fmt, Args) ->
134+
Cmd = binary_to_list(iolist_to_binary(io_lib:format(Fmt, Args))),
135+
{{ok, Output}, State2} = grisp_tools_util:shell(State, Cmd),
136+
{Output, State2}.
137+
138+
is_compressed(Path) ->
139+
Ext = <<".gz">>,
140+
case binary:matches(Path, Ext) of
141+
[{Pos, _Length}] when Pos + byte_size(Ext) =:= byte_size(Path) ->
142+
NoExt = binary:part(Path, 0, byte_size(Path) - byte_size(Ext)),
143+
{true, filename:basename(NoExt)};
144+
_ ->
145+
{false, filename:basename(Path)}
146+
end.
147+
148+
maybe_expand(State = #{temp_dir := TempDir}, Path) ->
149+
case is_compressed(Path) of
150+
{false, _} -> {Path, State};
151+
{true, ExpName} ->
152+
CompName = filename:basename(Path),
153+
State2 = event(State, [{expanding, CompName}]),
154+
TempCompPath = filename:join([TempDir, CompName]),
155+
TempExpPath = filename:join([TempDir, ExpName]),
156+
{_, State2} = shell(State, "cp '~s' '~s'", [Path, TempDir]),
157+
{_, State3} = shell(State2, "gunzip '~s'", [TempCompPath]),
158+
{TempExpPath, State3}
159+
end.
160+
161+
prepare_output_file(State, Filepath) ->
162+
Force = maps:get(force, State, false),
163+
case file:read_file_info(Filepath) of
164+
{ok, #file_info{}} when Force =:= false ->
165+
event(State, [{error, file_exists, Filepath}]);
166+
{ok, #file_info{type = regular}} when Force =:= true->
167+
case file:delete(Filepath) of
168+
ok -> State;
169+
{error, _Reason} ->
170+
event(State, [{error, file_access, Filepath}])
171+
end;
172+
{ok, #file_info{type = regular}} ->
173+
event(State, [{error, not_a_file, Filepath}]);
174+
{error, enoent} ->
175+
State
176+
end.

0 commit comments

Comments
 (0)