This commit is contained in:
Craig Everett 2017-12-06 14:05:21 +09:00
parent 8476f3a89a
commit 8d97664056

231
zx
View File

@ -90,37 +90,17 @@ start(["set", "version", VersionString]) ->
start(["list", "realms"]) -> start(["list", "realms"]) ->
list_realms(); list_realms();
start(["list", "packages", Realm]) -> start(["list", "packages", Realm]) ->
case valid_lower0_9(Realm) of ok = valid_realm(Realm),
true -> list_packages(Realm);
list_packages(Realm);
false ->
ok = log(error, "Bad realm name."),
halt(1)
end;
start(["list", "versions", Package]) -> start(["list", "versions", Package]) ->
case string:lexemes(Package, "-") of Package = string_to_package(PackageName),
[Realm, Name] -> list_versions(Package);
list_versions({Realm, Name}); start(["list", "pending", PackageName]) ->
_ -> Package = string_to_package(PackageName),
ok = log(error, "Bad package name."), list_pending(Package);
halt(1)
end;
start(["list", "pending", Package]) ->
case string:lexemes(Package, "-") of
[Realm, Name] ->
list_pending({Realm, Name});
_ ->
ok = log(error, "Bad package name."),
halt(1)
end;
start(["list", "resigns", Realm]) -> start(["list", "resigns", Realm]) ->
case valid_lower0_9(Realm) of ok = valid_realm(Realm),
true -> list_resigns(Realm);
list_resigns(Realm);
false ->
ok = log(error, "Bad realm name."),
halt(1)
end;
start(["add", "realm", RealmFile]) -> start(["add", "realm", RealmFile]) ->
add_realm(RealmFile); add_realm(RealmFile);
start(["add", "package", PackageName]) -> start(["add", "package", PackageName]) ->
@ -130,8 +110,7 @@ start(["add", "packager", Package, UserName]) ->
start(["add", "maintainer", Package, UserName]) -> start(["add", "maintainer", Package, UserName]) ->
add_maintainer(Package, UserName); add_maintainer(Package, UserName);
start(["review", PackageString]) -> start(["review", PackageString]) ->
PackageID = package_id(PackageString), review(PackageString);
review(PackageID);
start(["approve", PackageString]) -> start(["approve", PackageString]) ->
PackageID = package_id(PackageString), PackageID = package_id(PackageString),
approve(PackageID); approve(PackageID);
@ -167,6 +146,8 @@ start(["submit", PackageFile]) ->
submit(PackageFile); submit(PackageFile);
start(["dialyze"]) -> start(["dialyze"]) ->
dialyze(); dialyze();
start(["create", "user", Realm, Name]) ->
create_user(Realm, Name);
start(["create", "keypair"]) -> start(["create", "keypair"]) ->
create_keypair(); create_keypair();
start(["create", "plt"]) -> start(["create", "plt"]) ->
@ -634,6 +615,10 @@ list_packages(Realm) ->
-spec list_versions(package()) -> no_return(). -spec list_versions(package()) -> no_return().
%% @private
%% List the available versions of the package indicated. The user enters a string-form
%% package name (such as "otpr-zomp") and the return values will be full package strings
%% of the form "otpr-zomp-1.2.3", one per line printed to stdout.
list_versions(Package = {Realm, Name}) -> list_versions(Package = {Realm, Name}) ->
ok = valid_package(Package), ok = valid_package(Package),
@ -656,6 +641,10 @@ list_versions(Package = {Realm, Name}) ->
-spec list_pending(package()) -> no_return(). -spec list_pending(package()) -> no_return().
%% @private
%% List the versions of a package that are pending review. The package name is input by the
%% user as a string of the form "otpr-zomp" and the output is a list of full package IDs,
%% printed one per line to stdout (like "otpr-zomp-3.2.2").
list_pending(Package = {Realm, Name}) -> list_pending(Package = {Realm, Name}) ->
ok = valid_package(Package), ok = valid_package(Package),
@ -677,7 +666,34 @@ list_pending(Package = {Realm, Name}) ->
end. end.
-spec list_resigns(realm()) -> no_return().
%% @private
%% List the package ids of all packages waiting in the resign queue for the given realm,
%% printed to stdout one per line.
list_resigns(Realm) ->
Socket = connect_auth_or_die(Realm),
ok = send(Socket, {list_resigns, Realm}),
case recv_or_die(Socket) of
{ok, []} ->
Message = "No packages pending signature in ~tp.",
ok = log(info, Message, [Realm]),
halt(0);
{ok, PackageIDs} ->
Print =
fun(PackageID) ->
PackageString = package_string(PackageID),
io:format("~ts~n", [PackageString])
end,
ok = lists:foreach(Print, PackageIDs),
halt(0)
end.
-spec valid_package(package()) -> ok | no_return(). -spec valid_package(package()) -> ok | no_return().
%% @private
%% Test whether a package() type is a valid value or not. If not, halt execution with
%% a non-zero error code, if so then return `ok'.
valid_package({Realm, Name}) -> valid_package({Realm, Name}) ->
case {valid_lower0_9(Realm), valid_lower0_9(Name)} of case {valid_lower0_9(Realm), valid_lower0_9(Name)} of
@ -692,6 +708,40 @@ valid_package({Realm, Name}) ->
{false, false} -> {false, false} ->
ok = log(error, "Invalid realm ~tp and package ~tp", [Realm, Name]), ok = log(error, "Invalid realm ~tp and package ~tp", [Realm, Name]),
halt(1) halt(1)
end;
valid_package(Bad) ->
ok = log(error, "Invalid package() value: ~160tp", [Bad]),
halt(1).
-spec string_to_package(string()) -> ok | no_return().
%% @private
%% Convert a string to a package() type if possible. If not then halt the system.
string_to_package(String) ->
case string:lexemes(Package, "-") of
[Realm, Name] ->
Package = {Realm, Name},
ok = valid_package(Package),
Package;
_ ->
ok = log(error, "Bad package name."),
halt(1)
end.
-spec valid_realm(realm()) -> ok | no_return().
%% @private
%% Test whether a realm name is a valid realm() type (that is, a lower0_9()) or not. If not,
%% halt execution with a non-zero error code, if so then return `ok'.
valid_realm(Realm) ->
case valid_lower0_9(Realm) of
true ->
ok;
false ->
ok = log(error, "Bad realm name."),
halt(1)
end. end.
@ -776,25 +826,6 @@ add_package(Realm, Name) ->
halt(0). halt(0).
list_resigns(Realm) ->
Socket = connect_auth_or_die(Realm),
ok = send(Socket, {list_resigns, Realm}),
case recv_or_die(Socket) of
{ok, []} ->
Message = "No packages pending signature in ~tp.",
ok = log(info, Message, [Realm]),
halt(0);
{ok, PackageIDs} ->
Print =
fun(PackageID) ->
PackageString = package_string(PackageID),
io:format("~ts~n", [PackageString])
end,
ok = lists:foreach(Print, PackageIDs),
halt(0)
end.
add_packager(Package, UserName) -> add_packager(Package, UserName) ->
ok = log(info, "Would add ~ts to packagers for ~160tp now.", [UserName, Package]), ok = log(info, "Would add ~ts to packagers for ~160tp now.", [UserName, Package]),
halt(0). halt(0).
@ -805,34 +836,34 @@ add_maintainer(Package, UserName) ->
halt(0). halt(0).
review(PackageID) -> review(PackageString) ->
PackageString = package_string(PackageID), PackageID = package_id(PackageString),
ZrpPath = PackageString ++ ".zrp",
ok = log(info, "Saving to ~ts, unpacking to ./~ts/", [ZrpPath, PackageString]),
ok =
case {filelib:is_file(ZrpPath), filelib:is_file(PackageString)} of
{false, false} ->
ok;
{true, false} ->
ok = log(error, "~ts already exists. Aborting.", [ZrpPath]),
halt(1);
{false, true} ->
ok = log(error, "~ts already exists. Aborting.", [PackageString]),
halt(1);
{true true} ->
Message = "~ts and ~ts already exist. Aborting.",
ok = log(error, Message, [ZrpPath, PackageString]),
halt(1);
end,
Socket = connect_auth_or_die(), Socket = connect_auth_or_die(),
ok = send(Socket, {review, PackageID}), ok = send(Socket, {review, PackageID}),
ok = receive_or_die(Socket), sending = recv_or_die(Socket),
{ok, ZrpBin} = recieve_or_die(Socket), {ok, ZrpBin} = recv_or_die(Socket),
ok = file:write_file(ZrpPath, ZrpBin),
ok = disconnect(Socket), ok = disconnect(Socket),
{"zomp.meta", MetaBin} = erl_tar:extract(ZrpBin, [memory, {files, "zomp.meta"}]), {ok, Files} = erl_tar:extract({binary, ZrpBin}, [memory]),
{"zomp.meta", MetaBin} = lists:keyfind("zomp.meta", 1, Files),
Meta = binary_to_term(MetaBin, [safe]), Meta = binary_to_term(MetaBin, [safe]),
PackageID = maps:get(package_id, Meta),
{KeyID, Signature} = maps:get(sig, Meta),
{ok, PubKey} = loadkey(public, KeyID),
TgzFile = PackageString ++ ".tgz",
{TgzFile, TgzData} = lists:keyfind(TgzFile, 1, Files),
ok = verify(TgzData, Signature, PubKey),
ok =
case file:make_dir(PackageString) of
ok ->
log(info, "Will unpack to directory ./~ts", [PackageString]);
{error, Error} ->
Message = "Creating dir ./~ts failed with ~ts. Aborting."
ok = log(error, Message, [PackageString, Error]),
halt(1)
end,
ok = erl_tar:extract({binary, TgzData}, [compressed, {cwd, PackageString}]),
ok = log(info, "Unpacked and awaiting inspection."),
halt(0).
approve(PackageID = {Realm, _, _}) -> approve(PackageID = {Realm, _, _}) ->
@ -851,8 +882,36 @@ reject(PackageID = {Realm, _, _}) ->
halt(0). halt(0).
resign(PackageID) -> resign(PackageString) ->
M = "Pull the indicated package", PackageID = {Realm, _, _} = package_id(PackageString),
Socket = connect_auth_or_die(Realm),
ok = send(Socket, {resign, PackageID}),
sending = recv_or_die(Socket),
{ok, ZrpBin} = recv_or_die(Socket),
{ok, Files} = erl_tar:extract({binary, ZrpBin}, [memory]),
{"zomp.meta", MetaBin} = lists:keyfind("zomp.meta", 1, Files),
Meta = binary_to_term(MetaBin, [safe]),
PackageID = maps:get(package_id, Meta),
{KeyID, Signature} = maps:get(sig, Meta),
{ok, PubKey} = loadkey(public, KeyID),
TgzFile = PackageString ++ ".tgz",
{TgzFile, TgzData} = lists:keyfind(TgzFile, 1, Files),
ok = verify(TgzData, Signature, PubKey),
RealmConf = read_realm_conf(Realm),
{package_keys, PackageKeys} = lists:keyfind(package_keys, 1, RealmConf),
PackageKeyName = select(PackageKeys),
PackageKeyID = {Realm, PackageKeyName}
{ok, PackageKey} = loadkey(private, PackageKeyID),
ReSignature = public_key:sign(TgzData, sha512, PackageKey),
FinalMeta = maps:put(sig, {PackageKeyID, ReSignature}),
NewFiles = lists:keystore("zomp.meta", 1, term_to_binary(FinalMeta)),
ResignedZrp = PackageString ++ ".zrp",
ok = erl_tar:create(ResignedZrp, NewFiles),
{ok, ResignedBin} = file:read_file(ResignedFile),
ok = send(Socket, {resigned, ResignedBin}),
ok = recv_or_die(Socket),
ok = file:delete(ResignedZrp),
ok = disconnect(Socket),
ok = log(info, M, [PackageID]), ok = log(info, M, [PackageID]),
halt(0). halt(0).
@ -2392,7 +2451,7 @@ install(PackageID) ->
{TgzFile, TgzData} = lists:keyfind(TgzFile, 1, Files), {TgzFile, TgzData} = lists:keyfind(TgzFile, 1, Files),
{"zomp.meta", MetaBin} = lists:keyfind("zomp.meta", 1, Files), {"zomp.meta", MetaBin} = lists:keyfind("zomp.meta", 1, Files),
Meta = binary_to_term(MetaBin), Meta = binary_to_term(MetaBin),
{sig, {KeyID, Signature}} = lists:keyfind(sig, 1, Meta), {KeyID, Signature} = maps:get(sig, 1, Meta),
{ok, PubKey} = loadkey(public, KeyID), {ok, PubKey} = loadkey(public, KeyID),
ok = ensure_package_dirs(PackageID), ok = ensure_package_dirs(PackageID),
PackageDir = filename:join("lib", PackageString), PackageDir = filename:join("lib", PackageString),
@ -3052,15 +3111,28 @@ usage() ->
T = "~n" T = "~n"
"zx~n" "zx~n"
"~n" "~n"
"Usage:~n" "Usage: zx [command] [object] [args]~n"
"~n"
"Examples:~n"
" zx help~n" " zx help~n"
" zx run~n"
" zx run PackageID [Args]~n" " zx run PackageID [Args]~n"
" zx init Type PackageID~n" " zx init Type PackageID~n"
" zx install PackageID~n" " zx install PackageID~n"
" zx set dep PackageID~n" " zx set dep PackageID~n"
" zx set version Version~n" " zx set version Version~n"
" zx list realms~n"
" zx list packages Realm~n"
" zx list versions PackageName~n"
" zx list pending PackageName~n"
" zx list resigns Realm~n"
" zx add realm RealmFile~n" " zx add realm RealmFile~n"
" zx add package PackageName~n"
" zx add packager PackageName~n"
" zx add maintainer PackageName~n"
" zx review PackageID~n"
" zx approve PackageID~n"
" zx reject PackageID~n"
" zx resign PackageID~n"
" zx drop dep PackageID~n" " zx drop dep PackageID~n"
" zx drop key Realm KeyName~n" " zx drop key Realm KeyName~n"
" zx drop realm Realm~n" " zx drop realm Realm~n"
@ -3068,6 +3140,7 @@ usage() ->
" zx runlocal [Args]~n" " zx runlocal [Args]~n"
" zx package [Path]~n" " zx package [Path]~n"
" zx submit Path~n" " zx submit Path~n"
" zx create user Realm Username~n"
" zx create keypair~n" " zx create keypair~n"
" zx create plt~n" " zx create plt~n"
" zx create realm~n" " zx create realm~n"