diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rwxr-xr-x | pr2relnotes.py | 188 | ||||
-rw-r--r-- | rebar.config.sample | 201 | ||||
-rw-r--r-- | src/rebar_file_utils.erl | 37 | ||||
-rw-r--r-- | src/rebar_git_resource.erl | 32 | ||||
-rw-r--r-- | src/rebar_hg_resource.erl | 29 | ||||
-rw-r--r-- | src/rebar_prv_compile.erl | 2 | ||||
-rw-r--r-- | src/rebar_prv_eunit.erl | 62 | ||||
-rw-r--r-- | src/rebar_utils.erl | 17 |
10 files changed, 430 insertions, 141 deletions
@@ -18,3 +18,4 @@ logs priv/templates/*.dtl.erl ebin .edts +env @@ -117,7 +117,7 @@ format_error(Reason) -> Building -------- -Recommended installation of [Erlang/OTP](http://www.erlang.org) is source built using [erln8](http://metadave.github.io/erln8/) or [kerl](https://github.com/yrashk/kerl). For binary packages use those provided by [Erlang Solutions](https://www.erlang-solutions.com/downloads/download-erlang-otp), but be sure to choose the "Standard" download option or you'll have issues building projects. +Recommended installation of [Erlang/OTP](http://www.erlang.org) is source built using [erln8](http://erln8.github.io/erln8/) or [kerl](https://github.com/yrashk/kerl). For binary packages use those provided by [Erlang Solutions](https://www.erlang-solutions.com/downloads/download-erlang-otp), but be sure to choose the "Standard" download option or you'll have issues building projects. ### Dependencies diff --git a/pr2relnotes.py b/pr2relnotes.py new file mode 100755 index 0000000..6e9a4f4 --- /dev/null +++ b/pr2relnotes.py @@ -0,0 +1,188 @@ +#!/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, 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/rebar.config.sample b/rebar.config.sample index 1bd5e0c..8621b44 100644 --- a/rebar.config.sample +++ b/rebar.config.sample @@ -3,6 +3,13 @@ %% This is a sample rebar.conf file that shows examples of some of rebar's %% options. +%% == Artifacts == + +%% Artifacts are files generated by other tools (or plugins) and whose +%% presence is necessary for a build to be considered successful +{artifacts, ["priv/mycode.so"]}. + + %% == Erlang Compiler == %% Erlang files to compile before the rest. Rebar automatically compiles @@ -13,12 +20,6 @@ %% Erlang compiler options {erl_opts, [no_debug_info, {i, "myinclude"}, - %% directories containing source files - {src_dirs, ["src", "src2", "src3"]}, - %% extra_src_dirs are directories containing - %% source files that are NOT part of the - %% application itself - {extra_src_dirs, ["eunit", "ct"]}, {platform_define, "(linux|solaris|freebsd|darwin)", 'HAVE_SENDFILE'}, {platform_define, "(linux|freebsd)", 'BACKLOG', 128}, @@ -26,60 +27,29 @@ %% MIB Options? {mib_opts, []}. - %% SNMP mibs to compile first? {mib_first_files, []}. %% leex options {xrl_opts, []}. - %% leex files to compile first {xrl_first_files, []}. %% yecc options {yrl_opts, []}. - %% yecc files to compile first {yrl_first_files, []}. -%% == EDoc == - -%% EDoc options -{edoc_opts, []}. - -%% == escriptize == -{escript_name, "application"}. -{escript_incl_apps, []}. -{escript_shebang, "#!/usr/bin/env escript\n"}. -{escript_comment, "%%\n"}. -{escript_emu_args, "%%! -pa application/application/ebin\n"}. - -%% == ErlyDTL Compiler == - -%% Options for the ErlyDTL compiler -{erlydtl_opts, []}. - -%% == EUnit == - -%% Options for eunit:test() -{eunit_opts, []}. - -%% Additional compile options for eunit. erl_opts is also used -{eunit_compile_opts, []}. - -%% Files to compile first when compiling eunit test suites -{eunit_first_files, []}. %% == Common Test == -%% Options to pass to ct:run_test/1 -{ct_opts, [{logdir, "logs"}, {dir, "test"}]}. - -%% Additional compile options for ct. erl_opts is also used +%% {erl_opts, [...]}, but for CT runs {ct_compile_opts, []}. - -%% Files to compile first when compiling ct test suites +%% {erl_first_files, ...} but for CT runs {ct_first_files, []}. +%% Same options as for ct:run_test(...) +{ct_opts, []}. + %% == Cover == @@ -90,6 +60,7 @@ %% Options to pass to cover provider {cover_opts, [verbose]}. + %% == Dependencies == %% What dependencies we have, dependencies can be of 3 forms, an application @@ -97,21 +68,112 @@ %% an application name, a version and the SCM details on how to fetch it (SCM %% type, location and revision). %% Rebar3 currently supports git and hg -{deps, [app_name, - {rebar, "1.0.*"}, - {rebar, ".*", - {git, "git://github.com/rebar/rebar.git"}}, - {rebar, ".*", - {git, "git://github.com/rebar/rebar.git", "Rev"}}, - {rebar, "1.0.*", - {git, "git://github.com/rebar/rebar.git", {branch, "master"}}}, - {rebar, "1.0.0", - {git, "git://github.com/rebar/rebar.git", {tag, "1.0.0"}}}, - {rebar, ".*", - {git, "git://github.com/rebar/rebar.git", {ref, "7f73b8d3650b41ffd53a199f3eda20985eda84e3"}}}, - {app_name, ".*", {hg, "https://www.example.org/url"}}]}. - -%% == Pre/Post Command Hooks == +{deps, [ + app_name, % latest version of package + {rebar, "1.0.0"}, % version 1.0.0 of a package + {rebar, % git, master branch of app, explicit + {git, "git://github.com/rebar/rebar.git", {branch, "master"}}}, + {rebar, % git, over HTTPS + {git, "https://github.com/rebar/rebar.git", {branch, "master"}}}, + {rebar, % git, tag + {git, "https://github.com/rebar/rebar.git", {tag, "1.0.0"}}}, + {rebar, % git, specific reference/hash + {git, "https://github.com/rebar/rebar.git", {ref, "7f73b8d6"}}}, + {rebar, % mercurial is also supported + {hg, "https://github.com/rebar/rebar.git", {tag, "1.0.0"}}}, + %% Alternative formats, backwards compatible declarations + {rebar, % implicit master, will warn recommending explicit branch + {git, "git://github.com/rebar/rebar.git"}}, + {rebar, "1.0.*", % regex version check, ignored + {git, "git://github.com/rebar/rebar.git"}}, + {rebar, "1.0.*", % literal branch/ref/tag, will warn for explicit reference + {git, "git://github.com/rebar/rebar.git", "Rev"}}, + {rebar, ".*", % 'raw' dependency, argument ignored + {git, "git://github.com/rebar/rebar.git", {branch, "master"}}, + [raw]} +]}. + + +%% == Dialyzer == + +{dialyzer, [ + {warnings, [underspecs, no_return]}, + {get_warnings, true}, + {plt_extra_apps, []}, + {plt_location, local}, % local | "/my/file/name" + {plt_prefix, "rebar3"}, + {base_plt_apps, [stdlib, kernel, crypto]}, + {base_plt_location, global}, % global | "/my/file/name" + {base_plt_prefix, "rebar3"} +]}. + + +%% == Directories == + +%% directory for artifacts produced by rebar3 +{base_dir, "_build"}. +%% directory in '<base_dir>/<profile>/' where deps go +{deps_dir, "lib"}. +%% where rebar3 operates from; defaults to the current working directory +{root_dir, "."}. +%% where checkout dependencies are to be located +{checkouts_dir, "_checkouts"}. +%% directory in '<base_dir>/<profile>/' where plugins go +{plugins_dir, "plugins"}. +%% directories where OTP applications for the project can be located +{project_app_dirs, ["apps/*", "lib/*", "."]}. +%% Directories where source files for an OTP application can be found +{src_dirs, ["src"]}. +%% Paths to miscellaneous Erlang files to compile for an app +%% without including them in its modules list +{extra_src_dirs, []}. + + +%% == EDoc == + +%% EDoc options, same as those passed to the edoc compiler +%% when called by hand. +{edoc_opts, []}. + + +%% == Escript == + +%% name of the main OTP application to boot +{escript_main_app, application} +%% Name of the resulting escript executable +{escript_name, "application"}. +%% apps (other than main and deps) to be included +{escript_incl_apps, []}. +%% Executable escript lines +{escript_shebang, "#!/usr/bin/env escript\n"}. +{escript_comment, "%%\n"}. +{escript_emu_args, "%%! -escript main ~s -pa ~s/~s/ebin\n"}. + + +%% == EUnit == + +%% Options for eunit:test(Tests, ...) +{eunit_opts, []}. +%% Additional compile options for eunit. erl_opts is also used +{eunit_compile_opts, []}. +%% {erl_first_files, ...} but for Eunit +{eunit_first_files, []}. + + +%% == Overrides == + +{overrides, [ + %% Add options to mydep's configurations for each element + {add, mydep, [{erl_opts, []}]}, + %% replace mydep's configuration option + {override, mydep, [{erl_opts, []}]}, + %% replace all dependencies' configuration options + {override, [{erl_opts, []}]} +]}. + +%% == Pre/Post Shell Hooks == + +%% Running shell commands before or after a given rebar3 command {pre_hooks, [{clean, "./prepare_package_files.sh"}, {"linux", compile, "c_src/build_linux.sh"}, @@ -123,6 +185,31 @@ {eunit, "touch file2.out"}, {compile, "touch postcompile.out"}]}. +%% == Provider Hooks == + +%% Run a rebar3 command before or after another one. +%% Only clean, ct, compile, eunit, release, and tar can be hooked around + +%% runs 'clean' before 'compile' +{provider_hooks, [{pre, [{compile, clean}]}]} + + +%% == Releases == + +{relx, [{release, {my_release, "0.0.1"}, + [myapp]}, + + {dev_mode, true}, + {include_erts, false}, + + {extended_start_script, true}]}. + +%% == Shell == + +%% apps to auto-boot with `rebar3 shell'; defaults to apps +%% specified in a `relx' tuple, if any. +{shell_apps, [app1, app2]} + %% == xref == {xref_warnings, false}. diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index b4cdc27..da58c00 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -98,7 +98,8 @@ symlink_or_copy(Source, Target) -> win32_symlink(Source, Target) -> Res = rebar_utils:sh( ?FMT("cmd /c mklink /j \"~s\" \"~s\"", - [filename:nativename(Target), filename:nativename(Source)]), + [rebar_utils:escape_double_quotes(filename:nativename(Target)), + rebar_utils:escape_double_quotes(filename:nativename(Source))]), [{use_stdout, false}, return_on_error]), case win32_ok(Res) of true -> ok; @@ -115,7 +116,7 @@ win32_symlink(Source, Target) -> rm_rf(Target) -> case os:type() of {unix, _} -> - EscTarget = escape_path(Target), + EscTarget = rebar_utils:escape_chars(Target), {ok, []} = rebar_utils:sh(?FMT("rm -rf ~s", [EscTarget]), [{use_stdout, false}, abort_on_error]), ok; @@ -134,10 +135,10 @@ cp_r([], _Dest) -> cp_r(Sources, Dest) -> case os:type() of {unix, _} -> - EscSources = [escape_path(Src) || Src <- Sources], + EscSources = [rebar_utils:escape_chars(Src) || Src <- Sources], SourceStr = string:join(EscSources, " "), {ok, []} = rebar_utils:sh(?FMT("cp -R ~s \"~s\"", - [SourceStr, Dest]), + [SourceStr, rebar_utils:escape_double_quotes(Dest)]), [{use_stdout, false}, abort_on_error]), ok; {win32, _} -> @@ -149,8 +150,8 @@ cp_r(Sources, Dest) -> mv(Source, Dest) -> case os:type() of {unix, _} -> - EscSource = escape_path(Source), - EscDest = escape_path(Dest), + EscSource = rebar_utils:escape_chars(Source), + EscDest = rebar_utils:escape_chars(Dest), {ok, []} = rebar_utils:sh(?FMT("mv ~s ~s", [EscSource, EscDest]), [{use_stdout, false}, abort_on_error]), ok; @@ -158,13 +159,13 @@ mv(Source, Dest) -> Cmd = case filelib:is_dir(Source) of true -> ?FMT("robocopy /move /s \"~s\" \"~s\" 1> nul", - [filename:nativename(Source), - filename:nativename(Dest)]); + [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", - [filename:nativename(filename:dirname(Source)), - filename:nativename(Dest), - filename:basename(Source)]) + [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))]) end, Res = rebar_utils:sh(Cmd, [{use_stdout, false}, return_on_error]), @@ -250,7 +251,7 @@ touch(Path) -> delete_each_dir_win32([]) -> ok; delete_each_dir_win32([Dir | Rest]) -> {ok, []} = rebar_utils:sh(?FMT("rd /q /s \"~s\"", - [filename:nativename(Dir)]), + [rebar_utils:escape_double_quotes(filename:nativename(Dir))]), [{use_stdout, false}, return_on_error]), delete_each_dir_win32(Rest). @@ -260,13 +261,13 @@ xcopy_win32(Source,Dest)-> Cmd = case filelib:is_dir(Source) of true -> ?FMT("robocopy \"~s\" \"~s\" /e /is 1> nul", - [filename:nativename(Source), - filename:nativename(Dest)]); + [rebar_utils:escape_double_quotes(filename:nativename(Source)), + rebar_utils:escape_double_quotes(filename:nativename(Dest))]); false -> ?FMT("robocopy \"~s\" \"~s\" \"~s\" /e /is 1> nul", - [filename:nativename(filename:dirname(Source)), - filename:nativename(Dest), - filename:basename(Source)]) + [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))]) end, Res = rebar_utils:sh(Cmd, [{use_stdout, false}, return_on_error]), @@ -318,5 +319,3 @@ cp_r_win32(Source,Dest) -> end, filelib:wildcard(Source)), ok. -escape_path(Str) -> - re:replace(Str, "([ ()?])", "\\\\&", [global, {return, list}]). diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl index e2f3f69..aec1535 100644 --- a/src/rebar_git_resource.erl +++ b/src/rebar_git_resource.erl @@ -16,7 +16,7 @@ lock(AppDir, {git, Url, _}) -> lock(AppDir, {git, Url}) -> AbortMsg = io_lib:format("Locking of git dependency failed in ~s", [AppDir]), {ok, VsnString} = - rebar_utils:sh("git --git-dir=\"" ++ AppDir ++ "/.git\" rev-parse --verify HEAD", + rebar_utils:sh("git --git-dir=\"" ++ rebar_utils:escape_double_quotes(AppDir) ++ "/.git\" rev-parse --verify HEAD", [{use_stdout, false}, {debug_abort_on_error, AbortMsg}]), Ref = string:strip(VsnString, both, $\n), {git, Url, {ref, Ref}}. @@ -32,10 +32,11 @@ needs_update(Dir, {git, Url, {tag, Tag}}) -> not ((Current1 =:= Tag) andalso compare_url(Dir, Url)); needs_update(Dir, {git, Url, {branch, Branch}}) -> %% Fetch remote so we can check if the branch has changed - {ok, _} = rebar_utils:sh(?FMT("git fetch origin ~s", [Branch]), + SafeBranch = rebar_utils:escape_chars(Branch), + {ok, _} = rebar_utils:sh(?FMT("git fetch origin ~s", [SafeBranch]), [{cd, Dir}]), %% Check for new commits to origin/Branch - {ok, Current} = rebar_utils:sh(?FMT("git log HEAD..origin/~s --oneline", [Branch]), + {ok, Current} = rebar_utils:sh(?FMT("git log HEAD..origin/~s --oneline", [SafeBranch]), [{cd, Dir}]), ?DEBUG("Checking git branch ~s for updates", [Branch]), not ((Current =:= []) andalso compare_url(Dir, Url)); @@ -93,24 +94,33 @@ download(Dir, {git, Url, ""}, State) -> download(Dir, {git, Url, {branch, Branch}}, _State) -> ok = filelib:ensure_dir(Dir), rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", - [Url, filename:basename(Dir), Branch]), + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir)), + rebar_utils:escape_chars(Branch)]), [{cd, filename:dirname(Dir)}]); download(Dir, {git, Url, {tag, Tag}}, _State) -> ok = filelib:ensure_dir(Dir), rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", - [Url, filename:basename(Dir), Tag]), + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir)), + rebar_utils:escape_chars(Tag)]), [{cd, filename:dirname(Dir)}]); download(Dir, {git, Url, {ref, Ref}}, _State) -> ok = filelib:ensure_dir(Dir), - rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(Dir)]), + rebar_utils:sh(?FMT("git clone -n ~s ~s", + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]), rebar_utils:sh(?FMT("git checkout -q ~s", [Ref]), [{cd, Dir}]); download(Dir, {git, Url, Rev}, _State) -> ?WARN("WARNING: It is recommended to use {branch, Name}, {tag, Tag} or {ref, Ref}, otherwise updating the dep may not work as expected.", []), ok = filelib:ensure_dir(Dir), - rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(Dir)]), + rebar_utils:sh(?FMT("git clone -n ~s ~s", + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]), - rebar_utils:sh(?FMT("git checkout -q ~s", [Rev]), [{cd, Dir}]). + rebar_utils:sh(?FMT("git checkout -q ~s", [rebar_utils:escape_chars(Rev)]), + [{cd, Dir}]). make_vsn(Dir) -> {Vsn, RawRef, RawCount} = collect_default_refcount(Dir), @@ -133,9 +143,10 @@ collect_default_refcount(Dir) -> {ok, RawCount} = case Tag of undefined -> - AbortMsg2 = "Getting rev-list of git depedency failed in " ++ rebar_dir:get_cwd(), + AbortMsg2 = "Getting rev-list of git depedency failed in " ++ Dir, {ok, PatchLines} = rebar_utils:sh("git rev-list HEAD", [{use_stdout, false}, + {cd, Dir}, {debug_abort_on_error, AbortMsg2}]), rebar_utils:line_count(PatchLines); _ -> @@ -161,9 +172,10 @@ get_patch_count(Dir, RawRef) -> AbortMsg = "Getting rev-list of git dep failed in " ++ Dir, Ref = re:replace(RawRef, "\\s", "", [global]), Cmd = io_lib:format("git rev-list ~s..HEAD", - [Ref]), + [rebar_utils:escape_chars(Ref)]), {ok, PatchLines} = rebar_utils:sh(Cmd, [{use_stdout, false}, + {cd, Dir}, {debug_abort_on_error, AbortMsg}]), rebar_utils:line_count(PatchLines). diff --git a/src/rebar_hg_resource.erl b/src/rebar_hg_resource.erl index a67abb9..7d03eda 100644 --- a/src/rebar_hg_resource.erl +++ b/src/rebar_hg_resource.erl @@ -57,26 +57,34 @@ download(Dir, {hg, Url, ""}, State) -> download(Dir, {hg, Url, {branch, Branch}}, _State) -> ok = filelib:ensure_dir(Dir), rebar_utils:sh(?FMT("hg clone -q -b ~s ~s ~s", - [Branch, Url, filename:basename(Dir)]), + [rebar_utils:escape_chars(Branch), + rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]); download(Dir, {hg, Url, {tag, Tag}}, _State) -> ok = filelib:ensure_dir(Dir), rebar_utils:sh(?FMT("hg clone -q -u ~s ~s ~s", - [Tag, Url, filename:basename(Dir)]), + [rebar_utils:escape_chars(Tag), + rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]); download(Dir, {hg, Url, {ref, Ref}}, _State) -> ok = filelib:ensure_dir(Dir), rebar_utils:sh(?FMT("hg clone -q -r ~s ~s ~s", - [Ref, Url, filename:basename(Dir)]), + [rebar_utils:escape_chars(Ref), + rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]); download(Dir, {hg, Url, Rev}, _State) -> ok = filelib:ensure_dir(Dir), rebar_utils:sh(?FMT("hg clone -q -r ~s ~s ~s", - [Rev, Url, filename:basename(Dir)]), + [rebar_utils:escape_chars(Rev), + rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]). make_vsn(Dir) -> - BaseHg = "hg -R '" ++ Dir ++ "' ", + BaseHg = "hg -R \"" ++ rebar_utils:escape_double_quotes(Dir) ++ "\" ", Ref = get_ref(Dir), Cmd = BaseHg ++ "log --template \"{latesttag}+build.{latesttagdistance}.rev.{node|short}\"" " --rev " ++ Ref, @@ -95,23 +103,23 @@ make_vsn(Dir) -> %%% Internal functions compare_url(Dir, Url) -> - CurrentUrl = string:strip(os:cmd("hg -R '" ++ Dir ++"' paths default"), both, $\n), + CurrentUrl = string:strip(os:cmd("hg -R \"" ++ rebar_utils:escape_double_quotes(Dir) ++"\" paths default"), both, $\n), CurrentUrl1 = string:strip(CurrentUrl, both, $\r), parse_hg_url(CurrentUrl1) =:= parse_hg_url(Url). get_ref(Dir) -> AbortMsg = io_lib:format("Get ref of hg dependency failed in ~s", [Dir]), {ok, RefString} = - rebar_utils:sh("hg -R '" ++ Dir ++ "' --debug id -i", + rebar_utils:sh("hg -R \"" ++ rebar_utils:escape_double_quotes(Dir) ++ "\" --debug id -i", [{use_stdout, false}, {debug_abort_on_error, AbortMsg}]), string:strip(RefString, both, $\n). get_tag_distance(Dir, Ref) -> AbortMsg = io_lib:format("Get tag distance of hg dependency failed in ~s", [Dir]), {ok, LogString} = - rebar_utils:sh("hg -R '" ++ Dir ++ "' " + rebar_utils:sh("hg -R \"" ++ rebar_utils:escape_double_quotes(Dir) ++ "\" " "log --template \"{latesttag}-{latesttagdistance}\n\" " - "--rev " ++ Ref, + "--rev " ++ rebar_utils:escape_chars(Ref), [{use_stdout, false}, {debug_abort_on_error, AbortMsg}]), Log = string:strip(LogString, both, $\n), @@ -121,7 +129,8 @@ get_tag_distance(Dir, Ref) -> get_branch_ref(Dir, Branch) -> AbortMsg = io_lib:format("Get branch ref of hg dependency failed in ~s", [Dir]), {ok, BranchRefString} = - rebar_utils:sh("hg -R '" ++ Dir ++ "' log --template \"{node}\n\" --rev " ++ Branch, + rebar_utils:sh("hg -R \"" ++ rebar_utils:escape_double_quotes(Dir) ++ + "\" log --template \"{node}\n\" --rev " ++ rebar_utils:escape_chars(Branch), [{use_stdout, false}, {debug_abort_on_error, AbortMsg}]), string:strip(BranchRefString, both, $\n). diff --git a/src/rebar_prv_compile.erl b/src/rebar_prv_compile.erl index d76fbda..74be7a6 100644 --- a/src/rebar_prv_compile.erl +++ b/src/rebar_prv_compile.erl @@ -142,7 +142,7 @@ copy_app_dirs(State, OldAppDir, AppDir) -> end, %% link to src_dirs to be adjacent to ebin is needed for R15 use of cover/xref - SrcDirs = rebar_dir:all_src_dirs(State, ["src"], ["test"]), + SrcDirs = rebar_dir:all_src_dirs(State, ["src"], []), [symlink_or_copy(OldAppDir, AppDir, Dir) || Dir <- ["priv", "include"] ++ SrcDirs]; false -> ok diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 28c0ed6..41dd434 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -85,7 +85,8 @@ format_error({error_running_tests, Reason}) -> test_state(State) -> ErlOpts = rebar_state:get(State, eunit_compile_opts, []), TestOpts = safe_define_test_macro(ErlOpts), - first_files(State) ++ [{erl_opts, TestOpts}]. + TestDir = [{extra_src_dirs, ["test"]}], + first_files(State) ++ [{erl_opts, TestOpts ++ TestDir}]. safe_define_test_macro(Opts) -> %% defining a compile macro twice results in an exception so @@ -106,49 +107,38 @@ first_files(State) -> prepare_tests(State) -> {RawOpts, _} = rebar_state:command_parsed_args(State), - resolve_apps(State, RawOpts). + ok = maybe_cover_compile(State, RawOpts), + ProjectApps = project_apps(State), + resolve_apps(ProjectApps, RawOpts). -resolve_apps(State, RawOpts) -> +maybe_cover_compile(State, Opts) -> + State1 = case proplists:get_value(cover, Opts, false) of + true -> rebar_state:set(State, cover_enabled, true); + false -> State + end, + rebar_prv_cover:maybe_cover_compile(State1). + +resolve_apps(ProjectApps, RawOpts) -> case proplists:get_value(app, RawOpts) of - undefined -> resolve_suites(State, RawOpts); + undefined -> resolve_suites(ProjectApps, RawOpts); %% convert app name strings to `rebar_app_info` objects Apps -> AppNames = string:tokens(Apps, [$,]), - ProjectApps = project_apps(State), case filter_apps_by_name(AppNames, ProjectApps) of - {ok, TestApps} -> resolve_suites(State, TestApps, RawOpts); + {ok, TestApps} -> resolve_suites(TestApps, RawOpts); Error -> Error end end. -resolve_suites(State, RawOpts) -> resolve_suites(State, project_apps(State), RawOpts). - -resolve_suites(State, Apps, RawOpts) -> +resolve_suites(Apps, RawOpts) -> case proplists:get_value(suite, RawOpts) of - undefined -> compile_tests(State, Apps, all, RawOpts); + undefined -> test_set(Apps, all); Suites -> SuiteNames = string:tokens(Suites, [$,]), case filter_suites_by_apps(SuiteNames, Apps) of - {ok, S} -> compile_tests(State, Apps, S, RawOpts); + {ok, S} -> test_set(Apps, S); Error -> Error end end. -compile_tests(State, TestApps, Suites, RawOpts) -> - F = fun(AppInfo) -> - S = rebar_app_info:state_or_new(State, AppInfo), - ok = rebar_erlc_compiler:compile(replace_src_dirs(S), - ec_cnv:to_list(rebar_app_info:out_dir(AppInfo))) - end, - lists:foreach(F, TestApps), - ok = maybe_cover_compile(State, RawOpts), - {ok, test_set(TestApps, Suites)}. - -maybe_cover_compile(State, Opts) -> - State1 = case proplists:get_value(cover, Opts, false) of - true -> rebar_state:set(State, cover_enabled, true); - false -> State - end, - rebar_prv_cover:maybe_cover_compile(State1). - project_apps(State) -> filter_checkouts(rebar_state:project_apps(State)). @@ -215,20 +205,8 @@ app_modules([App|Rest], Acc) -> app_modules(Rest, NewAcc) end. -replace_src_dirs(State) -> - %% replace any `src_dirs` with the test dirs - ErlOpts = rebar_state:get(State, erl_opts, []), - StrippedOpts = filter_src_dirs(ErlOpts), - case rebar_dir:extra_src_dirs(State) of - [] -> rebar_state:set(State, erl_opts, [{src_dirs, ["test"]}|StrippedOpts]); - _ -> rebar_state:set(State, erl_opts, StrippedOpts) - end. - -filter_src_dirs(ErlOpts) -> - lists:filter(fun({src_dirs, _}) -> false; (_) -> true end, ErlOpts). - -test_set(Apps, all) -> set_apps(Apps, []); -test_set(_Apps, Suites) -> set_suites(Suites, []). +test_set(Apps, all) -> {ok, set_apps(Apps, [])}; +test_set(_Apps, Suites) -> {ok, set_suites(Suites, [])}. set_apps([], Acc) -> lists:reverse(Acc); set_apps([App|Rest], Acc) -> diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index ebdf0fe..6eb4f4b 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -57,7 +57,10 @@ tup_umerge/2, tup_sort/1, line_count/1, - set_httpc_options/0]). + set_httpc_options/0, + escape_chars/1, + escape_double_quotes/1, + escape_double_quotes_weak/1]). %% for internal use only -export([otp_release/0]). @@ -684,3 +687,15 @@ set_httpc_options(_, []) -> set_httpc_options(Scheme, Proxy) -> {ok, {_, _, Host, Port, _, _}} = http_uri:parse(Proxy), httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar). + +%% escape\ as\ a\ shell\? +escape_chars(Str) -> + re:replace(Str, "([ ()?`!$])", "\\\\&", [global, {return, list}]). + +%% "escape inside these" +escape_double_quotes(Str) -> + re:replace(Str, "([\"\\\\`!$*])", "\\\\&", [global, {return, list}]). + +%% "escape inside these" but allow * +escape_double_quotes_weak(Str) -> + re:replace(Str, "([\"\\\\`!$])", "\\\\&", [global, {return, list}]). |