diff --git a/src/rebar3_hex.erl b/src/rebar3_hex.erl index 8fcbf2c3..db853796 100644 --- a/src/rebar3_hex.erl +++ b/src/rebar3_hex.erl @@ -23,6 +23,7 @@ init(State) -> lists:foldl(fun provider_init/2, {ok, State}, [rebar3_hex_user, + rebar3_hex_build, rebar3_hex_cut, rebar3_hex_owner, rebar3_hex_repo, diff --git a/src/rebar3_hex_build.erl b/src/rebar3_hex_build.erl index 92418569..5b1607d0 100644 --- a/src/rebar3_hex_build.erl +++ b/src/rebar3_hex_build.erl @@ -1,3 +1,88 @@ +% @doc `rebar3 hex build' +% +%% Builds a new local version of your package. +%% +%% By default this provider will build both a package tarball and docs tarball. +%% +%% The package and docs .tar files are created in the current directory, but is not pushed to the repository. An app +%% named foo at version 1.2.3 will be built as foo-1.2.3.tar. Likewise the docs .tar would be built as +%% foo-1.2.4-docs.tar. +%% +%% ```shell +%% $ rebar3 hex build +%% ''' +%% +%% You may also build only a package or docs tarball utilizing the same available command line options. +%% +%% ``` shell +%% $ rebar3 hex build package +%% ''' +%% +%% ``` +%% $ rebar3 hex build docs +%% ''' +%% +%%

Configuration

+%% Packages are configured via `src/.app.src' attributes. +%% +%% == Required configuration == +%% +%% + +%% +%% == Optional configuration == +%% In addition, the following meta attributes are supported and highly recommended : +%% +%% +%% +%% Below is an example : +%% +%% ```erlang +%% {application, myapp, +%% [{description, "An Erlang/OTP application"}, +%% {vsn, "0.1.0"}, +%% {modules, []}, +%% {registered, []}, +%% {applications, [kernel, +%% stdlib, +%% ]}, +%% {licenses, ["Apache-2.0"]}, +%% {links, [{"GitHub", "https://github.com/my_name/myapp"}]}]}. +%% ``` +%% +%%

Command line options

+%% +%% + + + -module(rebar3_hex_build). -export([create_package/3, create_docs/3, create_docs/4]). @@ -21,8 +106,136 @@ "NOTICE" ]). +-define(DEPS, [{default, lock}]). +-define(PROVIDER, build). -define(DEFAULT_DOC_DIR, "doc"). +-export([ + init/1, + do/1, + format_error/1 +]). + +%% =================================================================== +%% Public API +%% =================================================================== + +%% @private +-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. +init(State) -> + Provider = providers:create([ + {name, ?PROVIDER}, + {module, ?MODULE}, + {namespace, hex}, + {bare, true}, + {deps, ?DEPS}, + {example, "rebar3 hex build"}, + {short_desc, "Builds a new local version of your package and docs."}, + {desc, ""}, + {opts, [ + rebar3_hex:repo_opt(), + {app, $a, "app", {string, undefined}, "HALP!"}, + {output_dir, $o, "output", {string, undefined}, "HALP!"}, + {unpack, $u, "unpack", {boolean, false}, "HALP!"} + ]} + ]), + State1 = rebar_state:add_provider(State, Provider), + {ok, State1}. + +%% @private +-spec do(rebar_state:t()) -> {ok, rebar_state:t()}. +do(State) -> + case rebar3_hex:task_state(State) of + {ok, Task} -> + handle_task(Task); + {error, Reason} -> + ?RAISE(Reason) + end. + +%% @private +-spec format_error(any()) -> iolist(). +format_error({build_package, Error}) when is_list(Error) -> + io_lib:format("Error building package : ~ts", [Error]); + +format_error({build_docs, Error}) when is_list(Error) -> + io_lib:format("Error building docs : ~ts", [Error]); + +format_error(Reason) -> + rebar3_hex_error:format_error(Reason). + +handle_task(#{state := State, repo := Repo, apps := [App], args := #{task := docs} = Args}) -> + case create_docs(State, Repo, App) of + {ok, Docs} -> + AbsDir = write_or_unpack(Docs, Args), + rebar3_hex_io:say("Your docs can be inspected at ~ts", [AbsDir]), + {ok, State}; + Error -> + ?RAISE({build_docs, Error}) + end; + +handle_task(#{state := State, repo := Repo, apps := [App], args := #{task := package} = Args}) -> + case create_package(State, Repo, App) of + {ok, Pkg} -> + AbsDir = write_or_unpack(Pkg, Args), + rebar3_hex_io:say("Your package contents can be inspected at ~ts", [AbsDir]), + {ok, State}; + Error -> + ?RAISE({build_package, Error}) + end; + +handle_task(#{state := State, repo := Repo, apps := [App], args := Args}) -> + case create_package(State, Repo, App) of + {ok, Pkg} -> + AbsOutput = write_or_unpack(Pkg, Args), + rebar3_hex_io:say("Your package tarball is available at ~ts", [AbsOutput]), + case create_docs(State, Repo, App) of + {ok, Docs} -> + AbsFile = write_or_unpack(Docs, Args), + rebar3_hex_io:say("Your docs tarball is available at ~ts", [AbsFile]), + {ok, State}; + Error -> + ?RAISE({build_docs, Error}) + end; + Error -> + ?RAISE({build_package, Error}) + end. + +output_path(docs, Name, Version, #{unpack := true}) -> + io_lib:format("~ts-~ts-docs", [Name, Version]); +output_path(docs, Name, Version, _Args) -> + io_lib:format("~ts-~ts-docs.tar", [Name, Version]); +output_path(package, Name, Version, #{unpack := true}) -> + io_lib:format("~ts-~ts", [Name, Version]); +output_path(package, Name, Version, _Args) -> + io_lib:format("~ts-~ts.tar", [Name, Version]). + +write_or_unpack(#{type := Type, tarball := Tarball, name := Name, version := Version}, Args) -> + OutputDir = output_dir(Args), + Out = output_path(Type, Name, Version, Args), + AbsOut = filename:join(OutputDir, Out), + case Args of + #{unpack := true} -> + file:make_dir(AbsOut), + case Type of + docs -> + hex_tarball:unpack_docs(Tarball, AbsOut); + package -> + hex_tarball:unpack(Tarball, AbsOut) + end; + _ -> + file:write_file(AbsOut, Tarball) + end, + AbsOut. + +output_dir(#{output_dir := undefined}) -> + {ok, Cwd} = file:get_cwd(), + Cwd; +output_dir(#{output_dir := Output}) -> + filename:absname(Output); +output_dir(_) -> + {ok, Cwd} = file:get_cwd(), + Cwd. + create_package(State, #{name := RepoName} = _Repo, App) -> Name = rebar_app_info:name(App), Version = rebar3_hex_app:vcs_vsn(State, App), @@ -68,6 +281,7 @@ create_package(State, #{name := RepoName} = _Repo, App) -> Err; Tarball -> Package = #{ + type => package, name => PkgName, repo_name => RepoName, deps => Deps1, @@ -108,7 +322,7 @@ include_files(Name, AppDir, AppDetails) -> FilePaths = proplists:get_value(files, AppDetails, ?DEFAULT_FILES), IncludeFilePaths = proplists:get_value(include_files, AppDetails, []), ExcludeFilePaths = proplists:get_value(exclude_files, AppDetails, []), - ExcludeRes = proplists:get_value(exclude_regexps, AppDetails, []), + ExcludeRes = proplists:get_value(exclude_patterns, AppDetails, []), AllFiles = lists:ukeysort(2, rebar3_hex_file:expand_paths(FilePaths, AppDir)), IncludeFiles = lists:ukeysort(2, rebar3_hex_file:expand_paths(IncludeFilePaths, AppDir)), @@ -179,9 +393,9 @@ create_docs(State, Repo, App, Args) -> case create_docs_tarball(FileList) of {ok, Tarball} -> {ok, #{ - tarball => Tarball, name => binarify(PkgName), vsn => binarify(Vsn) + type => docs, tarball => Tarball, name => binarify(PkgName), version => binarify(Vsn) }}; - {error, Reason} -> + {error, Reason} -> {error, hex_tarball:format_error(Reason)}; Err -> Err diff --git a/src/rebar3_hex_publish.erl b/src/rebar3_hex_publish.erl index d083e6e7..142224ee 100644 --- a/src/rebar3_hex_publish.erl +++ b/src/rebar3_hex_publish.erl @@ -413,7 +413,7 @@ hex_opts(Opts) -> %%% =================================================================== publish_docs(State, Repo, App, Args) -> - #{tarball := Tar, name := Name, vsn := Vsn} = create_docs(State, Repo, App, Args), + #{tarball := Tar, name := Name, version := Vsn} = create_docs(State, Repo, App, Args), case Args of #{dry_run := true} -> rebar_api:info("--dry-run enabled : will not publish docs.", []),