From 124f121d38322a3db50458caeac47e84dccee45c Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 6 May 2016 23:18:55 -0400 Subject: Support package hashes in structure and lockfile - the internal representation for package locks moves from `{Name, {pkg, PkgName, Vsn}, Lvl}` to `{Name, {pkg, PkgName, Vsn, Hash}, Lvl}` - the internal representation for packages moves from `{pkg, PkgName, Vsn}` to `{pkg, PkgName, Vsn, Hash}` - the hash can be `undefined`, meaning no check will be done - no checking is done yet. --- src/rebar.hrl | 1 + src/rebar_app_utils.erl | 16 ++++---- src/rebar_config.erl | 66 ++++++++++++++++++++++++++------- src/rebar_packages.erl | 2 +- src/rebar_pkg_resource.erl | 6 +-- src/rebar_prv_deps.erl | 2 +- test/mock_pkg_resource.erl | 11 +++--- test/rebar_deps_SUITE.erl | 2 +- test/rebar_install_deps_SUITE.erl | 2 +- test/rebar_lock_SUITE.erl | 78 ++++++++++++++++++++++++++++++++++++--- test/rebar_pkg_SUITE.erl | 14 +++---- test/rebar_test_utils.erl | 18 ++++----- 12 files changed, 163 insertions(+), 55 deletions(-) diff --git a/src/rebar.hrl b/src/rebar.hrl index 0b7f0b1..4e1ec00 100644 --- a/src/rebar.hrl +++ b/src/rebar.hrl @@ -22,6 +22,7 @@ -define(DEFAULT_TEST_DEPS_DIR, "test/lib"). -define(DEFAULT_RELEASE_DIR, "rel"). -define(DEFAULT_CONFIG_FILE, "rebar.config"). +-define(CONFIG_VERSION, "1.1.0"). -define(DEFAULT_CDN, "https://repo.hex.pm/"). -define(REMOTE_PACKAGE_DIR, "tarballs"). -define(REMOTE_REGISTRY_FILE, "registry.ets.gz"). diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl index d3ef841..7028c32 100644 --- a/src/rebar_app_utils.erl +++ b/src/rebar_app_utils.erl @@ -119,17 +119,17 @@ parse_dep(Dep, Parent, DepsDir, State, Locks, Level) -> parse_dep(Parent, {Name, Vsn, {pkg, PkgName}}, DepsDir, IsLock, State) -> {PkgName1, PkgVsn} = {ec_cnv:to_binary(PkgName), ec_cnv:to_binary(Vsn)}, - dep_to_app(Parent, DepsDir, Name, PkgVsn, {pkg, PkgName1, PkgVsn}, IsLock, State); + dep_to_app(Parent, DepsDir, Name, PkgVsn, {pkg, PkgName1, PkgVsn, undefined}, IsLock, State); parse_dep(Parent, {Name, {pkg, PkgName}}, DepsDir, IsLock, State) -> %% Package dependency with different package name from app name - dep_to_app(Parent, DepsDir, Name, undefined, {pkg, ec_cnv:to_binary(PkgName), undefined}, IsLock, State); + dep_to_app(Parent, DepsDir, Name, undefined, {pkg, ec_cnv:to_binary(PkgName), undefined, undefined}, IsLock, State); parse_dep(Parent, {Name, Vsn}, DepsDir, IsLock, State) when is_list(Vsn); is_binary(Vsn) -> %% Versioned Package dependency {PkgName, PkgVsn} = {ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)}, - dep_to_app(Parent, DepsDir, PkgName, PkgVsn, {pkg, PkgName, PkgVsn}, IsLock, State); + dep_to_app(Parent, DepsDir, PkgName, PkgVsn, {pkg, PkgName, PkgVsn, undefined}, IsLock, State); parse_dep(Parent, Name, DepsDir, IsLock, State) when is_atom(Name); is_binary(Name) -> %% Unversioned package dependency - dep_to_app(Parent, DepsDir, ec_cnv:to_binary(Name), undefined, {pkg, ec_cnv:to_binary(Name), undefined}, IsLock, State); + dep_to_app(Parent, DepsDir, ec_cnv:to_binary(Name), undefined, {pkg, ec_cnv:to_binary(Name), undefined, undefined}, IsLock, State); parse_dep(Parent, {Name, Source}, DepsDir, IsLock, State) when is_tuple(Source) -> dep_to_app(Parent, DepsDir, Name, [], Source, IsLock, State); parse_dep(Parent, {Name, _Vsn, Source}, DepsDir, IsLock, State) when is_tuple(Source) -> @@ -138,7 +138,9 @@ parse_dep(Parent, {Name, _Vsn, Source, Opts}, DepsDir, IsLock, State) when is_tu ?WARN("Dependency option list ~p in ~p is not supported and will be ignored", [Opts, Name]), dep_to_app(Parent, DepsDir, Name, [], Source, IsLock, State); parse_dep(Parent, {Name, {pkg, PkgName, Vsn}, Level}, DepsDir, IsLock, State) when is_integer(Level) -> - dep_to_app(Parent, DepsDir, Name, Vsn, {pkg, PkgName, Vsn}, IsLock, State); + dep_to_app(Parent, DepsDir, Name, Vsn, {pkg, PkgName, Vsn, undefined}, IsLock, State); +parse_dep(Parent, {Name, {pkg, PkgName, Vsn, Hash}, Level}, DepsDir, IsLock, State) when is_integer(Level) -> + dep_to_app(Parent, DepsDir, Name, Vsn, {pkg, PkgName, Vsn, Hash}, IsLock, State); parse_dep(Parent, {Name, Source, Level}, DepsDir, IsLock, State) when is_tuple(Source) , is_integer(Level) -> dep_to_app(Parent, DepsDir, Name, [], Source, IsLock, State); @@ -170,7 +172,7 @@ dep_to_app(Parent, DepsDir, Name, Vsn, Source, IsLock, State) -> AppInfo5 = rebar_app_info:profiles(AppInfo4, [default]), rebar_app_info:is_lock(AppInfo5, IsLock). -update_source(AppInfo, {pkg, PkgName, PkgVsn}, State) -> +update_source(AppInfo, {pkg, PkgName, PkgVsn, Hash}, State) -> {PkgName1, PkgVsn1} = case PkgVsn of undefined -> get_package(PkgName, "0", State); @@ -180,7 +182,7 @@ update_source(AppInfo, {pkg, PkgName, PkgVsn}, State) -> _ -> {PkgName, PkgVsn} end, - AppInfo1 = rebar_app_info:source(AppInfo, {pkg, PkgName1, PkgVsn1}), + AppInfo1 = rebar_app_info:source(AppInfo, {pkg, PkgName1, PkgVsn1, Hash}), Deps = rebar_packages:deps(PkgName1 ,PkgVsn1 ,State), diff --git a/src/rebar_config.erl b/src/rebar_config.erl index 8d7bcf4..031df8b 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -56,35 +56,73 @@ consult_lock_file(File) -> [] -> []; [Locks] when is_list(Locks) -> % beta lock file - Locks; + read_attrs(beta, Locks, []); [{Vsn, Locks}|Attrs] when is_list(Locks) -> % versioned lock file - %% Make sure the warning above is to be shown whenever a version - %% newer than the current one is being used, as we can't parse - %% all the contents of the lock file properly. - ?WARN("Rebar3 detected a lock file from a newer version. " - "It will be loaded in compatibility mode, but important " - "information may be missing or lost. It is recommended to " - "upgrade Rebar3.", []), + case Vsn of + ?CONFIG_VERSION -> + ok; + _ -> + %% Make sure the warning below is to be shown whenever a version + %% newer than the current one is being used, as we can't parse + %% all the contents of the lock file properly. + ?WARN("Rebar3 detected a lock file from a newer version. " + "It will be loaded in compatibility mode, but important " + "information may be missing or lost. It is recommended to " + "upgrade Rebar3.", []) + end, read_attrs(Vsn, Locks, Attrs) end. write_lock_file(LockFile, Locks) -> - NewLocks = write_attrs(Locks), + {NewLocks, Attrs} = write_attrs(Locks), %% Write locks in the beta format, at least until it's been long %% enough we can start modifying the lock format. - file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks])). + case Attrs of + [] -> % write the old beta copy to avoid changes + file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks])); + _ -> + file:write_file(LockFile, + io_lib:format("{~p,~p}.~n~p.~n", + [?CONFIG_VERSION, NewLocks, Attrs])) + end. -read_attrs(_Vsn, Locks, _Attrs) -> +read_attrs(_Vsn, Locks, Attrs) -> %% Beta copy does not know how to expand attributes, but %% is ready to support it. - Locks. + expand_locks(Locks, extract_pkg_hashes(Attrs)). + +extract_pkg_hashes(Attrs) -> + Props = case Attrs of + [First|_] -> First; + [] -> [] + end, + proplists:get_value(pkg_hash, Props, []). + +expand_locks([], _Hashes) -> + []; +expand_locks([{Name, {pkg,PkgName,Vsn}, Lvl} | Locks], Hashes) -> + Hash = proplists:get_value(Name, Hashes), + [{Name, {pkg,PkgName,Vsn,Hash}, Lvl} | expand_locks(Locks, Hashes)]; +expand_locks([Lock|Locks], Hashes) -> + [Lock | expand_locks(Locks, Hashes)]. write_attrs(Locks) -> %% No attribute known that needs to be taken out of the structure, %% just return terms as is. - Locks. - + {NewLocks, Hashes} = split_locks(Locks, [], []), + case Hashes of + [] -> {NewLocks, []}; + _ -> {NewLocks, [{pkg_hash, lists:sort(Hashes)}]} + end. +split_locks([], Locks, Hashes) -> + {lists:reverse(Locks), Hashes}; +split_locks([{Name, {pkg,PkgName,Vsn,undefined}, Lvl} | Locks], LAcc, HAcc) -> + split_locks(Locks, [{Name,{pkg,PkgName,Vsn},Lvl}|LAcc], HAcc); +split_locks([{Name, {pkg,PkgName,Vsn,Hash}, Lvl} | Locks], LAcc, HAcc) -> + split_locks(Locks, [{Name,{pkg,PkgName,Vsn},Lvl}|LAcc], [{Name, Hash}|HAcc]); +split_locks([Lock|Locks], LAcc, HAcc) -> + split_locks(Locks, [Lock|LAcc], HAcc). consult_file(File) -> Terms = consult_file_(File), diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl index d4b8a14..8b4611b 100644 --- a/src/rebar_packages.erl +++ b/src/rebar_packages.erl @@ -122,7 +122,7 @@ package_dir(State) -> Error end. -registry_checksum({pkg, Name, Vsn}, State) -> +registry_checksum({pkg, Name, Vsn, _Hash}, State) -> try ?MODULE:verify_table(State), ets:lookup_element(?PACKAGE_TABLE, {Name, Vsn}, 3) diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl index ec7e09d..257df21 100644 --- a/src/rebar_pkg_resource.erl +++ b/src/rebar_pkg_resource.erl @@ -19,7 +19,7 @@ lock(_AppDir, Source) -> Source. -needs_update(Dir, {pkg, _Name, Vsn}) -> +needs_update(Dir, {pkg, _Name, Vsn, _Hash}) -> [AppInfo] = rebar_app_discover:find_apps([Dir], all), case rebar_app_info:original_vsn(AppInfo) =:= ec_cnv:to_list(Vsn) of true -> @@ -28,7 +28,7 @@ needs_update(Dir, {pkg, _Name, Vsn}) -> true end. -download(TmpDir, Pkg={pkg, Name, Vsn}, State) -> +download(TmpDir, Pkg={pkg, Name, Vsn, _Hash}, State) -> CDN = rebar_state:get(State, rebar_packages_cdn, ?DEFAULT_CDN), {ok, PackageDir} = rebar_packages:package_dir(State), Package = binary_to_list(<>), @@ -40,7 +40,7 @@ download(TmpDir, Pkg={pkg, Name, Vsn}, State) -> {fetch_fail, Name, Vsn} end. -cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn}, Url, ETag, State) -> +cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn, _Hash}, Url, ETag, State) -> case request(Url, ETag) of {ok, cached} -> ?INFO("Version cached at ~s is up to date, reusing it", [CachePath]), diff --git a/src/rebar_prv_deps.erl b/src/rebar_prv_deps.erl index 9ff2bfa..c865276 100644 --- a/src/rebar_prv_deps.erl +++ b/src/rebar_prv_deps.erl @@ -97,7 +97,7 @@ display_dep(_State, {Name, _Vsn, Source}) when is_tuple(Source) -> display_dep(_State, {Name, _Vsn, Source, _Opts}) when is_tuple(Source) -> ?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]); %% Locked -display_dep(State, {Name, Source={pkg, _, Vsn}, Level}) when is_integer(Level) -> +display_dep(State, {Name, Source={pkg, _, Vsn, _}, Level}) when is_integer(Level) -> DepsDir = rebar_dir:deps_dir(State), AppDir = filename:join([DepsDir, ec_cnv:to_binary(Name)]), NeedsUpdate = case rebar_fetch:needs_update(AppDir, Source, State) of diff --git a/test/mock_pkg_resource.erl b/test/mock_pkg_resource.erl index a94fe2f..f837713 100644 --- a/test/mock_pkg_resource.erl +++ b/test/mock_pkg_resource.erl @@ -22,8 +22,9 @@ mock() -> mock([]). | {not_in_index, [{App, Vsn}]} | {pkgdeps, [{{App,Vsn}, [Dep]}]}, App :: string(), - Dep :: {App, string(), {pkg, App, Vsn}}, - Vsn :: string(). + Dep :: {App, string(), {pkg, App, Vsn, Hash}}, + Vsn :: string(), + Hash :: string() | undefined. mock(Opts) -> meck:new(?MOD, [no_link]), mock_lock(Opts), @@ -51,7 +52,7 @@ mock_update(Opts) -> ToUpdate = proplists:get_value(upgrade, Opts, []), meck:expect( ?MOD, needs_update, - fun(_Dir, {pkg, App, _Vsn}) -> + fun(_Dir, {pkg, App, _Vsn, _Hash}) -> lists:member(binary_to_list(App), ToUpdate) end). @@ -66,7 +67,7 @@ mock_vsn(_Opts) -> %% @doc For each app to download, create a dummy app on disk instead. %% The configuration for this one (passed in from `mock/1') includes: %% -%% - Specify a version with `{pkg, _, Vsn}' +%% - Specify a version with `{pkg, _, Vsn, _}' %% - Dependencies for each application must be passed of the form: %% `{pkgdeps, [{"app1", [{app2, ".*", {pkg, ...}}]}]}' -- basically %% the `pkgdeps' option takes a key/value list of terms to output directly @@ -76,7 +77,7 @@ mock_download(Opts) -> Config = proplists:get_value(config, Opts, []), meck:expect( ?MOD, download, - fun (Dir, {pkg, AppBin, Vsn}, _) -> + fun (Dir, {pkg, AppBin, Vsn, _}, _) -> App = binary_to_list(AppBin), filelib:ensure_dir(Dir), AppDeps = proplists:get_value({App,Vsn}, Deps, []), diff --git a/test/rebar_deps_SUITE.erl b/test/rebar_deps_SUITE.erl index c95854a..4ef9f79 100644 --- a/test/rebar_deps_SUITE.erl +++ b/test/rebar_deps_SUITE.erl @@ -405,5 +405,5 @@ in_warnings(git, Warns, NameRaw, VsnRaw) -> in_warnings(pkg, Warns, NameRaw, VsnRaw) -> Name = iolist_to_binary(NameRaw), Vsn = iolist_to_binary(VsnRaw), - 1 =< length([1 || {_, [AppName, {pkg, _, AppVsn}]} <- Warns, + 1 =< length([1 || {_, [AppName, {pkg, _, AppVsn, _}]} <- Warns, AppName =:= Name, AppVsn =:= Vsn]). diff --git a/test/rebar_install_deps_SUITE.erl b/test/rebar_install_deps_SUITE.erl index b8b70b3..9ff28c7 100644 --- a/test/rebar_install_deps_SUITE.erl +++ b/test/rebar_install_deps_SUITE.erl @@ -475,5 +475,5 @@ in_warnings(git, Warns, NameRaw, VsnRaw) -> in_warnings(pkg, Warns, NameRaw, VsnRaw) -> Name = iolist_to_binary(NameRaw), Vsn = iolist_to_binary(VsnRaw), - 1 =< length([1 || {_, [AppName, {pkg, _, AppVsn}]} <- Warns, + 1 =< length([1 || {_, [AppName, {pkg, _, AppVsn, _}]} <- Warns, AppName =:= Name, AppVsn =:= Vsn]). diff --git a/test/rebar_lock_SUITE.erl b/test/rebar_lock_SUITE.erl index 00875f7..f1ab3b5 100644 --- a/test/rebar_lock_SUITE.erl +++ b/test/rebar_lock_SUITE.erl @@ -7,7 +7,8 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). -all() -> [current_version, future_versions_no_attrs, future_versions_attrs]. +all() -> [current_version, + beta_version, future_versions_no_attrs, future_versions_attrs]. current_version(Config) -> %% Current version just dumps the locks as is on disk. @@ -15,9 +16,60 @@ current_version(Config) -> Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2}, {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, + {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}, + {<<"pkg2">>,{pkg,<<"name1">>,<<"1.1.6">>},2}, + {<<"pkg3">>,{pkg,<<"name2">>,<<"3.0.6">>},1} + ], + ExpandedNull = [ + {<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2}, + {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, + {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, + {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>,undefined},3}, + {<<"pkg2">>,{pkg,<<"name1">>,<<"1.1.6">>,undefined},2}, + {<<"pkg3">>,{pkg,<<"name2">>,<<"3.0.6">>,undefined},1} + ], + %% Simulate a beta lockfile + file:write_file(LockFile, io_lib:format("~p.~n", [Locks])), + %% No properties fetched from a beta lockfile, expand locks + %% to undefined + ?assertEqual(ExpandedNull, + rebar_config:consult_lock_file(LockFile)), + %% Adding hash data + Hashes = [{<<"pkg1">>, <<"tarballhash">>}, + {<<"pkg3">>, <<"otherhash">>}], + ExpandedLocks = [ + {<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2}, + {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, + {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, + {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>,<<"tarballhash">>},3}, + {<<"pkg2">>,{pkg,<<"name1">>,<<"1.1.6">>,undefined},2}, + {<<"pkg3">>,{pkg,<<"name2">>,<<"3.0.6">>,<<"otherhash">>},1} + ], + file:write_file(LockFile, + io_lib:format("~p.~n~p.~n", + [{"1.1.0", Locks}, + [{pkg_hash, Hashes}]])), + ?assertEqual(ExpandedLocks, rebar_config:consult_lock_file(LockFile)), + %% Then check that we can reverse that + ok = rebar_config:write_lock_file(LockFile, ExpandedLocks), + ?assertEqual({ok, [{"1.1.0", Locks}, [{pkg_hash, Hashes}]]}, + file:consult(LockFile)). + +beta_version(Config) -> + %% Current version just dumps the locks as is on disk. + LockFile = filename:join(?config(priv_dir, Config), "current_version"), + Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2}, + {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, + {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}], + ExpandedLocks = [ + {<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2}, + {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, + {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, + {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>,undefined},3} + ], file:write_file(LockFile, io_lib:format("~p.~n", [Locks])), - ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)). + ?assertEqual(ExpandedLocks, rebar_config:consult_lock_file(LockFile)). future_versions_no_attrs(Config) -> %% Future versions will keep the same core attribute in there, but @@ -27,10 +79,14 @@ future_versions_no_attrs(Config) -> Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2}, {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, - {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}], + {<<"pkg1">>, {pkg,<<"name">>,<<"0.1.6">>},3}], + ExpandedLocks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2}, + {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, + {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, + {<<"pkg1">>, {pkg,<<"name">>,<<"0.1.6">>,undefined},3}], LockData = {"3.5.2", Locks}, file:write_file(LockFile, io_lib:format("~p.~n", [LockData])), - ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)). + ?assertEqual(ExpandedLocks, rebar_config:consult_lock_file(LockFile)). future_versions_attrs(Config) -> %% Future versions will keep the same core attribute in there, but @@ -41,6 +97,16 @@ future_versions_attrs(Config) -> {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}], + ExpandedLocks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2}, + {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, + {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, + {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>, <<"tarballhash">>},3}], + Hashes = [{<<"pkg1">>, <<"tarballhash">>}], LockData = {"3.5.2", Locks}, - file:write_file(LockFile, io_lib:format("~p.~na.~n{b,c}.~n[d,e,f].~n", [LockData])), - ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)). + file:write_file(LockFile, + io_lib:format("~p.~n~p.~ngarbage.~n", + [LockData, + [{a, x}, + {pkg_hash, Hashes}, + {b, y}]])), + ?assertEqual(ExpandedLocks, rebar_config:consult_lock_file(LockFile)). diff --git a/test/rebar_pkg_SUITE.erl b/test/rebar_pkg_SUITE.erl index 6a75f32..8003b10 100644 --- a/test/rebar_pkg_SUITE.erl +++ b/test/rebar_pkg_SUITE.erl @@ -103,7 +103,7 @@ good_uncached(Config) -> {Pkg,Vsn} = ?config(pkg, Config), State = ?config(state, Config), ?assertEqual({ok, true}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), Cache = ?config(cache_dir, Config), ?assert(filelib:is_regular(filename:join(Cache, <>))). @@ -116,7 +116,7 @@ good_cached(Config) -> ?assert(filelib:is_regular(CachedFile)), {ok, Content} = file:read_file(CachedFile), ?assertEqual({ok, true}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), {ok, Content} = file:read_file(CachedFile). badindexchk(Config) -> @@ -124,7 +124,7 @@ badindexchk(Config) -> {Pkg,Vsn} = ?config(pkg, Config), State = ?config(state, Config), ?assertMatch({bad_registry_checksum, _Path}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), %% The cached file is there for forensic purposes Cache = ?config(cache_dir, Config), ?assert(filelib:is_regular(filename:join(Cache, <>))). @@ -134,7 +134,7 @@ badpkg(Config) -> {Pkg,Vsn} = ?config(pkg, Config), State = ?config(state, Config), ?assertMatch({bad_download, _Path}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), %% The cached file is there for forensic purposes Cache = ?config(cache_dir, Config), ?assert(filelib:is_regular(filename:join(Cache, <>))). @@ -148,7 +148,7 @@ bad_to_good(Config) -> ?assert(filelib:is_regular(CachedFile)), {ok, Contents} = file:read_file(CachedFile), ?assertEqual({ok, true}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), %% Cache has refreshed ?assert({ok, Contents} =/= file:read_file(CachedFile)). @@ -161,7 +161,7 @@ good_disconnect(Config) -> ?assert(filelib:is_regular(CachedFile)), {ok, Content} = file:read_file(CachedFile), ?assertEqual({ok, true}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), {ok, Content} = file:read_file(CachedFile). bad_disconnect(Config) -> @@ -169,7 +169,7 @@ bad_disconnect(Config) -> {Pkg,Vsn} = ?config(pkg, Config), State = ?config(state, Config), ?assertEqual({fetch_fail, Pkg, Vsn}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)). + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)). pkgs_provider(Config) -> Config1 = rebar_test_utils:init_rebar_state(Config), diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index 23b0178..8c2d36d 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -149,21 +149,21 @@ expand_deps(git, [{Name, Vsn, Deps} | Rest]) -> Dep = {Name, Vsn, {git, "https://example.org/user/"++Name++".git", {tag, Vsn}}}, [{Dep, expand_deps(git, Deps)} | expand_deps(git, Rest)]; expand_deps(pkg, [{Name, Deps} | Rest]) -> - Dep = {pkg, Name, "0.0.0"}, + Dep = {pkg, Name, "0.0.0", undefined}, [{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)]; expand_deps(pkg, [{Name, Vsn, Deps} | Rest]) -> - Dep = {pkg, Name, Vsn}, + Dep = {pkg, Name, Vsn, undefined}, [{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)]; expand_deps(mixed, [{Name, Deps} | Rest]) -> Dep = if hd(Name) >= $a, hd(Name) =< $z -> - {pkg, string:to_upper(Name), "0.0.0"} + {pkg, string:to_upper(Name), "0.0.0", undefined} ; hd(Name) >= $A, hd(Name) =< $Z -> {Name, ".*", {git, "https://example.org/user/"++Name++".git", "master"}} end, [{Dep, expand_deps(mixed, Deps)} | expand_deps(mixed, Rest)]; expand_deps(mixed, [{Name, Vsn, Deps} | Rest]) -> Dep = if hd(Name) >= $a, hd(Name) =< $z -> - {pkg, string:to_upper(Name), Vsn} + {pkg, string:to_upper(Name), Vsn, undefined} ; hd(Name) >= $A, hd(Name) =< $Z -> {Name, Vsn, {git, "https://example.org/user/"++Name++".git", {tag, Vsn}}} end, @@ -177,7 +177,7 @@ expand_deps(mixed, [{Name, Vsn, Deps} | Rest]) -> flat_deps(Deps) -> flat_deps(Deps, [], []). flat_deps([], Src, Pkg) -> {Src, Pkg}; -flat_deps([{{pkg, Name, Vsn}, PkgDeps} | Rest], Src, Pkg) -> +flat_deps([{{pkg, Name, Vsn, undefined}, PkgDeps} | Rest], Src, Pkg) -> Current = {{iolist_to_binary(Name), iolist_to_binary(Vsn)}, top_level_deps(PkgDeps)}, {[], FlatPkgDeps} = flat_deps(PkgDeps), @@ -195,7 +195,7 @@ vsn_from_ref({git, _, {_, Vsn}}) -> Vsn; vsn_from_ref({git, _, Vsn}) -> Vsn. top_level_deps([]) -> []; -top_level_deps([{{pkg, Name, Vsn}, _} | Deps]) -> +top_level_deps([{{pkg, Name, Vsn, undefined}, _} | Deps]) -> [{list_to_atom(Name), Vsn} | top_level_deps(Deps)]; top_level_deps([{{Name, Vsn, Ref}, _} | Deps]) -> [{list_to_atom(Name), Vsn, Ref} | top_level_deps(Deps)]. @@ -306,7 +306,7 @@ check_results(AppDir, Expected, ProfileRun) -> case lists:keyfind(iolist_to_binary(Name), 1, Locks) of false -> error({lock_not_found, Name}); - {_LockName, {pkg, _, LockVsn}, _} -> + {_LockName, {pkg, _, LockVsn, _}, _} -> ?assertEqual(iolist_to_binary(Vsn), iolist_to_binary(LockVsn)); {_LockName, {_, _, {ref, LockVsn}}, _} -> @@ -318,7 +318,7 @@ check_results(AppDir, Expected, ProfileRun) -> case lists:keyfind(iolist_to_binary(Name), 1, Locks) of false -> error({lock_not_found, Name}); - {_LockName, {pkg, _, LockVsn}, _} -> + {_LockName, {pkg, _, LockVsn, _}, _} -> ?assertEqual(iolist_to_binary(Vsn), iolist_to_binary(LockVsn)); {_LockName, {_, _, {ref, LockVsn}}, _} -> @@ -329,7 +329,7 @@ check_results(AppDir, Expected, ProfileRun) -> case lists:keyfind(iolist_to_binary(Name), 1, Locks) of false -> error({lock_not_found, Name}); - {_LockName, {pkg, _, LockVsn}, _} -> + {_LockName, {pkg, _, LockVsn, _}, _} -> error({pkg_lock, {Name, LockVsn}}); {_LockName, {_, _, {ref, LockVsn}}, _} -> ?assertEqual(iolist_to_binary(Vsn), -- cgit v1.1 From be79259e324e66ac2f948aed186474cb06a2ea85 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Tue, 24 May 2016 20:43:04 -0400 Subject: Test support for OTP-19 --- rebar.config | 1 + test/rebar_test_utils.erl | 23 +++++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/rebar.config b/rebar.config index ea5af27..7326b46 100644 --- a/rebar.config +++ b/rebar.config @@ -20,6 +20,7 @@ {"rebar/priv/templates/*", "_build/default/lib/"}]}. {erl_opts, [{platform_define, "^[0-9]+", namespaced_types}, + {platform_define, "^(19|2)", rand_only}, no_debug_info, warnings_as_errors]}. diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index 8c2d36d..7e62109 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -7,6 +7,13 @@ create_config/2, create_config/3, package_app/3]). -export([create_random_name/1, create_random_vsn/0, write_src_file/2]). +%% Pick the right random module +-ifdef(rand_only). +-define(random, rand). +-else. +-define(random, random). +-endif. + %%%%%%%%%%%%%% %%% Public %%% %%%%%%%%%%%%%% @@ -126,20 +133,24 @@ create_config(_AppDir, ConfFilename, Contents) -> %% @doc Util to create a random variation of a given name. create_random_name(Name) -> random_seed(), - Name ++ erlang:integer_to_list(random:uniform(1000000)). + Name ++ erlang:integer_to_list(?random:uniform(1000000)). %% @doc Util to create a random variation of a given version. create_random_vsn() -> random_seed(), - lists:flatten([erlang:integer_to_list(random:uniform(100)), - ".", erlang:integer_to_list(random:uniform(100)), - ".", erlang:integer_to_list(random:uniform(100))]). + lists:flatten([erlang:integer_to_list(?random:uniform(100)), + ".", erlang:integer_to_list(?random:uniform(100)), + ".", erlang:integer_to_list(?random:uniform(100))]). +-ifdef(rand_only). +random_seed() -> + %% the rand module self-seeds + ok. +-else. random_seed() -> <> = crypto:rand_bytes(12), random:seed({A,B,C}). - - +-endif. expand_deps(_, []) -> []; expand_deps(git, [{Name, Deps} | Rest]) -> -- cgit v1.1 From 9a7ede0196c78224c15d1e9c8d13bed84743dacf Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Tue, 24 May 2016 20:43:38 -0400 Subject: Fetch hashes from index prior to fetching --- src/rebar_app_utils.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl index 7028c32..f3b2962 100644 --- a/src/rebar_app_utils.erl +++ b/src/rebar_app_utils.erl @@ -182,7 +182,14 @@ update_source(AppInfo, {pkg, PkgName, PkgVsn, Hash}, State) -> _ -> {PkgName, PkgVsn} end, - AppInfo1 = rebar_app_info:source(AppInfo, {pkg, PkgName1, PkgVsn1, Hash}), + %% store the expected hash for the dependency + Hash1 = case Hash of + undefined -> % unknown, define the hash since we know the dep + rebar_packages:registry_checksum({pkg, PkgName1, PkgVsn1, Hash}, State); + _ -> % keep as is + Hash + end, + AppInfo1 = rebar_app_info:source(AppInfo, {pkg, PkgName1, PkgVsn1, Hash1}), Deps = rebar_packages:deps(PkgName1 ,PkgVsn1 ,State), -- cgit v1.1 From 351b757a0eb03e2d2e8714d0f5f447531446aa68 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Tue, 24 May 2016 21:12:49 -0400 Subject: Validate checksums expected vs obtained --- src/rebar_fetch.erl | 6 ++++++ src/rebar_pkg_resource.erl | 17 ++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/rebar_fetch.erl b/src/rebar_fetch.erl index b80c125..47bfe1d 100644 --- a/src/rebar_fetch.erl +++ b/src/rebar_fetch.erl @@ -67,6 +67,12 @@ needs_update(AppDir, Source, State) -> format_error({bad_download, CachePath}) -> io_lib:format("Download of package does not match md5sum from server: ~s", [CachePath]); +format_error({unexpected_hash, CachePath, Expected, Found}) -> + io_lib:format("The checksum for package at ~s (~s) does not match the " + "checksum previously locked (~s). Either unlock or " + "upgrade the package, or make sure you fetched it from " + "the same index from which it was initially fetched.", + [CachePath, Found, Expected]); format_error({failed_extract, CachePath}) -> io_lib:format("Failed to extract package: ~s", [CachePath]); format_error({bad_etag, Source}) -> diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl index 257df21..5817817 100644 --- a/src/rebar_pkg_resource.erl +++ b/src/rebar_pkg_resource.erl @@ -58,17 +58,20 @@ cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn, _Hash}, Url, ETag, State serve_from_cache(TmpDir, CachePath, Pkg, State) -> {Files, Contents, Version, Meta} = extract(TmpDir, CachePath), case checksums(Pkg, Files, Contents, Version, Meta, State) of - {Chk, Chk, Chk} -> + {Chk, Chk, Chk, Chk} -> ok = erl_tar:extract({binary, Contents}, [{cwd, TmpDir}, compressed]), {ok, true}; - {_Bin, Chk, Chk} -> + {_Hash, Chk, Chk, Chk} -> + ?DEBUG("Expected hash ~p does not match checksums ~p", [_Hash, Chk]), + {unexpected_hash, CachePath, _Hash, Chk}; + {Chk, _Bin, Chk, Chk} -> ?DEBUG("Checksums: registry: ~p, pkg: ~p", [Chk, _Bin]), {failed_extract, CachePath}; - {Chk, _Reg, Chk} -> + {Chk, Chk, _Reg, Chk} -> ?DEBUG("Checksums: registry: ~p, pkg: ~p", [_Reg, Chk]), {bad_registry_checksum, CachePath}; - {_Bin, _Reg, _Tar} -> - ?DEBUG("Checksums: registry: ~p, pkg: ~p, meta: ~p", [_Reg, _Bin, _Tar]), + {_Hash, _Bin, _Reg, _Tar} -> + ?DEBUG("Checksums: expected: ~p, registry: ~p, pkg: ~p, meta: ~p", [_Hash, _Reg, _Bin, _Tar]), {bad_checksum, CachePath} end. @@ -92,13 +95,13 @@ extract(TmpDir, CachePath) -> {"metadata.config", Meta} = lists:keyfind("metadata.config", 1, Files), {Files, Contents, Version, Meta}. -checksums(Pkg, Files, Contents, Version, Meta, State) -> +checksums(Pkg={pkg, _Name, _Vsn, Hash}, Files, Contents, Version, Meta, State) -> Blob = <>, <> = crypto:hash(sha256, Blob), BinChecksum = list_to_binary(string:to_upper(lists:flatten(io_lib:format("~64.16.0b", [X])))), RegistryChecksum = rebar_packages:registry_checksum(Pkg, State), {"CHECKSUM", TarChecksum} = lists:keyfind("CHECKSUM", 1, Files), - {BinChecksum, RegistryChecksum, TarChecksum}. + {Hash, BinChecksum, RegistryChecksum, TarChecksum}. make_vsn(_) -> {error, "Replacing version of type pkg not supported."}. -- cgit v1.1 From 24c43040c9280220efd01096605980f8b28b5f33 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Tue, 24 May 2016 21:17:59 -0400 Subject: Rebar3 with hash-locked deps --- rebar.lock | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/rebar.lock b/rebar.lock index 40463ac..38e22e5 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,10 +1,32 @@ -[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, - {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, - {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, - {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.2">>},0}, - {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.21.0">>},0}, - {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, - {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, - {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.19.0">>},0}, - {<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0}]. +{"1.1.0",[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, + {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, + {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, + {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.2">>},0}, + {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.21.0">>},0}, + {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, + {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, + {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.19.0">>},0}, + {<<"ssl_verify_hostname">>, + {pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>}, + 0}]}. +[{pkg_hash,[{<<"bbmustache">>, + <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>}, + {<<"certifi">>, + <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, + {<<"cf">>, + <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>}, + {<<"cth_readable">>, + <<"983913A8E8572310B7EAF5F2631148B7D70B3C090D2120DCFE777A93AA4165FB">>}, + {<<"erlware_commons">>, + <<"A04433071AD7D112EDEFC75AC77719DD3E6753E697AC09428FC83D7564B80B15">>}, + {<<"eunit_formatters">>, + <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, + {<<"getopt">>, + <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, + {<<"providers">>, + <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, + {<<"relx">>, + <<"286DD5244B4786F56AAC75D5C8E2D1FB4CFD306810D4EC8548F3AE1B3AADB8F7">>}, + {<<"ssl_verify_hostname">>, + <<"2E73E068CD6393526F9FA6D399353D7C9477D6886BA005F323B592D389FB47BE">>}]}]. -- cgit v1.1 From 00e06c1999e0e6e1dafdc4d7c835c89b473b65aa Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Tue, 24 May 2016 21:44:20 -0400 Subject: Make tests pass Tests have broken as locks were expanded and auto-filled newer versions of lockfiles. This fixes them back. --- test/rebar_pkg_SUITE.erl | 14 +++++++------- test/rebar_pkg_alias_SUITE.erl | 6 +++--- test/rebar_upgrade_SUITE.erl | 9 ++++++++- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/test/rebar_pkg_SUITE.erl b/test/rebar_pkg_SUITE.erl index 8003b10..7fdf55e 100644 --- a/test/rebar_pkg_SUITE.erl +++ b/test/rebar_pkg_SUITE.erl @@ -103,7 +103,7 @@ good_uncached(Config) -> {Pkg,Vsn} = ?config(pkg, Config), State = ?config(state, Config), ?assertEqual({ok, true}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)), Cache = ?config(cache_dir, Config), ?assert(filelib:is_regular(filename:join(Cache, <>))). @@ -116,7 +116,7 @@ good_cached(Config) -> ?assert(filelib:is_regular(CachedFile)), {ok, Content} = file:read_file(CachedFile), ?assertEqual({ok, true}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)), {ok, Content} = file:read_file(CachedFile). badindexchk(Config) -> @@ -124,7 +124,7 @@ badindexchk(Config) -> {Pkg,Vsn} = ?config(pkg, Config), State = ?config(state, Config), ?assertMatch({bad_registry_checksum, _Path}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)), %% The cached file is there for forensic purposes Cache = ?config(cache_dir, Config), ?assert(filelib:is_regular(filename:join(Cache, <>))). @@ -134,7 +134,7 @@ badpkg(Config) -> {Pkg,Vsn} = ?config(pkg, Config), State = ?config(state, Config), ?assertMatch({bad_download, _Path}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)), %% The cached file is there for forensic purposes Cache = ?config(cache_dir, Config), ?assert(filelib:is_regular(filename:join(Cache, <>))). @@ -148,7 +148,7 @@ bad_to_good(Config) -> ?assert(filelib:is_regular(CachedFile)), {ok, Contents} = file:read_file(CachedFile), ?assertEqual({ok, true}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)), %% Cache has refreshed ?assert({ok, Contents} =/= file:read_file(CachedFile)). @@ -161,7 +161,7 @@ good_disconnect(Config) -> ?assert(filelib:is_regular(CachedFile)), {ok, Content} = file:read_file(CachedFile), ?assertEqual({ok, true}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)), + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)), {ok, Content} = file:read_file(CachedFile). bad_disconnect(Config) -> @@ -169,7 +169,7 @@ bad_disconnect(Config) -> {Pkg,Vsn} = ?config(pkg, Config), State = ?config(state, Config), ?assertEqual({fetch_fail, Pkg, Vsn}, - rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, undefined}, State)). + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)). pkgs_provider(Config) -> Config1 = rebar_test_utils:init_rebar_state(Config), diff --git a/test/rebar_pkg_alias_SUITE.erl b/test/rebar_pkg_alias_SUITE.erl index fef2310..8915357 100644 --- a/test/rebar_pkg_alias_SUITE.erl +++ b/test/rebar_pkg_alias_SUITE.erl @@ -55,7 +55,7 @@ diff_alias(Config) -> Config, RebarConfig, ["lock"], {ok, [{lock, "fakelib"},{dep, "fakelib"}]} ), - {ok, [LockData]} = file:consult(Lockfile), + {ok, [{_Vsn, LockData}|_]} = file:consult(Lockfile), ?assert(lists:any(fun({<<"fakelib">>,{pkg,<<"goodpkg">>,_},_}) -> true ; (_) -> false end, LockData)), %% An second run yields the same @@ -63,13 +63,13 @@ diff_alias(Config) -> Config, RebarConfig, ["lock"], {ok, [{lock, "fakelib"},{dep, "fakelib"}]} ), - {ok, [LockData]} = file:consult(Lockfile), + {ok, [{_Vsn, LockData}|_]} = file:consult(Lockfile), %% So does an upgrade rebar_test_utils:run_and_check( Config, RebarConfig, ["upgrade"], {ok, [{lock, "fakelib"},{dep, "fakelib"}]} ), - {ok, [LockData]} = file:consult(Lockfile). + {ok, [{_Vsn, LockData}|_]} = file:consult(Lockfile). diff_alias_vsn(Config) -> diff_alias(Config). diff --git a/test/rebar_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl index cfe1d8a..22fb14d 100644 --- a/test/rebar_upgrade_SUITE.erl +++ b/test/rebar_upgrade_SUITE.erl @@ -610,7 +610,14 @@ novsn_pkg(Config) -> rewrite_locks({ok, Expectations}, Config) -> AppDir = ?config(apps, Config), LockFile = filename:join([AppDir, "rebar.lock"]), - {ok, [Locks]} = file:consult(LockFile), + Locks = case ?config(deps_type, Config) of + git -> + {ok, [LockData]} = file:consult(LockFile), + LockData; + pkg -> + {ok, [{_Vsn, LockData}|_]} = file:consult(LockFile), + LockData + end, ExpLocks = [{list_to_binary(Name), Vsn} || {lock, Name, Vsn} <- Expectations], NewLocks = lists:foldl( -- cgit v1.1 From c15c31f35c46e8184dd76bc8463eb6f4ffd65978 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Wed, 25 May 2016 10:58:50 -0400 Subject: Testing expected hash behaviour and errors in pkgs - also making sure unlocking works fine --- test/rebar_pkg_SUITE.erl | 38 +++++++++++++++++++++++++ test/rebar_test_utils.erl | 10 ++++--- test/rebar_unlock_SUITE.erl | 43 +++++++++++++++++++++++++++-- test/rebar_unlock_SUITE_data/pkg.rebar.lock | 32 +++++++++++++++++++++ 4 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 test/rebar_unlock_SUITE_data/pkg.rebar.lock diff --git a/test/rebar_pkg_SUITE.erl b/test/rebar_pkg_SUITE.erl index 7fdf55e..30cc0a8 100644 --- a/test/rebar_pkg_SUITE.erl +++ b/test/rebar_pkg_SUITE.erl @@ -11,6 +11,7 @@ -define(good_checksum, <<"1C6CE379D191FBAB41B7905075E0BF87CBBE23C77CECE775C5A0B786B2244C35">>). all() -> [good_uncached, good_cached, badindexchk, badpkg, + badhash_nocache, badhash_cache, bad_to_good, good_disconnect, bad_disconnect, pkgs_provider, find_highest_matching]. @@ -58,6 +59,19 @@ init_per_testcase(badpkg=Name, Config0) -> {pkg, {<<"badpkg">>, <<"1.0.0">>}} | Config0], mock_config(Name, Config); +init_per_testcase(badhash_nocache=Name, Config0) -> + Config = [{good_cache, false}, + {pkg, {<<"goodpkg">>, <<"1.0.0">>}} + | Config0], + mock_config(Name, Config); +init_per_testcase(badhash_cache=Name, Config0) -> + Pkg = {<<"goodpkg">>, <<"1.0.0">>}, + Config1 = [{good_cache, true}, + {pkg, Pkg} + | Config0], + Config = mock_config(Name, Config1), + copy_to_cache(Pkg, Config), + Config; init_per_testcase(bad_to_good=Name, Config0) -> Config1 = [{good_cache, false}, {pkg, {<<"goodpkg">>, <<"1.0.0">>}} @@ -139,6 +153,30 @@ badpkg(Config) -> Cache = ?config(cache_dir, Config), ?assert(filelib:is_regular(filename:join(Cache, <>))). +badhash_nocache(Config) -> + Tmp = ?config(tmp_dir, Config), + {Pkg,Vsn} = ?config(pkg, Config), + State = ?config(state, Config), + ?assertMatch({unexpected_hash, _Path, ?bad_checksum, ?good_checksum}, + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?bad_checksum}, State)), + %% The cached file is there for forensic purposes + Cache = ?config(cache_dir, Config), + ?assert(filelib:is_regular(filename:join(Cache, <>))). + +badhash_cache(Config) -> + Tmp = ?config(tmp_dir, Config), + {Pkg,Vsn} = ?config(pkg, Config), + Cache = ?config(cache_dir, Config), + State = ?config(state, Config), + CachedFile = filename:join(Cache, <>), + ?assert(filelib:is_regular(CachedFile)), + {ok, Content} = file:read_file(CachedFile), + ?assertMatch({unexpected_hash, _Path, ?bad_checksum, ?good_checksum}, + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?bad_checksum}, State)), + %% The cached file is there still, unchanged. + ?assert(filelib:is_regular(CachedFile)), + ?assertEqual({ok, Content}, file:read_file(CachedFile)). + bad_to_good(Config) -> Tmp = ?config(tmp_dir, Config), {Pkg,Vsn} = ?config(pkg, Config), diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index 7e62109..8c177c9 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -317,9 +317,10 @@ check_results(AppDir, Expected, ProfileRun) -> case lists:keyfind(iolist_to_binary(Name), 1, Locks) of false -> error({lock_not_found, Name}); - {_LockName, {pkg, _, LockVsn, _}, _} -> + {_LockName, {pkg, _, LockVsn, Hash}, _} -> ?assertEqual(iolist_to_binary(Vsn), - iolist_to_binary(LockVsn)); + iolist_to_binary(LockVsn)), + ?assertNotEqual(undefined, Hash); {_LockName, {_, _, {ref, LockVsn}}, _} -> ?assertEqual(iolist_to_binary(Vsn), iolist_to_binary(LockVsn)) @@ -329,9 +330,10 @@ check_results(AppDir, Expected, ProfileRun) -> case lists:keyfind(iolist_to_binary(Name), 1, Locks) of false -> error({lock_not_found, Name}); - {_LockName, {pkg, _, LockVsn, _}, _} -> + {_LockName, {pkg, _, LockVsn, Hash}, _} -> ?assertEqual(iolist_to_binary(Vsn), - iolist_to_binary(LockVsn)); + iolist_to_binary(LockVsn)), + ?assertNotEqual(undefined, Hash); {_LockName, {_, _, {ref, LockVsn}}, _} -> error({source_lock, {Name, LockVsn}}) end diff --git a/test/rebar_unlock_SUITE.erl b/test/rebar_unlock_SUITE.erl index 31dca72..8dbdb3a 100644 --- a/test/rebar_unlock_SUITE.erl +++ b/test/rebar_unlock_SUITE.erl @@ -3,8 +3,14 @@ -include_lib("eunit/include/eunit.hrl"). -compile(export_all). -all() -> [unlock, unlock_all]. +all() -> [pkgunlock, unlock, unlock_all]. +init_per_testcase(pkgunlock, Config0) -> + Config = rebar_test_utils:init_rebar_state(Config0, "pkgunlock"), + Lockfile = filename:join(?config(apps, Config), "rebar.lock"), + ec_file:copy(filename:join(?config(data_dir, Config), "pkg.rebar.lock"), + Lockfile), + [{lockfile, Lockfile} | Config]; init_per_testcase(Case, Config0) -> Config = rebar_test_utils:init_rebar_state(Config0, atom_to_list(Case)), Lockfile = filename:join(?config(apps, Config), "rebar.lock"), @@ -15,6 +21,23 @@ init_per_testcase(Case, Config0) -> end_per_testcase(_, Config) -> Config. +pkgunlock(Config) -> + Locks = read_locks(Config), + Hashes = read_hashes(Config), + rebar_test_utils:run_and_check(Config, [], ["unlock", "fakeapp"], {ok, []}), + Locks = read_locks(Config), + Hashes = read_hashes(Config), + rebar_test_utils:run_and_check(Config, [], ["unlock", "bbmustache"], {ok, []}), + ?assertEqual(Locks -- ["bbmustache"], read_locks(Config)), + ?assertEqual(Hashes -- ["bbmustache"], read_hashes(Config)), + rebar_test_utils:run_and_check(Config, [], ["unlock", "cf,certifi"], {ok, []}), + ?assertEqual(Locks -- ["bbmustache","cf","certifi"], read_locks(Config)), + ?assertEqual(Hashes -- ["bbmustache","cf","certifi"], read_hashes(Config)), + rebar_test_utils:run_and_check(Config, [], ["unlock", string:join(Locks,",")], {ok, []}), + ?assertEqual({error, enoent}, read_locks(Config)), + ?assertEqual({error, enoent}, read_hashes(Config)), + ok. + unlock(Config) -> Locks = read_locks(Config), rebar_test_utils:run_and_check(Config, [], ["unlock", "fakeapp"], {ok, []}), @@ -35,6 +58,20 @@ unlock_all(Config) -> read_locks(Config) -> case file:consult(?config(lockfile, Config)) of - {ok, [Locks]} -> [binary_to_list(element(1,Lock)) || Lock <- Locks]; - Other -> Other + {ok, _} -> + Locks = rebar_config:consult_lock_file(?config(lockfile, Config)), + [binary_to_list(element(1,Lock)) || Lock <- Locks]; + Other -> + Other + end. + +read_hashes(Config) -> + case file:consult(?config(lockfile, Config)) of + {ok, [{_Vsn, _Locks},Props|_]} -> + Hashes = proplists:get_value(pkg_hash, Props, []), + [binary_to_list(element(1,Hash)) || Hash <- Hashes]; + {ok, [{_Vsn, _Locks}]} -> + []; + Other -> + Other end. diff --git a/test/rebar_unlock_SUITE_data/pkg.rebar.lock b/test/rebar_unlock_SUITE_data/pkg.rebar.lock new file mode 100644 index 0000000..38e22e5 --- /dev/null +++ b/test/rebar_unlock_SUITE_data/pkg.rebar.lock @@ -0,0 +1,32 @@ +{"1.1.0",[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, + {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, + {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, + {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.2">>},0}, + {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.21.0">>},0}, + {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, + {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, + {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.19.0">>},0}, + {<<"ssl_verify_hostname">>, + {pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>}, + 0}]}. +[{pkg_hash,[{<<"bbmustache">>, + <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>}, + {<<"certifi">>, + <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, + {<<"cf">>, + <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>}, + {<<"cth_readable">>, + <<"983913A8E8572310B7EAF5F2631148B7D70B3C090D2120DCFE777A93AA4165FB">>}, + {<<"erlware_commons">>, + <<"A04433071AD7D112EDEFC75AC77719DD3E6753E697AC09428FC83D7564B80B15">>}, + {<<"eunit_formatters">>, + <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, + {<<"getopt">>, + <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, + {<<"providers">>, + <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, + {<<"relx">>, + <<"286DD5244B4786F56AAC75D5C8E2D1FB4CFD306810D4EC8548F3AE1B3AADB8F7">>}, + {<<"ssl_verify_hostname">>, + <<"2E73E068CD6393526F9FA6D399353D7C9477D6886BA005F323B592D389FB47BE">>}]}]. -- cgit v1.1 From 760ffdc79d19ffff732915b2abaa81ecdbeeef91 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Wed, 25 May 2016 13:25:34 -0400 Subject: Hide the expected hash on fetch output --- src/rebar_prv_install_deps.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index 5e6aa4c..a8a7ea0 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -352,10 +352,14 @@ make_relative_to_root(State, Path) when is_list(Path) -> rebar_dir:make_relative_path(Path, Root). fetch_app(AppInfo, AppDir, State) -> - ?INFO("Fetching ~s (~p)", [rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)]), + ?INFO("Fetching ~s (~p)", [rebar_app_info:name(AppInfo), + format_source(rebar_app_info:source(AppInfo))]), Source = rebar_app_info:source(AppInfo), true = rebar_fetch:download_source(AppDir, Source, State). +format_source({pkg, Name, Vsn, _Hash}) -> {pkg, Name, Vsn}; +format_source(Source) -> Source. + %% This is called after the dep has been downloaded and unpacked, if it hadn't been already. %% So this is the first time for newly downloaded apps that its .app/.app.src data can %% be read in an parsed. -- cgit v1.1 From 589eaf13e1de55695cea4f34719c22c4b6467734 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Thu, 9 Jun 2016 21:30:42 -0400 Subject: Hashes in lockfile are diff friendly This reworks the version and hash printing in the lockfile to minimize diff changes: - the version is on its own line so that the locks are mostly the same aside from the last line - the hashes are each printed on one line with the package name for simpler diffing too. --- rebar.lock | 56 ++++++++++++++++++++++------------------------------ src/rebar_config.erl | 23 +++++++++++++++++++-- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/rebar.lock b/rebar.lock index 38e22e5..4f133d6 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,32 +1,24 @@ -{"1.1.0",[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, - {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, - {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, - {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.2">>},0}, - {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.21.0">>},0}, - {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, - {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, - {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.19.0">>},0}, - {<<"ssl_verify_hostname">>, - {pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>}, - 0}]}. -[{pkg_hash,[{<<"bbmustache">>, - <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>}, - {<<"certifi">>, - <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, - {<<"cf">>, - <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>}, - {<<"cth_readable">>, - <<"983913A8E8572310B7EAF5F2631148B7D70B3C090D2120DCFE777A93AA4165FB">>}, - {<<"erlware_commons">>, - <<"A04433071AD7D112EDEFC75AC77719DD3E6753E697AC09428FC83D7564B80B15">>}, - {<<"eunit_formatters">>, - <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, - {<<"getopt">>, - <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, - {<<"providers">>, - <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, - {<<"relx">>, - <<"286DD5244B4786F56AAC75D5C8E2D1FB4CFD306810D4EC8548F3AE1B3AADB8F7">>}, - {<<"ssl_verify_hostname">>, - <<"2E73E068CD6393526F9FA6D399353D7C9477D6886BA005F323B592D389FB47BE">>}]}]. +{"1.1.0", +[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, + {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, + {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, + {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.2">>},0}, + {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.21.0">>},0}, + {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, + {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, + {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.19.0">>},0}, + {<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0}]}. +[ +{pkg_hash,[ + {<<"bbmustache">>, <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>}, + {<<"certifi">>, <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, + {<<"cf">>, <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>}, + {<<"cth_readable">>, <<"983913A8E8572310B7EAF5F2631148B7D70B3C090D2120DCFE777A93AA4165FB">>}, + {<<"erlware_commons">>, <<"A04433071AD7D112EDEFC75AC77719DD3E6753E697AC09428FC83D7564B80B15">>}, + {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, + {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, + {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, + {<<"relx">>, <<"286DD5244B4786F56AAC75D5C8E2D1FB4CFD306810D4EC8548F3AE1B3AADB8F7">>}, + {<<"ssl_verify_hostname">>, <<"2E73E068CD6393526F9FA6D399353D7C9477D6886BA005F323B592D389FB47BE">>}]} +]. diff --git a/src/rebar_config.erl b/src/rebar_config.erl index 031df8b..828c45d 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -82,10 +82,29 @@ write_lock_file(LockFile, Locks) -> file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks])); _ -> file:write_file(LockFile, - io_lib:format("{~p,~p}.~n~p.~n", - [?CONFIG_VERSION, NewLocks, Attrs])) + io_lib:format("{~p,~n~p}.~n[~n~s~n].~n", + [?CONFIG_VERSION, NewLocks, + format_attrs(Attrs)])) end. +%% Attributes have a special formatting to ensure there's only one per +%% line in terms of pkg_hash, so we disturb source diffing as little +%% as possible. +format_attrs([]) -> []; +format_attrs([{pkg_hash, Vals}|T]) -> + [io_lib:format("{pkg_hash,[~n",[]), format_hashes(Vals), "]}", + maybe_comma(T) | format_attrs(T)]; +format_attrs([H|T]) -> + [io_lib:format("~p~s", [H, maybe_comma(T)]) | format_attrs(T)]. + +format_hashes([]) -> []; +format_hashes([{Pkg,Hash}|T]) -> + [" {", io_lib:format("~p",[Pkg]), ", ", io_lib:format("~p", [Hash]), "}", + maybe_comma(T) | format_hashes(T)]. + +maybe_comma([]) -> ""; +maybe_comma([_|_]) -> io_lib:format(",~n", []). + read_attrs(_Vsn, Locks, Attrs) -> %% Beta copy does not know how to expand attributes, but %% is ready to support it. -- cgit v1.1 From 71df9bf1411c04e2f7dae7e9f0352180664b9365 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 10 Jun 2016 08:43:13 -0400 Subject: Only display old version warning once This uses the env variable as a global store for variables. It's not the cleanest thing, but it sounded nicer than pdicts. --- src/rebar_config.erl | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/rebar_config.erl b/src/rebar_config.erl index 828c45d..9e13d46 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -58,6 +58,9 @@ consult_lock_file(File) -> [Locks] when is_list(Locks) -> % beta lock file read_attrs(beta, Locks, []); [{Vsn, Locks}|Attrs] when is_list(Locks) -> % versioned lock file + %% Because this is the first version of rebar3 to introduce a lock + %% file, all versionned lock files with a different versions have + %% to be newer. case Vsn of ?CONFIG_VERSION -> ok; @@ -65,14 +68,24 @@ consult_lock_file(File) -> %% Make sure the warning below is to be shown whenever a version %% newer than the current one is being used, as we can't parse %% all the contents of the lock file properly. - ?WARN("Rebar3 detected a lock file from a newer version. " - "It will be loaded in compatibility mode, but important " - "information may be missing or lost. It is recommended to " - "upgrade Rebar3.", []) + warn_vsn_once() end, read_attrs(Vsn, Locks, Attrs) end. +warn_vsn_once() -> + Warn = application:get_env(rebar, warn_config_vsn) =/= {ok, false}, + application:set_env(rebar, warn_config_vsn, false), + case Warn of + false -> ok; + true -> + ?WARN("Rebar3 detected a lock file from a newer version. " + "It will be loaded in compatibility mode, but important " + "information may be missing or lost. It is recommended to " + "upgrade Rebar3.", []) + end. + + write_lock_file(LockFile, Locks) -> {NewLocks, Attrs} = write_attrs(Locks), %% Write locks in the beta format, at least until it's been long -- cgit v1.1 From 391f3b3bec6283866fec8b16e128488de5205640 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 18 Jun 2016 03:35:53 +0200 Subject: Let DEBUG="" and QUIET="" mean disable the option Setting DEBUG/QUIET environment variable to the empty string now acts the same as unsetting it. Unsetting is not always easy/possible. --- src/rebar3.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rebar3.erl b/src/rebar3.erl index 82b4472..d3ea15f 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -221,10 +221,10 @@ set_options(State, {Options, NonOptArgs}) -> %% log_level() -> case os:getenv("QUIET") of - false -> + Q when Q == false; Q == "" -> DefaultLevel = rebar_log:default_level(), case os:getenv("DEBUG") of - false -> + D when D == false; D == "" -> DefaultLevel; _ -> DefaultLevel + 3 -- cgit v1.1 From c7b7b9005f733ec867f99932b3fd50803a56cd99 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 18 Jun 2016 06:04:42 +0200 Subject: Fix unicode rendering of deps tree (issue #1140) --- src/rebar_prv_deps_tree.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar_prv_deps_tree.erl b/src/rebar_prv_deps_tree.erl index cefa60a..c0c8bab 100644 --- a/src/rebar_prv_deps_tree.erl +++ b/src/rebar_prv_deps_tree.erl @@ -70,7 +70,7 @@ print_children(Prefix, [{Name, Vsn, Source} | Rest], Dict, Verbose) -> [Prefix, " "]; _ -> io:format("~ts~ts", [Prefix, <<226,148,156,226,148,128,32>>]), %Binary for ├─ utf8% - [Prefix, "│ "] + [Prefix, <<226,148,130,32,32>>] %Binary for │ utf8% end, io:format("~ts~ts~ts (~ts)~n", [Name, <<226,148,128>>, Vsn, type(Source, Verbose)]), %Binary for ─ utf8% case dict:find(Name, Dict) of -- cgit v1.1 From 2ffbb80cea3baf6763069319f089c078764edb59 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 18 Jun 2016 16:59:13 -0400 Subject: Bump to 3.2.0 --- bootstrap | 2 +- src/rebar.app.src | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap b/bootstrap index 323b0a5..90775c8 100755 --- a/bootstrap +++ b/bootstrap @@ -24,7 +24,7 @@ main(_) -> bootstrap_rebar3(), %% Build rebar.app from rebar.app.src - {ok, App} = rebar_app_info:new(rebar, "3.1.1", filename:absname("_build/default/lib/rebar/")), + {ok, App} = rebar_app_info:new(rebar, "3.2.0", filename:absname("_build/default/lib/rebar/")), rebar_otp_app:compile(rebar_state:new(), App), %% Because we are compiling files that are loaded already we want to silence diff --git a/src/rebar.app.src b/src/rebar.app.src index bd0f871..020d152 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "git"}, + {vsn, "3.2.0"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From 7b56c41090d18e2fd97fc4e74bd986347ad845c7 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 18 Jun 2016 17:02:18 -0400 Subject: go back to semver post-release --- src/rebar.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar.app.src b/src/rebar.app.src index 020d152..bd0f871 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "3.2.0"}, + {vsn, "git"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From f1ae529d58f2e52052815edf1a9be26e9d6198c2 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 18 Jun 2016 19:37:26 -0400 Subject: use logs rather than skimming github pages --- pr2relnotes.py | 188 --------------------------------------------------------- pr2relnotes.sh | 25 ++++++++ 2 files changed, 25 insertions(+), 188 deletions(-) delete mode 100755 pr2relnotes.py create mode 100755 pr2relnotes.sh diff --git a/pr2relnotes.py b/pr2relnotes.py deleted file mode 100755 index 7a23fda..0000000 --- a/pr2relnotes.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env python - -## Install info -## $ virtualenv env -## $ source env/bin/activate -## $ pip install PyGithub -## -## Examples: -## Find the differences from last tag to current -## $ pr2relnotes.py alpha-6 HEAD - -import argparse -import re -import os -import subprocess -from github import Github -from github import GithubException - - -def dprint(*args): - if VERBOSE: - print str(args) - -def get_args(): - """ - Get command line arguments - """ - parser = argparse.ArgumentParser(description="Find the PR's between two versions") - parser.add_argument("old", help = "old version to use") - parser.add_argument("new", help = "new version to use") - parser.add_argument("-v", "--verbose", help="Enable debug output", - default=False, - action="store_true") - parser.add_argument("-f", "--file", - help="Output file to store results (default: tagdiff.md)", - default="tagdiff.md") - return parser.parse_args() - -def search_prs(log): - """ - Search lines of text for PR numbers - """ - # Find all matches using regex iterator, using the PR # as the group match - resultlist = [str(m.group(1)) for m in re.finditer(r"erge pull request #(\d+)", log)] - return sorted(resultlist) - -def get_env(env): - return os.environ[env] - -def get_formatted_issue(repo, issue, title, url): - """ - Single place to adjust formatting output of PR data - """ - # Newline support writelines() call which doesn't add newlines - # on its own - return("* {}/{}: [{}]({})\n".format(repo, issue, title.encode('utf-8'), url)) - -def gh_get_issue_output(org, repo, issuenum): - """ - Look up PR information using the GitHub api - """ - # Attempt to look up the PR, and don't take down the whole - # shebang if a API call fails - # This will fail often on forks who don't have the - # PRs numbers associated with the forked account - # Return empty string on error - try: - repoObj = gh.get_repo(org + "/" + repo) - issue = repoObj.get_issue(int(issuenum)) - title = issue.title - html_url = issue.html_url - except GithubException as e: - print "Github error({0}): {1}".format(e.status, e.data) - return "" - except: - print "Some github error" - return "" - - return(get_formatted_issue(repo, issuenum, title, html_url)) - - -def get_org(repourl): - """ - Simple function to parse the organization out of a GitHub URL - """ - dprint("Current repourl to search: " + repourl) - # GitHub URLs can be: - # http[s]://www.github.com/org/repo - # or git@github.com:/org/repo - pattern = re.compile(r"github.com[/:]+(\w+)/") - m = re.search(pattern, repourl) - # Fail fast if this is wrong so we can add a pattern to the search - if m: - return m.group(1) - else: - raise Exception("Incorrect regex pattern finding repo org") - -def get_name(repourl): - """ - Simple function to parse the repository name out of a GitHub URL - """ - dprint("Current repourl to search: " + repourl) - repo_pattern = re.compile(r"github.com[/:]\w+/(\w+)") - m = re.search(repo_pattern, repourl) - if m: - return m.group(1) - else: - raise Exception("Incorrect rexex pattern finding repo url") - -def get_repo_url_from_remote(): - """ - Function that gets the repository URL from the `git remote` listing - """ - git_remote_bytes = subprocess.check_output(["git", "remote", "-v"]) - # check_output returns the command results in raw byte format - remote_string = git_remote_bytes.decode('utf-8') - - pattern = re.compile(r"github.com[/:]\w+/\w+") - m = re.search(pattern, remote_string) - if m: - return m.group(0) - else: - raise Exception("Incorrect rexex pattern finding repo url") - -def process_log(gitlog, repo_url): - """ - Handles the processing of the gitlog and returns a list - of PRs already formatted for output - """ - pr_list = search_prs(gitlog) - repoorg = get_org(repo_url) - reponame = get_name(repo_url) - pr_buffer = [] - for issue in pr_list: - pr_buffer.append(gh_get_issue_output(repoorg, reponame, issue)) - - return pr_buffer - -def fetch_log(old_ver, new_ver): - """ - Function that processes the git log between the old and new versions - """ - dprint("Current working directory", os.getcwd()) - gitlogbytes = subprocess.check_output(["git", "log", - str(old_ver + ".." + new_ver)]) - return gitlogbytes.decode('utf-8') - - -def compare_versions(repo_url, old_ver, new_ver): - # Formatted list of all PRs for all repos - pr_out = [] - gitlog = fetch_log(old_ver, new_ver) - pr_out.extend(process_log(gitlog, repo_url)) - return pr_out - -def main(): - args = get_args() - - # Setup the GitHub object for later use - global gh - gh = Github(get_env("GHAUTH")) - - if gh == "": - raise Exception("Env var GHAUTH must be set to a valid GitHub API key") - - if args.verbose: - global VERBOSE - VERBOSE=True - - dprint("Inspecting difference in between: ", args.old, " and ", args.new) - - # Find the github URL of the repo we are operating on - repo_url = get_repo_url_from_remote() - - # Compare old and new versions - pr_list = compare_versions(repo_url, args.old, args.new) - - # Writeout PR listing - print "Writing output to file %s" % args.file - with open(args.file, 'w') as output: - output.writelines(pr_list) - - -if __name__ == "__main__": - VERBOSE=False - gh=None - topdir=os.getcwd() - main() diff --git a/pr2relnotes.sh b/pr2relnotes.sh new file mode 100755 index 0000000..28712bb --- /dev/null +++ b/pr2relnotes.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env sh + +if [ -z $1 ] +then + echo "usage: $0 [pull-request-url]" + exit 0 +fi +export url=${2:-"https://github.com/erlang/rebar3/pull/"} + +git log --merges --pretty=medium $1..HEAD | \ +awk -v url=$url ' + # first line of a merge commit entry + /^commit / {mode="new"} + + # merge commit default message + / +Merge pull request/ { + page_id=substr($4, 2, length($4)-1); + mode="started"; + next; + } + + # line of content including title + mode=="started" && / [^ ]+/ { + print "- [" substr($0, 5) "](" url page_id ")"; mode="done" + }' -- cgit v1.1 From 6bc8ccefd344ba1620b183c5c6810b6cd727d841 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sun, 26 Jun 2016 20:26:51 -0400 Subject: Escriptize based on configured apps only Prior to this patch, the escriptize command flat out selected all declared dependencies. This patch instead looks at the app files and only includes the dependencies of the top level app and the extra ones, avoiding to package more apps than required. This required a version bump on cth_readable as it mistakenly included 'syntax_lib' instead of 'syntax_tools' as a dependency. --- rebar.config | 2 +- rebar.lock | 4 ++-- src/rebar_prv_escriptize.erl | 24 +++++++++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/rebar.config b/rebar.config index 7326b46..0489572 100644 --- a/rebar.config +++ b/rebar.config @@ -9,7 +9,7 @@ {bbmustache, "1.0.4"}, {relx, "3.19.0"}, {cf, "0.2.1"}, - {cth_readable, "1.2.2"}, + {cth_readable, "1.2.3"}, {eunit_formatters, "0.3.1"}]}. {escript_name, rebar3}. diff --git a/rebar.lock b/rebar.lock index 4f133d6..66241a9 100644 --- a/rebar.lock +++ b/rebar.lock @@ -2,7 +2,7 @@ [{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, - {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.2">>},0}, + {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.3">>},0}, {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.21.0">>},0}, {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, @@ -14,7 +14,7 @@ {<<"bbmustache">>, <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>}, {<<"certifi">>, <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, {<<"cf">>, <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>}, - {<<"cth_readable">>, <<"983913A8E8572310B7EAF5F2631148B7D70B3C090D2120DCFE777A93AA4165FB">>}, + {<<"cth_readable">>, <<"293120673DFF82F0768612C5282E35C40CACC1B6F94FE99077438FD3749D0E27">>}, {<<"erlware_commons">>, <<"A04433071AD7D112EDEFC75AC77719DD3E6753E697AC09428FC83D7564B80B15">>}, {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, diff --git a/src/rebar_prv_escriptize.erl b/src/rebar_prv_escriptize.erl index 32f5508..d8704f6 100644 --- a/src/rebar_prv_escriptize.erl +++ b/src/rebar_prv_escriptize.erl @@ -90,9 +90,9 @@ escriptize(State0, App) -> %% Look for a list of other applications (dependencies) to include %% in the output file. We then use the .app files for each of these %% to pull in all the .beam files. - InclApps = lists:usort([ec_cnv:to_atom(AppName) | rebar_state:get(State, escript_incl_apps, []) - ++ all_deps(State)]), + TopInclApps = lists:usort([ec_cnv:to_atom(AppName) | rebar_state:get(State, escript_incl_apps, [])]), AllApps = rebar_state:all_deps(State)++rebar_state:project_apps(State), + InclApps = find_deps(TopInclApps, AllApps), InclBeams = get_apps_beams(InclApps, AllApps), %% Look for a list of extra files to include in the output file. @@ -219,9 +219,23 @@ usort(List) -> get_nonempty(Files) -> [{FName,FBin} || {FName,FBin} <- Files, FBin =/= <<>>]. -all_deps(State) -> - [list_to_existing_atom(binary_to_list(rebar_app_info:name(App))) - || App <- rebar_state:all_deps(State)]. +find_deps(AppNames, AllApps) -> + BinAppNames = [ec_cnv:to_binary(Name) || Name <- AppNames], + [ec_cnv:to_atom(Name) || + Name <- find_deps_of_deps(BinAppNames, AllApps, BinAppNames)]. + +%% Should look at the app files to find direct dependencies +find_deps_of_deps([], _, Acc) -> Acc; +find_deps_of_deps([Name|Names], Apps, Acc) -> + ?DEBUG("processing ~p", [Name]), + {ok, App} = rebar_app_utils:find(Name, Apps), + DepNames = proplists:get_value(applications, rebar_app_info:app_details(App), []), + BinDepNames = [ec_cnv:to_binary(Dep) || Dep <- DepNames, + %% ignore system libs; shouldn't include them. + not lists:prefix(code:root_dir(), code:lib_dir(Dep))] + -- ([Name|Names]++Acc), % avoid already seen deps + ?DEBUG("new deps of ~p found to be ~p", [Name, BinDepNames]), + find_deps_of_deps(BinDepNames ++ Names, Apps, BinDepNames ++ Acc). def(Rm, State, Key, Default) -> Value0 = rebar_state:get(State, Key, Default), -- cgit v1.1 From c3f336763f3f65babcdfe8c1faedb5ca9eff2a8c Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sun, 26 Jun 2016 21:03:03 -0400 Subject: Fix invalid template value Used a nonexisting option instead of a correct one --- priv/templates/escript_rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/templates/escript_rebar.config b/priv/templates/escript_rebar.config index 196f835..ef498a8 100644 --- a/priv/templates/escript_rebar.config +++ b/priv/templates/escript_rebar.config @@ -3,7 +3,7 @@ {escript_incl_apps, [{{name}}]}. -{escript_top_level_app, {{name}}}. +{escript_main_app, {{name}}}. {escript_name, {{name}}}. {escript_emu_args, "%%! +sbtu +A0\n"}. -- cgit v1.1 From c86201b85b7f426f8eaca31d95ee39a71fab9faf Mon Sep 17 00:00:00 2001 From: Steve Strong Date: Tue, 28 Jun 2016 13:58:10 +0100 Subject: Fixes for windows environment: 1) copy empty directories when cloning (since git occasionally has a refs directory with no files in it - if the directory is not present, then git does not believe it is a git repo) and 2) change order of git rev-parse arguments to match git docs --- src/rebar_file_utils.erl | 4 ++-- src/rebar_git_resource.erl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index 667be62..104c047 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -177,11 +177,11 @@ mv(Source, Dest) -> {win32, _} -> Cmd = case filelib:is_dir(Source) of true -> - ?FMT("robocopy /move /s \"~s\" \"~s\" 1> nul", + ?FMT("robocopy /move /e \"~s\" \"~s\" 1> nul", [rebar_utils:escape_double_quotes(filename:nativename(Source)), rebar_utils:escape_double_quotes(filename:nativename(Dest))]); false -> - ?FMT("robocopy /move /s \"~s\" \"~s\" \"~s\" 1> nul", + ?FMT("robocopy /move /e \"~s\" \"~s\" \"~s\" 1> nul", [rebar_utils:escape_double_quotes(filename:nativename(filename:dirname(Source))), rebar_utils:escape_double_quotes(filename:nativename(Dest)), rebar_utils:escape_double_quotes(filename:basename(Source))]) diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl index f0f5f03..acb9ec0 100644 --- a/src/rebar_git_resource.erl +++ b/src/rebar_git_resource.erl @@ -22,7 +22,7 @@ lock(AppDir, {git, Url}) -> {ok, VsnString} = case os:type() of {win32, _} -> - rebar_utils:sh("git rev-parse --git-dir=\"" ++ Dir ++ "/.git\" --work-tree=\"" ++ Dir ++ "\" --verify HEAD", + rebar_utils:sh("git --git-dir=\"" ++ Dir ++ "/.git\" --work-tree=\"" ++ Dir ++ "\" rev-parse --verify HEAD", [{use_stdout, false}, {debug_abort_on_error, AbortMsg}]); _ -> rebar_utils:sh("git --git-dir=\"" ++ Dir ++ "/.git\" rev-parse --verify HEAD", -- cgit v1.1 From e334f211e373c9f019c68d5d35cc8b6aa0a1723a Mon Sep 17 00:00:00 2001 From: James Fish Date: Mon, 11 Jul 2016 14:28:38 +0100 Subject: Handle empty PLTs --- src/rebar_prv_dialyzer.erl | 13 ++++++++- test/rebar_dialyzer_SUITE.erl | 65 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index 622ee60..f7e3f21 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -353,8 +353,19 @@ update_base_plt(State, BasePlt, Output, BaseFiles) -> build_plt(State, BasePlt, Output, BaseFiles) end. +build_plt(State, Plt, _, []) -> + ?INFO("Building with no files in ~p...", [Plt]), + Opts = [{get_warnings, false}, + {output_plt, Plt}, + {apps, [erts]}], + % Create a PLT with erts files and then remove erts files to be left with an + % empty PLT. Dialyzer will crash when trying to build a PLT with an empty + % file list. + _ = dialyzer:run([{analysis_type, plt_build} | Opts]), + _ = dialyzer:run([{analysis_type, plt_remove}, {init_plt, Plt} | Opts]), + {0, State}; build_plt(State, Plt, Output, Files) -> - ?INFO("Adding ~b files to ~p...", [length(Files), Plt]), + ?INFO("Building with ~b files in ~p...", [length(Files), Plt]), GetWarnings = get_config(State, get_warnings, false), Opts = [{analysis_type, plt_build}, {get_warnings, GetWarnings}, diff --git a/test/rebar_dialyzer_SUITE.erl b/test/rebar_dialyzer_SUITE.erl index 22a4894..ededf6a 100644 --- a/test/rebar_dialyzer_SUITE.erl +++ b/test/rebar_dialyzer_SUITE.erl @@ -3,8 +3,13 @@ -export([suite/0, init_per_suite/1, end_per_suite/1, + init_per_group/2, + end_per_group/2, init_per_testcase/2, all/0, + groups/0, + empty_base_plt/1, + empty_app_plt/1, update_base_plt/1, update_app_plt/1, build_release_plt/1, @@ -23,6 +28,14 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. +init_per_group(empty, Config) -> + [{base_plt_apps, []} | Config]; +init_per_group(_Group, Config) -> + [{base_plt_apps, [erts]} | Config]. + +end_per_group(_Group, _Config) -> + ok. + init_per_testcase(Testcase, Config) -> PrivDir = ?config(priv_dir, Config), Prefix = ec_cnv:to_list(Testcase), @@ -31,7 +44,7 @@ init_per_testcase(Testcase, Config) -> {plt_location, PrivDir}, {base_plt_prefix, BasePrefix}, {base_plt_location, PrivDir}, - {base_plt_apps, [erts]}], + {base_plt_apps, ?config(base_plt_apps, Config)}], Suffix = "_" ++ rebar_utils:otp_release() ++ "_plt", [{plt, filename:join(PrivDir, Prefix ++ Suffix)}, {base_plt, filename:join(PrivDir, BasePrefix ++ Suffix)}, @@ -39,7 +52,55 @@ init_per_testcase(Testcase, Config) -> rebar_test_utils:init_rebar_state(Config)]. all() -> - [update_base_plt, update_app_plt, build_release_plt, plt_apps_option]. + [{group, empty}, {group, build_and_check}, {group, update}]. + +groups() -> + [{empty, [empty_base_plt, empty_app_plt]}, + {build_and_check, [build_release_plt, plt_apps_option]}, + {update, [update_base_plt, update_app_plt]}]. + +empty_base_plt(Config) -> + AppDir = ?config(apps, Config), + RebarConfig = ?config(rebar_config, Config), + BasePlt = ?config(base_plt, Config), + Plt = ?config(plt, Config), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [erts]), + + rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"], + {ok, [{app, Name}]}), + + {ok, BasePltFiles} = plt_files(BasePlt), + ?assertEqual([], BasePltFiles), + + ErtsFiles = erts_files(), + {ok, PltFiles} = plt_files(Plt), + ?assertEqual(ErtsFiles, PltFiles), + + ok. + +empty_app_plt(Config) -> + AppDir = ?config(apps, Config), + RebarConfig = ?config(rebar_config, Config), + BasePlt = ?config(base_plt, Config), + Plt = ?config(plt, Config), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, []), + + rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"], + {ok, [{app, Name}]}), + + {ok, BasePltFiles} = plt_files(BasePlt), + ?assertEqual([], BasePltFiles), + + {ok, PltFiles} = plt_files(Plt), + ?assertEqual([], PltFiles), + + ok. update_base_plt(Config) -> AppDir = ?config(apps, Config), -- cgit v1.1 From 0fa2b501f05e700a35c272f98f1ab976a5bd6170 Mon Sep 17 00:00:00 2001 From: James Fish Date: Mon, 11 Jul 2016 14:41:19 +0100 Subject: Don't error when analyzing empty app --- src/rebar_prv_dialyzer.erl | 11 +++++++---- test/rebar_dialyzer_SUITE.erl | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index f7e3f21..ec9e81a 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -380,12 +380,15 @@ succ_typings(State, Plt, Output) -> {0, State}; _ -> Apps = rebar_state:project_apps(State), - succ_typings(State, Plt, Output, Apps) + ?INFO("Doing success typing analysis...", []), + Files = apps_to_files(Apps), + succ_typings(State, Plt, Output, Files) end. -succ_typings(State, Plt, Output, Apps) -> - ?INFO("Doing success typing analysis...", []), - Files = apps_to_files(Apps), +succ_typings(State, Plt, _, []) -> + ?INFO("Analyzing no files with ~p...", [Plt]), + {0, State}; +succ_typings(State, Plt, Output, Files) -> ?INFO("Analyzing ~b files with ~p...", [length(Files), Plt]), Opts = [{analysis_type, succ_typings}, {get_warnings, true}, diff --git a/test/rebar_dialyzer_SUITE.erl b/test/rebar_dialyzer_SUITE.erl index ededf6a..e5d8c52 100644 --- a/test/rebar_dialyzer_SUITE.erl +++ b/test/rebar_dialyzer_SUITE.erl @@ -10,6 +10,7 @@ groups/0, empty_base_plt/1, empty_app_plt/1, + empty_app_succ_typings/1, update_base_plt/1, update_app_plt/1, build_release_plt/1, @@ -55,7 +56,7 @@ all() -> [{group, empty}, {group, build_and_check}, {group, update}]. groups() -> - [{empty, [empty_base_plt, empty_app_plt]}, + [{empty, [empty_base_plt, empty_app_plt, empty_app_succ_typings]}, {build_and_check, [build_release_plt, plt_apps_option]}, {update, [update_base_plt, update_app_plt]}]. @@ -102,6 +103,19 @@ empty_app_plt(Config) -> ok. +empty_app_succ_typings(Config) -> + AppDir = ?config(apps, Config), + RebarConfig = ?config(rebar_config, Config), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_empty_app(AppDir, Name, Vsn, []), + + rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"], + {ok, [{app, Name}]}), + + ok. + update_base_plt(Config) -> AppDir = ?config(apps, Config), RebarConfig = ?config(rebar_config, Config), -- cgit v1.1 From 941048dce7b0fab65ee8a07828bbc8c5e2034841 Mon Sep 17 00:00:00 2001 From: James Fish Date: Mon, 11 Jul 2016 18:01:33 +0100 Subject: Avoid PLT rebuild when files deleted on new dialyzer --- src/rebar_prv_dialyzer.erl | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index ec9e81a..82d2d07 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -249,10 +249,15 @@ ebin_files(EbinDir) -> File <- filelib:wildcard(Wildcard, EbinDir)]. read_plt(_State, Plt) -> - case dialyzer:plt_info(Plt) of - {ok, Info} -> - Files = proplists:get_value(files, Info, []), + Vsn = dialyzer_version(), + case plt_files(Plt) of + {ok, Files} when Vsn < {2, 9, 0} -> + % Before dialyzer-2.9 (OTP 18.3) removing a beam file from the PLT + % that no longer exists would crash. Therefore force a rebuild of + % PLT if any files no longer exist. read_plt_files(Plt, Files); + {ok, _} = Result when Vsn >= {2, 9, 0} -> + Result; {error, no_such_file} -> error; {error, read_error} -> @@ -260,6 +265,14 @@ read_plt(_State, Plt) -> throw({dialyzer_error, Error}) end. +plt_files(Plt) -> + case dialyzer:plt_info(Plt) of + {ok, Info} -> + {ok, proplists:get_value(files, Info, [])}; + {error, _} = Error -> + Error + end. + %% If any file no longer exists dialyzer will fail when updating the PLT. read_plt_files(Plt, Files) -> case [File || File <- Files, not filelib:is_file(File)] of -- cgit v1.1 From a759ed662030b390daab2b92b985068a7a020eb5 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Mon, 11 Jul 2016 18:12:04 -0400 Subject: Add OTP 19.0 to travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index a45cb05..dbb4f26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ sudo: false language: erlang install: 'true' otp_release: +- 19.0 - 18.0 - 17.5 - R16B03-1 -- cgit v1.1