$ cd git clone [email protected]/flox/flox.git;
$ cd flox;
# Enter Dev Shell
$ nix develop;
# Build `pkgdb' and `flox'
$ just build;
# Run the build
$ ./cli/target/debug/flox --help;
# Run the test suite
$ just test-all;
- All commits in a Pull Request are signed and Verified by Github or via GPG.
- As an outside contributor you need to accept the flox
Contributor License Agreement by adding your Git/Github
details in a row at the end of the
CONTRIBUTORS.csv
file by way of the same pull request or one done previously.
CI can only be run against the flox/flox repository - it can't be run on forks. To run CI on external contributions, a maintainer will have to fetch the branch for a PR and push it to a branch in the flox/flox repo. The maintainer reviewing a PR will run CI after approving the PR. If you ever need a run triggered, feel free to ping your reviewer!
This project follows (tries to), conventional commits.
We employ commitizen to help enforcing those rules.
Commit messages that explain the content of the commit are appreciated
For starters: commit messages should follow the pattern:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
The commit contains the following structural elements, to communicate the intent of the change:
- fix: a commit of the type
fix
patches a bug in the codebase (this correlates with PATCH in Semantic Versioning). - feat: a commit of the type feat introduces a new feature to the codebase (this correlates with MINOR in Semantic Versioning).
- BREAKING CHANGE: a commit that has a footer BREAKING CHANGE:, or appends a ! after the type/scope, introduces a breaking API change (correlating with MAJOR in Semantic Versioning). A BREAKING CHANGE can be part of commits of any type.
- types other than fix: and feat: are allowed,
for example @commitlint/config-conventional (based on the Angular convention)
recommends
build
,chore
,ci
,docs
,style
,refactor
,perf
,test
, and others. - footers other than BREAKING CHANGE: may be provided and follow a convention similar to git trailer format.
Additional types are not mandated by the Conventional Commits specification, and have no implicit effect in Semantic Versioning (unless they include a BREAKING CHANGE).
A scope may be provided to a commit’s type, to provide additional contextual information and is contained within parenthesis, e.g., feat(parser): add ability to parse arrays.
A pre-commit hook will ensure only correctly formatted commit messages are committed.
You can also run
$ cz c
or
$ cz commit
to make conforming commits interactively.
$ nix develop;
This sets up an environment with dependencies, rust toolchain, variable
and pre-commit-hooks
.
In the environment, use cargo
to build the rust based cli.
Note:
cargo based builds should only be used locally.
Flox must be buildable using flox
or nix
.
- build and run flox
$ pushd cli; $ cargo run -- <args>;
- build a debug build of flox
$ just build; # builds to ./cli/target/debug/flox
- run flox unit tests
$ pushd cli; $ cargo test [<args>] # or enable impure and other long running tests with $ cargo -F extra-tests [<args>]
- build an optimized release build of flox
$ make -C pkgdb -j RELEASE=1; $ ( pushd cli||return; cargo build --release; ) # builds to ./cli/target/release/flox
- format rust code:
The project is formatted using rustfmt and applies custom rules through
$ pushd cli; $ cargo fmt $ cargo fmt --check # just check
.rustfmt.toml
. A pre-commit hook is set up to check rust file formatting. - format nix code
A pre-commit hook is set up to check nix file formatting.
$ treefmt -f nix . $ treefmt -f nix . --fail-on-change # just check
- lint rust
$ pushd cli; $ cargo clippy --all
- lint all files (including for formatting):
$ pre-commit run -a
For VSCode, it's suggested to use the C/C++ plugin from Microsoft. This section refers to settings from that plugin.
- Set your environment to use
c17
andc++20
standards.- For VSCode, this is available under C++ Configuration
- Compile commands are built with
just build-cdb
and will be placed in the root folder ascompile_commands.json
.- For VSCode, this is available under the Advanced drop down under
C++ Configuration and can be set to
${workspaceFolder}/compile_commands.json
.
- For VSCode, this is available under the Advanced drop down under
C++ Configuration and can be set to
- Install the
rust-analyzer
plugin for your editor- See the official installation instruction
(in the
nix develop
subshell the toolchain will aready be provided, you can skip right to your editor of choice)
- See the official installation instruction
(in the
- If you prefer to open your editor at the project root, you'll need to help
rust-analyzer
find the rust workspace by configuing thelinkedProjects
forrust-analyzer
. In VS Code you can add this: to you.vscode/settings.json
:"rust-analyzer.linkedProjects": [ "${workspaceFolder}/cli/Cargo.toml" ]
- If you want to be able to run and get analytics on impure tests, you need to
activate the
extra-tests
feature In VS Code you can add this: to you.vscode/settings.json
:"rust-analyzer.cargo.features": [ "extra-tests" ]
- If you use
foxundermoon.shell-format
on VS Code make sure to configure editor config support for it:"shellformat.useEditorConfig": true,
Most changes should be unit tested.
If it's possible to test logic with a unit test, a unit test is preferred to any
other kind of test.
Unit tests should be added throughout the Rust code in ./cli
.
Unit tests can be run with just
:
$ nix develop
$ just impure-tests
$ just impure-tests models::environment::test
Integration tests are written with bats
.
expect
can be used for activate
tests that require testing an interactive
shell,
but in general expect
should be avoided.
Integration tests are located in the ./cli/tests
folder.
Integration tests currently test:
- CLI flags
- A lot of things that should be unit tests
- Integration (no way!?) with:
- The nix-daemon
- The shell
- github:NixOS/nixpkgs
- cache.nixos.org
- external Flox services like FloxHub
- language ecosystems
Integration tests can be run with just
:
$ nix develop
$ just integ-tests
Mock catalog responses for use with integration tests are generated by:
When working on the test you would probably want to run them continuously on every change. In that case run the following:
$ just integ-tests --watch
You can pass arbitrary flags through to bats
using a --
separator.
$ just integ-tests -- -j 4
This example tells bats
to run 4 jobs in parallel.
You can specify which tests to run by passing arguments.
In order to run a specific test file, pass the filename relative to the tests directory:
$ just integ-tests usage.bats
This example will only run tests in the cli/tests/usage.bats
file.
When writing integration tests it's important to add tags to each test to identify which subsystems the integration test is using. This makes it easier to target a test run at the subsystem you're working on.
You add tags to a test with a special comment:
# bats test_tags=foo,bar,baz
@test "this is the name of my test" {
run "$FLOX_BIN" --help;
assert_success;
}
You can apply a tag to tests in a file with another special comment, which applies the tags to all of the tests that come after the comment:
# bats file_tags=foo
@test "this is the name of my test" {
run "$FLOX_BIN" --help;
assert_success;
}
@test "this is the name of my test" {
run "$FLOX_BIN" --help;
assert_success;
}
Tags cannot contain whitespace, but may contain -
, _
, and :
, where :
is
used for namespacing.
The list of tags to use for integration tests is as follows:
init
build_env
install
uninstall
activate
push
pull
search
edit
list
delete
upgrade
project_env
managed_env
remote_env
python
,node
,go
,ruby
, etc (anything language specific)
Some of these tags will overlap. For example, the build_env
tag should be used
any time an environment is built, so there is overlap with install
,
activate
, etc.
In order to run tests with a specific tag, you'll pass the --filter-tags
option to bats
:
$ just integ-tests -- --filter-tags activate
This example will only run tests tagged with activate
.
You can use boolean logic and specify the flag multiple times to run specific
subsets of tests.
See the bats usage documentation
for details.
It's possible to shorten the feedback loop when developing Linux dependent features on a MacOS system by running the tests from a container.
Start a container with the code mounted in so that you can continue to use your normal editor:
docker run \
--rm --interactive --tty \
--volume $(pwd):/mnt --workdir /mnt \
--name flox-dev \
nixos/nix
Within the container:
echo 'experimental-features = nix-command flakes' >> /etc/nix/nix.conf
nix develop
Outside the container, snapshot the store so that you don't have to download the world next time:
docker commit flox-dev flox:dev
Within the container, remove any existing MacOS binaries and rebuild for Linux:
just clean
just build
Within the container, to avoid variable $src or $srcs should point to the source
errors per NixOS/nix#8355:
unset TMPDIR
Unreleased changes to man
pages are available from the nix develop
shell but
you will need to restart the shell or call direnv reload
to pick up new
changes.
- In general, structs should derive
Clone
andDebug
.
Changes should be squashed and merged into main
.
Development is done on branches and merged back to main
. Keep your branch
updated by rebasing and resolving conflicts regularly to avoid messy merges.
Merges to main
should be squashed and ff-only back to main
using GitHub
PRs. Or, if they represent multiple bigger changes, squashed into multiple
distinct change sets. Also be sure to run all tests before creating a mergable
PR (See above).