diff options
32 files changed, 546 insertions, 290 deletions
| 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 @@ -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/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 <tag> [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" +    }' 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"}. diff --git a/rebar.config b/rebar.config index ea5af27..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}. @@ -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]}. @@ -1,10 +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}, + {<<"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},   {<<"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}]. + {<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0}]}. +[ +{pkg_hash,[ + {<<"bbmustache">>, <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>}, + {<<"certifi">>, <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, + {<<"cf">>, <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>}, + {<<"cth_readable">>, <<"293120673DFF82F0768612C5282E35C40CACC1B6F94FE99077438FD3749D0E27">>}, + {<<"erlware_commons">>, <<"A04433071AD7D112EDEFC75AC77719DD3E6753E697AC09428FC83D7564B80B15">>}, + {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, + {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, + {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, + {<<"relx">>, <<"286DD5244B4786F56AAC75D5C8E2D1FB4CFD306810D4EC8548F3AE1B3AADB8F7">>}, + {<<"ssl_verify_hostname">>, <<"2E73E068CD6393526F9FA6D399353D7C9477D6886BA005F323B592D389FB47BE">>}]} +]. diff --git a/src/rebar.hrl b/src/rebar.hrl index 4d69c7b..f96ed5e 100644 --- a/src/rebar.hrl +++ b/src/rebar.hrl @@ -23,6 +23,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/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 diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl index 957526e..d256cac 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) -> @@ -143,7 +143,9 @@ parse_dep(Parent, {Name, Source, Opts}, DepsDir, IsLock, State) when is_tuple(So      ?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); @@ -175,7 +177,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); @@ -185,7 +187,14 @@ update_source(AppInfo, {pkg, PkgName, PkgVsn}, State) ->                                _ ->                                    {PkgName, PkgVsn}                            end, -    AppInfo1 = rebar_app_info:source(AppInfo, {pkg, PkgName1, PkgVsn1}), +    %% 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), diff --git a/src/rebar_config.erl b/src/rebar_config.erl index 836980e..b50c030 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -56,35 +56,105 @@ 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. +            %% 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; +                _ -> +                    %% 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_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.", []), -            read_attrs(Vsn, Locks, Attrs) +                  "upgrade Rebar3.", [])      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,~n~p}.~n[~n~s~n].~n", +                                          [?CONFIG_VERSION, NewLocks, +                                           format_attrs(Attrs)])) +    end. -read_attrs(_Vsn, Locks, _Attrs) -> +%% 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. -    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_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_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", 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..5817817 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(<<Name/binary, "-", Vsn/binary, ".tar">>), @@ -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]), @@ -58,17 +58,20 @@ cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn}, 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 = <<Version/binary, Meta/binary, Contents/binary>>,      <<X:256/big-unsigned>> = 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."}. 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/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 diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index 622ee60..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 @@ -353,8 +366,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}, @@ -369,12 +393,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/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), 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. 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 aa2e7b2..24bf2a0 100644 --- a/test/rebar_deps_SUITE.erl +++ b/test/rebar_deps_SUITE.erl @@ -476,5 +476,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_dialyzer_SUITE.erl b/test/rebar_dialyzer_SUITE.erl index 22a4894..e5d8c52 100644 --- a/test/rebar_dialyzer_SUITE.erl +++ b/test/rebar_dialyzer_SUITE.erl @@ -3,8 +3,14 @@  -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, +         empty_app_succ_typings/1,           update_base_plt/1,           update_app_plt/1,           build_release_plt/1, @@ -23,6 +29,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 +45,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 +53,68 @@ 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, empty_app_succ_typings]}, +     {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. + +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), 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..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">>}} @@ -103,7 +117,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, ?good_checksum}, State)),      Cache = ?config(cache_dir, Config),      ?assert(filelib:is_regular(filename:join(Cache, <<Pkg/binary, "-", Vsn/binary, ".tar">>))). @@ -116,7 +130,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, ?good_checksum}, State)),      {ok, Content} = file:read_file(CachedFile).  badindexchk(Config) -> @@ -124,7 +138,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, ?good_checksum}, State)),      %% The cached file is there for forensic purposes      Cache = ?config(cache_dir, Config),      ?assert(filelib:is_regular(filename:join(Cache, <<Pkg/binary, "-", Vsn/binary, ".tar">>))). @@ -134,11 +148,35 @@ 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, ?good_checksum}, State)),      %% The cached file is there for forensic purposes      Cache = ?config(cache_dir, Config),      ?assert(filelib:is_regular(filename:join(Cache, <<Pkg/binary, "-", Vsn/binary, ".tar">>))). +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, <<Pkg/binary, "-", Vsn/binary, ".tar">>))). + +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, <<Pkg/binary, "-", Vsn/binary, ".tar">>), +    ?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), @@ -148,7 +186,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, ?good_checksum}, State)),      %% Cache has refreshed      ?assert({ok, Contents} =/= file:read_file(CachedFile)). @@ -161,7 +199,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, ?good_checksum}, State)),      {ok, Content} = file:read_file(CachedFile).  bad_disconnect(Config) -> @@ -169,7 +207,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, ?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_test_utils.erl b/test/rebar_test_utils.erl index 23b0178..8c177c9 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() ->      <<A:32, B:32, C:32>> = crypto:rand_bytes(12),      random:seed({A,B,C}). - - +-endif.  expand_deps(_, []) -> [];  expand_deps(git, [{Name, Deps} | Rest]) -> @@ -149,21 +160,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 +188,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 +206,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,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)) @@ -318,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 @@ -329,7 +342,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), 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">>}]}]. diff --git a/test/rebar_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl index e7651a1..66e1fdf 100644 --- a/test/rebar_upgrade_SUITE.erl +++ b/test/rebar_upgrade_SUITE.erl @@ -654,7 +654,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( | 
