|
|
@ -1,129 +1,322 @@ |
|
|
|
Contributing to rebar |
|
|
|
--------------------- |
|
|
|
# Contributing to Rebar3 |
|
|
|
|
|
|
|
Before implementing a new feature, please submit a ticket to discuss your plans. |
|
|
|
The feature might have been rejected already, or the implementation might already be decided. |
|
|
|
1. [License](#license) |
|
|
|
2. [Submitting a bug](#submitting-a-bug) |
|
|
|
3. [Requesting or implementing a feature](#requesting-or-implementing-a-feature) |
|
|
|
4. [Project Structure](#project-structure) |
|
|
|
5. [Tests](#tests) |
|
|
|
6. [Submitting your changes](#submitting-your-changes) |
|
|
|
1. [Code Style](#code-style) |
|
|
|
2. [Committing your changes](#committing-your-changes) |
|
|
|
3. [Pull Requests and Branching](#pull-requests-and-branching) |
|
|
|
4. [Credit](#credit) |
|
|
|
|
|
|
|
See [Community and Resources](README.md#community-and-resources). |
|
|
|
## License ## |
|
|
|
|
|
|
|
Code style |
|
|
|
---------- |
|
|
|
Rebar3 is licensed under the [Apache License 2.0](LICENSE) for all new code. |
|
|
|
However, since it is built from older code bases, some files still hold other |
|
|
|
free licenses (such as BSD). Where it is the case, the license is added in |
|
|
|
comments. |
|
|
|
|
|
|
|
All files without specific headers can safely be assumed to be under Apache |
|
|
|
2.0. |
|
|
|
|
|
|
|
## Submitting a Bug |
|
|
|
|
|
|
|
Bugs can be submitted to the [Github issue page](https://github.com/rebar/rebar3/issues). |
|
|
|
|
|
|
|
Rebar3 is not perfect software and will be buggy. When submitting a bug, be |
|
|
|
careful to know the following: |
|
|
|
|
|
|
|
- The Erlang version you are running |
|
|
|
- The Rebar3 version you are using |
|
|
|
- The command you were attempting to run |
|
|
|
|
|
|
|
This information can be automatically generated to put into your bug report |
|
|
|
by calling `rebar3 report "my command"`. |
|
|
|
|
|
|
|
You may be asked for further information regarding: |
|
|
|
|
|
|
|
- Your environment, including the Erlang version used to compile rebar3, |
|
|
|
details about your operating system, where your copy of Erlang was installed |
|
|
|
from, and so on; |
|
|
|
- Your project, including its structure, and possibly to remove build |
|
|
|
artifacts to start from a fresh build |
|
|
|
- What it is you are trying to do exactly; we may provide alternative |
|
|
|
means to do so. |
|
|
|
|
|
|
|
If you can provide an example code base to reproduce the issue on, we will |
|
|
|
generally be able to provide more help, and faster. |
|
|
|
|
|
|
|
All contributors and rebar3 maintainers are generally unpaid developers |
|
|
|
working on the project in their own free time with limited resources. We |
|
|
|
ask for respect and understanding and will try to provide the same back. |
|
|
|
|
|
|
|
## Requesting or implementing a feature |
|
|
|
|
|
|
|
Before requesting or implementing a new feature, please do the following: |
|
|
|
|
|
|
|
- Take a look at our [list of plugins](http://www.rebar3.org/docs/using-available-plugins) |
|
|
|
to know if the feature isn't already supported by the community. |
|
|
|
- Verify in existing [tickets](https://github.com/rebar/rebar3/issues) whether |
|
|
|
the feature might already is in the works, has been moved to a plugin, or |
|
|
|
has already been rejected. |
|
|
|
|
|
|
|
If this is done, open up a ticket. Tell us what is the feature you want, |
|
|
|
why you need it, and why you think it should be in rebar3 itself. |
|
|
|
|
|
|
|
We may discuss details with you regarding the implementation, its inclusion |
|
|
|
within the project or as a plugin. Depending on the feature, we may provide |
|
|
|
full support for it, or ask you to help implement and/or commit to maintaining |
|
|
|
it in the future. We're dedicated to providing a stable build tool, and may |
|
|
|
also ask features to exist as a plugin before being included in core rebar3 -- |
|
|
|
the migration path from one to the other is fairly simple and little to no code |
|
|
|
needs rewriting. |
|
|
|
|
|
|
|
## Project Structure |
|
|
|
|
|
|
|
Rebar3 is an escript built around the concept of providers. Providers are the |
|
|
|
modules that do the work to fulfill a user's command. They are documented in |
|
|
|
[the official documentation website](http://www.rebar3.org/docs/plugins#section-provider-interface). |
|
|
|
|
|
|
|
Example provider: |
|
|
|
|
|
|
|
```erlang |
|
|
|
-module(rebar_prv_something). |
|
|
|
|
|
|
|
-behaviour(rebar_provider). |
|
|
|
|
|
|
|
-export([init/1, |
|
|
|
do/1, |
|
|
|
format_error/1]). |
|
|
|
|
|
|
|
-define(PROVIDER, something). |
|
|
|
-define(DEPS, []). |
|
|
|
|
|
|
|
%% =================================================================== |
|
|
|
%% Public API |
|
|
|
%% =================================================================== |
|
|
|
|
|
|
|
-spec init(rebar_state:state()) -> {ok, rebar_state:state()}. |
|
|
|
init(State) -> |
|
|
|
State1 = rebar_state:add_provider(State, rebar_provider:create([ |
|
|
|
{name, ?PROVIDER}, |
|
|
|
{module, ?MODULE}, |
|
|
|
{bare, true}, |
|
|
|
{deps, ?DEPS}, |
|
|
|
{example, "rebar dummy"}, |
|
|
|
{short_desc, "dummy plugin."}, |
|
|
|
{desc, ""}, |
|
|
|
{opts, []} |
|
|
|
])), |
|
|
|
{ok, State1}. |
|
|
|
|
|
|
|
The following rules apply: |
|
|
|
* Do not introduce trailing whitespace |
|
|
|
* We use spaces for indenting only |
|
|
|
* Try not to introduce lines longer than 80 characters |
|
|
|
* Write small functions whenever possible |
|
|
|
* Avoid having too many clauses containing clauses containing clauses. |
|
|
|
Basically, avoid deeply nested functions. |
|
|
|
-spec do(rebar_state:state()) -> {ok, rebar_state:state()}. |
|
|
|
do(State) -> |
|
|
|
%% Do something |
|
|
|
{ok, State}. |
|
|
|
|
|
|
|
Follow the indentation style of existing files. The [erlang-mode for |
|
|
|
(emacs)](http://www.erlang.org/doc/man/erlang.el.html) indentation is going to |
|
|
|
always work. Other users may want to use 4-width spaces and make sure things |
|
|
|
align mostly the way they would with Emacs code, or with the rest of the |
|
|
|
project. |
|
|
|
-spec format_error(any()) -> iolist(). |
|
|
|
format_error(Reason) -> |
|
|
|
io_lib:format("~p", [Reason]). |
|
|
|
``` |
|
|
|
|
|
|
|
Where possible, include type specifications for your code so type analysis |
|
|
|
will be as accurate as possible. |
|
|
|
Providers are then listed in `rebar.app.src`, and can be called from |
|
|
|
the command line or as a programmatical API. |
|
|
|
|
|
|
|
Please add comments around tricky fixes or workarounds so that we can |
|
|
|
easily know why they're there at a glance. |
|
|
|
All commands are therefore implemented in standalone modules. If you call |
|
|
|
`rebar3 <task>`, the module in charge of it is likely located in |
|
|
|
`src/rebar_prv_<task>.erl`. |
|
|
|
|
|
|
|
Pull requests and branching |
|
|
|
--------------------------- |
|
|
|
Templates are included in `priv/templates/` |
|
|
|
|
|
|
|
All fixes to rebar end up requiring a +1 from one or more of the project's |
|
|
|
maintainers. When opening a pull request, explain what the patch is doing |
|
|
|
and if it makes sense, why the proposed implementation was chosen. |
|
|
|
The official test suite is Common Test, and tests are located in `test/`. |
|
|
|
|
|
|
|
Try to use well-defined commits (one feature per commit) so that reading |
|
|
|
them and testing them is easier for reviewers and while bissecting the code |
|
|
|
base for issues. |
|
|
|
Useful modules include: |
|
|
|
- `rebar_api`, providing an interface for plugins to call into core rebar3 |
|
|
|
functionality |
|
|
|
- `rebar_core`, for initial boot and setup of a project |
|
|
|
- `rebar_config`, handling the configuration of each project. |
|
|
|
- `rebar_app_info`, giving access to the metadata of a specific OTP application |
|
|
|
in a project. |
|
|
|
- `rebar_base_compiler`, giving a uniform interface to compile `.erl` files. |
|
|
|
- `rebar_dir` for directory handling and management |
|
|
|
- `rebar_file_util` for cross-platform file handling |
|
|
|
- `rebar_state`, the glue holding together a specific build or task run; |
|
|
|
includes canonical versions of the configuration, profiles, applications, |
|
|
|
dependencies, and so on. |
|
|
|
- `rebar_utils` for generic tasks and functionality required across |
|
|
|
multiple providers or modules. |
|
|
|
|
|
|
|
During the review process, you may be asked to correct or edit a few things |
|
|
|
before a final rebase to merge things. |
|
|
|
|
|
|
|
Please work in feature branches, and do not commit to `master` in your fork. |
|
|
|
|
|
|
|
Provide a clean branch without merge commits. |
|
|
|
## Tests |
|
|
|
|
|
|
|
Tests |
|
|
|
----- |
|
|
|
Rebar3 tries to have as many of its features tested as possible. Everything |
|
|
|
that a user can do and should be repeatable in any way should be tested. |
|
|
|
|
|
|
|
As a general rule, any behavioral change to rebar requires a test to go with |
|
|
|
it. If there's already a test case, you may have to modify that one. If there |
|
|
|
isn't a test case or a test suite, add a new test case or suite in `test/`. |
|
|
|
Tests are written using the Common Test framework. Tests for rebar3 can be run |
|
|
|
by calling: |
|
|
|
|
|
|
|
To run the tests: |
|
|
|
|
|
|
|
```sh |
|
|
|
$ ./bootstrap |
|
|
|
```bash |
|
|
|
$ rebar3 escriptize # or bootstrap |
|
|
|
$ ./rebar3 ct |
|
|
|
``` |
|
|
|
|
|
|
|
The rebar3 test suite is written using Common Test. As many of the tests as |
|
|
|
possible are written by using the programmatic rebar3 API rather than |
|
|
|
by running the escriptized project directly. The tests should be restricted |
|
|
|
to their `priv_dir` and leave the system clean after a run. |
|
|
|
Most tests are named according to their module name followed by the `_SUITE` |
|
|
|
suffi. Providers are made shorter, such that `rebar_prv_new` is tested in |
|
|
|
`rebar_new_SUITE`. |
|
|
|
|
|
|
|
Most tests in the test suite will rely on calling Rebar3 in its API form, |
|
|
|
then investigating the build output. Because most tests have similar |
|
|
|
requirements, the `test/rebar_test_utils` file contains common code |
|
|
|
to set up test projects, run tasks, and verify artifacts at once. |
|
|
|
|
|
|
|
A basic example can look like: |
|
|
|
|
|
|
|
```erlang |
|
|
|
-module(rebar_some_SUITE). |
|
|
|
-compile(export_all). |
|
|
|
-include_lib("common_test/include/ct.hrl"). |
|
|
|
-include_lib("eunit/include/eunit.hrl"). |
|
|
|
|
|
|
|
all() -> [checks_success, checks_failure]. |
|
|
|
|
|
|
|
init_per_testcase(Case, Config0) -> |
|
|
|
%% Create a project directory in the test run's priv_dir |
|
|
|
Config = rebar_test_utils:init_rebar_state(Config0), |
|
|
|
%% Create toy applications |
|
|
|
AppDir = ?config(apps, Config), |
|
|
|
Name = rebar_test_utils:create_random_name("app1_"++atom_to_list(Case)), |
|
|
|
Vsn = rebar_test_utils:create_random_vsn(), |
|
|
|
rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), |
|
|
|
%% Add the data to the test config |
|
|
|
[{name, Name} | Config]. |
|
|
|
|
|
|
|
end_per_testcase(_, Config) -> |
|
|
|
Config. |
|
|
|
|
|
|
|
checks_success(Config) -> |
|
|
|
%% Validates that the application in `name' is successfully compiled |
|
|
|
Name = ?config(name, Config), |
|
|
|
rebar_test_utils:run_and_check(Config, [], |
|
|
|
["compile"], |
|
|
|
{ok, [{app, Name}]}). |
|
|
|
|
|
|
|
checks_failure(Config) -> |
|
|
|
%% Checks that a result fails |
|
|
|
Command = ["fakecommand", "fake-arg"], |
|
|
|
rebar_test_utils:run_and_check( |
|
|
|
Config, [], Command, |
|
|
|
{error, io_lib:format("Command ~p not found", [fakecommand])} |
|
|
|
). |
|
|
|
``` |
|
|
|
|
|
|
|
If such tests prove hard to write, you can ask for help doing that in your |
|
|
|
pull request. |
|
|
|
The general interface to `rebar_test_utils:run_and_check` is |
|
|
|
`run_and_check(CTConfig, RebarConfig, Command, Expect)` where `Expect` can |
|
|
|
be any of: |
|
|
|
|
|
|
|
```erlang |
|
|
|
{ok, OKRes} |
|
|
|
{ok, OKRes, ProfilesUsed} |
|
|
|
{error, Reason} |
|
|
|
|
|
|
|
% where: |
|
|
|
ProfilesUsed :: string() % matching the profiles to validate (defaults to "*") |
|
|
|
OKRes :: {app, Name} % name of an app that is in the build directory |
|
|
|
| {app, Name, valid} % name of an app that is in the build directory and compiled properly |
|
|
|
| {app, Name, invalid} % name of an app that didn't compile properly |
|
|
|
| {dep, Name} % name of a dependency in the build directory |
|
|
|
| {dep, Name, Vsn} % name of a dependency in the build directory with a specific version |
|
|
|
| {dep_not_exist, Name} % name of a dependency missing from the build directory |
|
|
|
| {checkout, Name} % name of an app that is a checkout dependency |
|
|
|
| {plugin, Name} % name of a plugin in the build directory |
|
|
|
| {plugin, Name, Vsn} % name of a plugin in the build directory with a specific version |
|
|
|
| {global_plugin, Name} % name of a global plugin in the build directory |
|
|
|
| {global_plugin, Name, Vsn} % name of a global plugin in the build directory with a specific version |
|
|
|
| {lock, Name} % name of a locked dependency |
|
|
|
| {lock, Name, Vsn} % name of a locked dependency of a specific version |
|
|
|
| {lock, pkg, Name, Vsn}% name of a locked package of a specific version |
|
|
|
| {lock, src, Name, Vsn}% name of a locked source dependency of a specific version |
|
|
|
| {release, Name, Vsn, ExpectedDevMode} % validates a release |
|
|
|
| {tar, Name, Vsn} % validates a tarball's existence |
|
|
|
| {file, Filename} % validates the presence of a given file |
|
|
|
| {dir, Dirname} % validates the presence of a given directory |
|
|
|
Reason :: term() % the exception thrown by rebar3 |
|
|
|
``` |
|
|
|
|
|
|
|
For tests having a lot to do with I/O and terminal interaction, consider |
|
|
|
adding them to https://github.com/tsloughter/rebar3_tests |
|
|
|
This generally lets most features be tested fine. Ask for help if you cannot |
|
|
|
figure out how to write tests for your feature or patch. |
|
|
|
|
|
|
|
## Submitting your changes |
|
|
|
|
|
|
|
Credit |
|
|
|
------ |
|
|
|
While we're not too formal when it comes to pull requests to the project, |
|
|
|
we do appreciate users taking the time to conform to the guidelines that |
|
|
|
follow. |
|
|
|
|
|
|
|
To give everyone proper credit in addition to the git history, please feel free to append |
|
|
|
your name to `THANKS` in your first contribution. |
|
|
|
We do expect all pull requests submitted to come with [tests](#tests) before |
|
|
|
they are merged. If you cannot figure out how to write your tests properly, ask |
|
|
|
in the pull request for guidance. |
|
|
|
|
|
|
|
### Code Style |
|
|
|
|
|
|
|
Committing your changes |
|
|
|
----------------------- |
|
|
|
* Do not introduce trailing whitespace |
|
|
|
* Indentation is 4 spaces wide, no tabs. |
|
|
|
* Try not to introduce lines longer than 80 characters |
|
|
|
* Write small functions whenever possible, and use descriptive names for |
|
|
|
functions and variables. |
|
|
|
* Avoid having too many clauses containing clauses containing clauses. |
|
|
|
Basically, avoid deeply nested `case ... of` or `try ... catch` expressions. |
|
|
|
Break them out into functions if possible. |
|
|
|
* Comment tricky or non-obvious decisions made to explain their rationale. |
|
|
|
|
|
|
|
Please ensure that all commits pass all tests, and do not have extra Dialyzer warnings. |
|
|
|
To do that run `./rebar3 ct` and `./rebar3 as dialyze dialyzer`. |
|
|
|
### Committing your changes |
|
|
|
|
|
|
|
#### Structuring your commits |
|
|
|
It helps if your commits are structured as follows: |
|
|
|
|
|
|
|
- Fixing a bug is one commit. |
|
|
|
- Adding a feature is one commit. |
|
|
|
- Adding two features is two commits. |
|
|
|
- Two unrelated changes is two commits (and likely two Pull requests) |
|
|
|
|
|
|
|
If you fix a (buggy) commit, squash (`git rebase -i`) the changes as a fixup commit into |
|
|
|
the original commit. |
|
|
|
If you fix a (buggy) commit, squash (`git rebase -i`) the changes as a fixup |
|
|
|
commit into the original commit, unless the patch was following a |
|
|
|
maintainer's code review. In such cases, it helps to have separate commits. |
|
|
|
|
|
|
|
#### Writing Commit Messages |
|
|
|
The reviewer may ask you to later squash the commits together to provide |
|
|
|
a clean commit history before merging in the feature. |
|
|
|
|
|
|
|
It's important to write a proper commit title and description. The commit title must be |
|
|
|
at most 50 characters; it is the first line of the commit text. The second line of the |
|
|
|
commit text must be left blank. The third line and beyond is the commit message. You |
|
|
|
should write a commit message. If you do, wrap all lines at 72 characters. You should |
|
|
|
explain what the commit does, what references you used, and any other information |
|
|
|
that helps understanding your changes. |
|
|
|
It's important to write a proper commit title and description. The commit title |
|
|
|
should fir around 50 characters; it is the first line of the commit text. The |
|
|
|
second line of the commit text must be left blank. The third line and beyond is |
|
|
|
the commit message. You should write a commit message. If you do, wrap all |
|
|
|
lines at 72 characters. You should explain what the commit does, what |
|
|
|
references you used, and any other information that helps understanding your |
|
|
|
changes. |
|
|
|
|
|
|
|
Basically, structure your commit message like this: |
|
|
|
### Pull Requests and Branching |
|
|
|
|
|
|
|
<pre> |
|
|
|
One line summary (at most 50 characters) |
|
|
|
All fixes to rebar end up requiring a +1 from one or more of the project's |
|
|
|
maintainers. When opening a pull request, explain what the patch is doing |
|
|
|
and if it makes sense, why the proposed implementation was chosen. |
|
|
|
|
|
|
|
Longer description (wrap at 72 characters) |
|
|
|
</pre> |
|
|
|
Try to use well-defined commits (one feature per commit) so that reading |
|
|
|
them and testing them is easier for reviewers and while bissecting the code |
|
|
|
base for issues. |
|
|
|
|
|
|
|
##### Commit title/summary |
|
|
|
During the review process, you may be asked to correct or edit a few things |
|
|
|
before a final rebase to merge things. Do send edits as individual commits |
|
|
|
to allow for gradual and partial reviews to be done by reviewers. Once the +1s |
|
|
|
are given, rebasing is appreciated but not mandatory. |
|
|
|
|
|
|
|
* At most 50 characters |
|
|
|
* What was changed |
|
|
|
* Imperative present tense (Fix, Add, Change) |
|
|
|
* `Fix bug 123` |
|
|
|
* `Add 'foobar' command` |
|
|
|
* `Change default timeout to 123` |
|
|
|
* No period |
|
|
|
Please work in feature branches, and do not commit to `master` in your fork. |
|
|
|
|
|
|
|
Provide a clean branch without merge commits. |
|
|
|
|
|
|
|
##### Commit description |
|
|
|
If you can, pick a descriptive title for your pull request. When we generate |
|
|
|
changelogs before cutting a release, a script uses the pull request names |
|
|
|
to populate the entries. |
|
|
|
|
|
|
|
* Wrap at 72 characters |
|
|
|
* Why, explain intention and implementation approach |
|
|
|
* Present tense |
|
|
|
|
|
|
|
### Credit |
|
|
|
|
|
|
|
To give everyone proper credit in addition to the git history, please feel free to append |
|
|
|
your name to `THANKS` in your first contribution. |