Contributing guide
Thank you for considering to contribute to Rex! Volunteers like you make this free and open source project possible.
We consider it important to ensure everyone involved can make an efficient use of the time and effort they choose to invest. This guide aims to streamline communication around contributions by documenting the most frequently discussed aspects.
While most sections focus on the code contributions we typically receive, we encourage and welcome other types of contributions too. For example:
- improving documentation
- testing with different setups
- donating (access to) hardware
- submitting and triaging bug reports
- sharing your use cases and experience with Rex
- spreading the word via blogs, talks, and social media
- starring the project on GitHub
- adding Rex to your favorites on MetaCPAN
- listing Rex as part of your stack on StackShare
Project guidelines
We took many decisions around Rex since the first commit in 2010. We found some of the ideas coming up more often than others, and decided to share them here (in no particular order).
Getting help and support
If you need help with questions like “How do I…?” and “Why does it…?”, please start a discussion via one of our support channels.
We list options there for both community and commercial support.
Discussions, issues, and pull requests
In short, we follow these steps in that order: discuss first, track next, implement last.
- Discuss: first we discuss the relevant details of any change proposal idea, and decide on next steps.
- Track: when we decide to work on a change proposal, we open an issue to track progress, and design or refine potential solutions.
- Implement: we open pull requests as a last step to propose and review a specific implementation of the given idea.
Please use the templates we provide to streamline our collaboration in typical situations.
Making exceptions
We may choose and accept skipping one or more of the steps in exceptional cases, as long as the reasoning makes sense and gets well documented. When we find ourselves repeating the same exception, we will document it here as a standard case instead.
When you feel our workflow, guidelines, or templates does not fit your situation, please consider that a strong indicator to contact us early, and we’ll help figuring out the best course of action.
Rex core vs extending Rex
Strictly speaking, we consider the following the core competency of Rex: execute commands and manage files, by defining tasks and orchestrating their execution.
Rex gained lots of other capabilities over time, and historically many of them landed in core as well. This includes the capability to extend Rex without changing the core (or only minimally), for example when adding support to:
- new operating systems
- new shell types
- new virtualization methods
- new cloud providers
We strongly encourage to add such new capabilities via their own extension modules outside the core. In case of questions or concerns, see also the “Common scenarios” section, or contact us.
Supported operating systems
We expect Rex to run on operating systems wherever Perl can run. This includes Linux, BSDs, Mac OS X, Windows, Solaris, and possibly others.
We also expect Rex to manage endpoints running different operating systems. This primarily means Unix-like systems for now.
We welcome patches and guides about how to support Rex both to run on and manage even more operating systems.
In general, we aim to support Rex to run on and manage actively maintained platforms only. See also the “Deprecation policy” section for more details.
Supported Perl versions
Rex aims to run even on older Perl versions up to 10 years old. Currently this means 5.14.4.
On top of the supported minimum version of Perl, we aim to support the latest versions of all minor stable Perl 5 releases since then. That makes the full list the following:
- 5.14.4
- 5.16.3
- 5.18.4
- 5.20.3
- 5.22.4
- 5.24.4
- 5.26.3
- 5.28.3
- 5.30.3
- 5.32.1
- 5.34.1
- 5.36.1
- 5.38.2
- 5.40.1
Backwards compatibility
We aim to remain backwards compatible within major versions of Rex, as implied by following Dotted Semantic Versioning.
To introduce new features while staying backwards compatible, Rex also uses feature flags. This makes it possible to selectively opt-in to or opt-out from new features, depending on the needs of the given use case.
Feature flags named after the minor releases of Rex (for example 1.4
) enable
the collection of preferred settings for the given Rex version.
In general, we strive to add new feature flags in pairs for opting in and out.
Deprecation policy
We may drop features and code paths by following a planned deprecation procedure. Adequate warnings and an ample transition period should precede any such change.
If a supported operating system gets deprecated, we may try to keep it supported while it doesn't cause any problems. As soon as a retired operating system starts causing bugs, or gets in the way of progress, we consider it a candidate to drop from support.
In other words, while we may choose to stay lazy to keep the support around, we also don’t plan to put active effort into maintaining compatibility with something which got abandoned by its own creators.
In case you depend on a deprecated feature, or must use or manage a retired operating system, please consider contributing your solutions, or asking for community or commercial support for your use case.
Code layout
We use the latest version of Perl::Tidy
to format the Rex codebase according to the rules described in .perltidyrc
.
We expect all contributions to follow the same formatting rules, and we also
actively test for it.
To avoid unnecessary “tidy only” commits in contributions, we recommend to integrate a formatting step directly into your workflow. For example via a git pre-commit hook, a shortcut in the editor, or as an automatic action upon saving.
Importantly, we emphasize the importance of a consistent layout throughout the
codebase over any specific formatting rule. Since we include .perltidyrc
in
the repo, consider it as subject of contributions too.
Code quality
We use
Test::Perl::Critic::Progressive
to make sure every change follows the code quality rules in .perlcriticrc
,
and no contribution introduces any violations accidentally.
The dist.ini
file contains the list of perlcritic policy modules we use.
Since we include both .perlcriticrc
and dist.ini
in the repo, consider it
as subject of contributions too. In fact, we welcome improving both our
policies and the codebase in this regard.
Tests should pass
Rex has two major test suites:
- the tests included with the code, exercising different modules and features
- the historical tests in the RexOps/rex-build repo, making sure Rex can manage actual VMs running different operating systems
In general, we expect contributions to include their own tests for the proposed changes, while ensuring that previous tests still pass.
To make it easier to follow the guidelines of this document, we strive to have corresponding tests for them as well.
Have fun
We find it highly important to have fun while using Rex and contributing to it. If you have a different experience, then please let us know, as that probably indicates a bug somewhere.
Development tools
We use Git for version control of the source code, and host Rex code in a GitHub repository.
We recommend to use a separate Perl environment for development, like the ones you managed by Perlbrew or similar tools.
The code and examples use cpanm to install modules from CPAN.
We also use Dist::Zilla as an authoring tool.
Perltidy takes care of maintaining a consistent source code formatting.
Perlcritic makes sure the codebase follows best practices and adheres to our minimum quality requirements.
Dependencies
After cloning the source code, run the following commands to install dependencies:
dzil authordeps | cpanm
dzil listdeps | cpanm
While some optional dependencies may not support your platform, use this command to install these as well:
dzil listdeps --suggests | cpanm
Install the remaining dependencies specific to the operating system, and Rex itself with:
dzil install
To only install the dependencies specific to the operating system, run one of these commands:
- Windows:
cpanm Net::SSH2
- non-Windows:
cpanm Net::OpenSSH Net::SFTP::Foreign IO::Pty
When considering new dependencies, we prefer:
- Perl core modules
- CPAN modules already packaged by the operating systems distributing Rex
- CPAN modules supporting the same platforms Rex itself supports
We welcome and encourage contributions aimed at reducing the amount of Rex dependencies, for example by:
- removing unused dependencies
- consolidating similar dependencies into a single one
- replacing external dependencies with Perl code modules
Testing
Run the test suite included with the source code of Rex with prove
:
prove --lib --recurse t/
Extended, author, and release tests may need further dependencies. Install these with:
dzil listdeps --author --missing | cpanm
Then run those tests with:
dzil test --all
As an important step before modifying the codebase, please run the progressive perlcritic tests on the default branch:
rm -f xt/author/.perlcritic-history
AUTHOR_TESTING=1 prove --lib xt/author/critic-progressive.t
The first run should succeed, and records the current state. This serves as baseline to compare later runs against. Please see the Test::Perl::Critic::Progressive NOTES for more details.
Git workflow
We prefer receiving contributions by forking the repository on GitHub, and then sending pull requests against the default branch of the repository. If you have to use a different way, please contact us first to discuss alternative options.
Please read and follow the “Discussions, issues, and pull requests” section before investing effort in a pull request.
We recommend to use feature branches when working on contributions. This helps keeping the commits related to the change together, while separating them from the main line of development.
We consider a single commit ideal when it:
- represents a single logical change (atomic)
- has a useful commit message
- has the expected test suite result
We expect commit messages to follow the guidelines collected in the How to Write a Git Commit Message article, and we also test for most of those expectations.
We generally recommend to:
- follow a test-driven approach: add new breaking tests on a first commit, then change the code to fix them on a follow up commit, then refactoring on follow-up commits as needed
- simplify making larger changes: refactor on a separate commit first to make later changes simpler on smaller commits
- use atomic commits: use separate commits in a single pull request to separate the logical steps, and to help understanding the series of changes during review
- share your ideas and for feedback early: open draft pull requests
- keep your branch up-to-date: rebase your feature branch on top of our default branch whenever we made new commits while you work
- keep a tidy history: use follow-up/cleanup commits on the same PR, and squash or fixup related commits together in the feature branch, while keeping commits atomic (in other words, no need for “tidy only”, “fix typo”, and merge commits, while avoiding to squash everything into a single commit)
- use force push on pull request branches: work on your contributions until they pass automated tests and review, and keep the history tidy there to prepare for merging
Common scenarios
Add support to manage new operating systems
Allowing Rex to manage a new operating system requires the following steps:
-
Teach rex about how to detect the given operating system
- add a way to
Rex::Hardware::Host::get_operating_system()
to detect the given operating system - add a new
is_myos()
function toRex::Commands::Gather
- add a way to
-
Let Rex choose the proper package and service management modules for the given operating system
- add support in
Rex::Service
andRex::Pkg
- add support in
-
Add new service and package management modules specific to the given operating system
- add
Rex::Service::MyOS
- add
Rex::Pkg::MyOS
- add
While the first two steps must currently go into Rex core, please consider
publishing the Rex::Service::MyOS
and Rex::Pkg::MyOS
modules as a separate
distribution.
Add support for new virtualization methods
Follow these steps to add support for a new virtualization method called MyVirt
:
- create the top-level
Rex::Virtualization::MyVirt
module which includes the constructor, and the documentation - create submodules for each virtualization command, e.g.
Rex::Virtualization::MyVirt::info
- implement the logic of the given command as the
execute
method
Please consider publishing the Rex::Virtualization::MyVirt
module as a
separate distribution.
Contribute to this guide
If you think some of the information in this document got outdated, lacks clarity, or has bugs, please propose changes to it too.