summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--README.md2
-rwxr-xr-xpr2relnotes.py188
-rw-r--r--rebar.config.sample201
-rw-r--r--src/rebar_file_utils.erl37
-rw-r--r--src/rebar_git_resource.erl32
-rw-r--r--src/rebar_hg_resource.erl29
-rw-r--r--src/rebar_prv_compile.erl2
-rw-r--r--src/rebar_prv_eunit.erl62
-rw-r--r--src/rebar_utils.erl17
10 files changed, 430 insertions, 141 deletions
diff --git a/.gitignore b/.gitignore
index 8028d9c..c6be7ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ logs
priv/templates/*.dtl.erl
ebin
.edts
+env
diff --git a/README.md b/README.md
index a060ec0..ae93f40 100644
--- a/README.md
+++ b/README.md
@@ -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}]).