Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

322 строки
12 KiB

9 лет назад
9 лет назад
9 лет назад
9 лет назад
9 лет назад
9 лет назад
9 лет назад
9 лет назад
9 лет назад
9 лет назад
  1. # Contributing to Rebar3
  2. 1. [License](#license)
  3. 2. [Submitting a bug](#submitting-a-bug)
  4. 3. [Requesting or implementing a feature](#requesting-or-implementing-a-feature)
  5. 4. [Project Structure](#project-structure)
  6. 5. [Tests](#tests)
  7. 6. [Submitting your changes](#submitting-your-changes)
  8. 1. [Code Style](#code-style)
  9. 2. [Committing your changes](#committing-your-changes)
  10. 3. [Pull Requests and Branching](#pull-requests-and-branching)
  11. 4. [Credit](#credit)
  12. ## License ##
  13. Rebar3 is licensed under the [Apache License 2.0](LICENSE) for all new code.
  14. However, since it is built from older code bases, some files still hold other
  15. free licenses (such as BSD). Where it is the case, the license is added in
  16. comments.
  17. All files without specific headers can safely be assumed to be under Apache
  18. 2.0.
  19. ## Submitting a Bug
  20. Bugs can be submitted to the [Github issue page](https://github.com/erlang/rebar3/issues).
  21. Rebar3 is not perfect software and will be buggy. When submitting a bug, be
  22. careful to know the following:
  23. - The Erlang version you are running
  24. - The Rebar3 version you are using
  25. - The command you were attempting to run
  26. This information can be automatically generated to put into your bug report
  27. by calling `rebar3 report "my command"`.
  28. You may be asked for further information regarding:
  29. - Your environment, including the Erlang version used to compile rebar3,
  30. details about your operating system, where your copy of Erlang was installed
  31. from, and so on;
  32. - Your project, including its structure, and possibly to remove build
  33. artifacts to start from a fresh build
  34. - What it is you are trying to do exactly; we may provide alternative
  35. means to do so.
  36. If you can provide an example code base to reproduce the issue on, we will
  37. generally be able to provide more help, and faster.
  38. All contributors and rebar3 maintainers are generally unpaid developers
  39. working on the project in their own free time with limited resources. We
  40. ask for respect and understanding and will try to provide the same back.
  41. ## Requesting or implementing a feature
  42. Before requesting or implementing a new feature, please do the following:
  43. - Take a look at our [list of plugins](http://www.rebar3.org/docs/using-available-plugins)
  44. to know if the feature isn't already supported by the community.
  45. - Verify in existing [tickets](https://github.com/erlang/rebar3/issues) whether
  46. the feature might already is in the works, has been moved to a plugin, or
  47. has already been rejected.
  48. If this is done, open up a ticket. Tell us what is the feature you want,
  49. why you need it, and why you think it should be in rebar3 itself.
  50. We may discuss details with you regarding the implementation, its inclusion
  51. within the project or as a plugin. Depending on the feature, we may provide
  52. full support for it, or ask you to help implement and/or commit to maintaining
  53. it in the future. We're dedicated to providing a stable build tool, and may
  54. also ask features to exist as a plugin before being included in core rebar3 --
  55. the migration path from one to the other is fairly simple and little to no code
  56. needs rewriting.
  57. ## Project Structure
  58. Rebar3 is an escript built around the concept of providers. Providers are the
  59. modules that do the work to fulfill a user's command. They are documented in
  60. [the official documentation website](http://www.rebar3.org/docs/plugins#section-provider-interface).
  61. Example provider:
  62. ```erlang
  63. -module(rebar_prv_something).
  64. -behaviour(rebar_provider).
  65. -export([init/1,
  66. do/1,
  67. format_error/1]).
  68. -define(PROVIDER, something).
  69. -define(DEPS, []).
  70. %% ===================================================================
  71. %% Public API
  72. %% ===================================================================
  73. -spec init(rebar_state:state()) -> {ok, rebar_state:state()}.
  74. init(State) ->
  75. State1 = rebar_state:add_provider(State, rebar_provider:create([
  76. {name, ?PROVIDER},
  77. {module, ?MODULE},
  78. {bare, true},
  79. {deps, ?DEPS},
  80. {example, "rebar dummy"},
  81. {short_desc, "dummy plugin."},
  82. {desc, ""},
  83. {opts, []}
  84. ])),
  85. {ok, State1}.
  86. -spec do(rebar_state:state()) -> {ok, rebar_state:state()}.
  87. do(State) ->
  88. %% Do something
  89. {ok, State}.
  90. -spec format_error(any()) -> iolist().
  91. format_error(Reason) ->
  92. io_lib:format("~p", [Reason]).
  93. ```
  94. Providers are then listed in `rebar.app.src`, and can be called from
  95. the command line or as a programmatical API.
  96. All commands are therefore implemented in standalone modules. If you call
  97. `rebar3 <task>`, the module in charge of it is likely located in
  98. `src/rebar_prv_<task>.erl`.
  99. Templates are included in `priv/templates/`
  100. The official test suite is Common Test, and tests are located in `test/`.
  101. Useful modules include:
  102. - `rebar_api`, providing an interface for plugins to call into core rebar3
  103. functionality
  104. - `rebar_core`, for initial boot and setup of a project
  105. - `rebar_config`, handling the configuration of each project.
  106. - `rebar_app_info`, giving access to the metadata of a specific OTP application
  107. in a project.
  108. - `rebar_base_compiler`, giving a uniform interface to compile `.erl` files.
  109. - `rebar_dir` for directory handling and management
  110. - `rebar_file_util` for cross-platform file handling
  111. - `rebar_state`, the glue holding together a specific build or task run;
  112. includes canonical versions of the configuration, profiles, applications,
  113. dependencies, and so on.
  114. - `rebar_utils` for generic tasks and functionality required across
  115. multiple providers or modules.
  116. ## Tests
  117. Rebar3 tries to have as many of its features tested as possible. Everything
  118. that a user can do and should be repeatable in any way should be tested.
  119. Tests are written using the Common Test framework. Tests for rebar3 can be run
  120. by calling:
  121. ```bash
  122. $ rebar3 escriptize # or bootstrap
  123. $ ./rebar3 ct
  124. ```
  125. Most tests are named according to their module name followed by the `_SUITE`
  126. suffix. Providers are made shorter, such that `rebar_prv_new` is tested in
  127. `rebar_new_SUITE`.
  128. Most tests in the test suite will rely on calling Rebar3 in its API form,
  129. then investigating the build output. Because most tests have similar
  130. requirements, the `test/rebar_test_utils` file contains common code
  131. to set up test projects, run tasks, and verify artifacts at once.
  132. A basic example can look like:
  133. ```erlang
  134. -module(rebar_some_SUITE).
  135. -compile(export_all).
  136. -include_lib("common_test/include/ct.hrl").
  137. -include_lib("eunit/include/eunit.hrl").
  138. all() -> [checks_success, checks_failure].
  139. init_per_testcase(Case, Config0) ->
  140. %% Create a project directory in the test run's priv_dir
  141. Config = rebar_test_utils:init_rebar_state(Config0),
  142. %% Create toy applications
  143. AppDir = ?config(apps, Config),
  144. Name = rebar_test_utils:create_random_name("app1_"++atom_to_list(Case)),
  145. Vsn = rebar_test_utils:create_random_vsn(),
  146. rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
  147. %% Add the data to the test config
  148. [{name, Name} | Config].
  149. end_per_testcase(_, Config) ->
  150. Config.
  151. checks_success(Config) ->
  152. %% Validates that the application in `name' is successfully compiled
  153. Name = ?config(name, Config),
  154. rebar_test_utils:run_and_check(Config, [],
  155. ["compile"],
  156. {ok, [{app, Name}]}).
  157. checks_failure(Config) ->
  158. %% Checks that a result fails
  159. Command = ["fakecommand", "fake-arg"],
  160. rebar_test_utils:run_and_check(
  161. Config, [], Command,
  162. {error, io_lib:format("Command ~p not found", [fakecommand])}
  163. ).
  164. ```
  165. The general interface to `rebar_test_utils:run_and_check` is
  166. `run_and_check(CTConfig, RebarConfig, Command, Expect)` where `Expect` can
  167. be any of:
  168. ```erlang
  169. {ok, OKRes}
  170. {ok, OKRes, ProfilesUsed}
  171. {error, Reason}
  172. % where:
  173. ProfilesUsed :: string() % matching the profiles to validate (defaults to "*")
  174. OKRes :: {app, Name} % name of an app that is in the build directory
  175. | {app, Name, valid} % name of an app that is in the build directory and compiled properly
  176. | {app, Name, invalid} % name of an app that didn't compile properly
  177. | {dep, Name} % name of a dependency in the build directory
  178. | {dep, Name, Vsn} % name of a dependency in the build directory with a specific version
  179. | {dep_not_exist, Name} % name of a dependency missing from the build directory
  180. | {checkout, Name} % name of an app that is a checkout dependency
  181. | {plugin, Name} % name of a plugin in the build directory
  182. | {plugin, Name, Vsn} % name of a plugin in the build directory with a specific version
  183. | {global_plugin, Name} % name of a global plugin in the build directory
  184. | {global_plugin, Name, Vsn} % name of a global plugin in the build directory with a specific version
  185. | {lock, Name} % name of a locked dependency
  186. | {lock, Name, Vsn} % name of a locked dependency of a specific version
  187. | {lock, pkg, Name, Vsn}% name of a locked package of a specific version
  188. | {lock, src, Name, Vsn}% name of a locked source dependency of a specific version
  189. | {release, Name, Vsn, ExpectedDevMode} % validates a release
  190. | {tar, Name, Vsn} % validates a tarball's existence
  191. | {file, Filename} % validates the presence of a given file
  192. | {dir, Dirname} % validates the presence of a given directory
  193. Reason :: term() % the exception thrown by rebar3
  194. ```
  195. This generally lets most features be tested fine. Ask for help if you cannot
  196. figure out how to write tests for your feature or patch.
  197. ## Submitting your changes
  198. While we're not too formal when it comes to pull requests to the project,
  199. we do appreciate users taking the time to conform to the guidelines that
  200. follow.
  201. We do expect all pull requests submitted to come with [tests](#tests) before
  202. they are merged. If you cannot figure out how to write your tests properly, ask
  203. in the pull request for guidance.
  204. ### Code Style
  205. * Do not introduce trailing whitespace
  206. * Indentation is 4 spaces wide, no tabs.
  207. * Try not to introduce lines longer than 80 characters
  208. * Write small functions whenever possible, and use descriptive names for
  209. functions and variables.
  210. * Avoid having too many clauses containing clauses containing clauses.
  211. Basically, avoid deeply nested `case ... of` or `try ... catch` expressions.
  212. Break them out into functions if possible.
  213. * Comment tricky or non-obvious decisions made to explain their rationale.
  214. ### Committing your changes
  215. It helps if your commits are structured as follows:
  216. - Fixing a bug is one commit.
  217. - Adding a feature is one commit.
  218. - Adding two features is two commits.
  219. - Two unrelated changes is two commits (and likely two Pull requests)
  220. If you fix a (buggy) commit, squash (`git rebase -i`) the changes as a fixup
  221. commit into the original commit, unless the patch was following a
  222. maintainer's code review. In such cases, it helps to have separate commits.
  223. The reviewer may ask you to later squash the commits together to provide
  224. a clean commit history before merging in the feature.
  225. It's important to write a proper commit title and description. The commit title
  226. should be no more than 50 characters; it is the first line of the commit text. The
  227. second line of the commit text must be left blank. The third line and beyond is
  228. the commit message. You should write a commit message. If you do, wrap all
  229. lines at 72 characters. You should explain what the commit does, what
  230. references you used, and any other information that helps understanding your
  231. changes.
  232. ### Pull Requests and Branching
  233. All fixes to rebar end up requiring a +1 from one or more of the project's
  234. maintainers. When opening a pull request, explain what the patch is doing
  235. and if it makes sense, why the proposed implementation was chosen.
  236. Try to use well-defined commits (one feature per commit) so that reading
  237. them and testing them is easier for reviewers and while bisecting the code
  238. base for issues.
  239. During the review process, you may be asked to correct or edit a few things
  240. before a final rebase to merge things. Do send edits as individual commits
  241. to allow for gradual and partial reviews to be done by reviewers. Once the +1s
  242. are given, rebasing is appreciated but not mandatory.
  243. Please work in feature branches, and do not commit to `master` in your fork.
  244. Provide a clean branch without merge commits.
  245. If you can, pick a descriptive title for your pull request. When we generate
  246. changelogs before cutting a release, a script uses the pull request names
  247. to populate the entries.
  248. ### Credit
  249. To give everyone proper credit in addition to the git history, please feel free to append
  250. your name to `THANKS` in your first contribution.