Skip to content

Latest commit

 

History

History
411 lines (320 loc) · 11.3 KB

CONTRIBUTING.md

File metadata and controls

411 lines (320 loc) · 11.3 KB

Flox CLI and Library

Quick Start

$ 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;

PR Guidelines

CLA

  • 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

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!

Commits

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:

  1. fix: a commit of the type fix patches a bug in the codebase (this correlates with PATCH in Semantic Versioning).
  2. feat: a commit of the type feat introduces a new feature to the codebase (this correlates with MINOR in Semantic Versioning).
  3. 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.
  4. 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.
  5. 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.

Development

$ 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

  • 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

Lint and Format flox

  • format rust code:
    $ pushd cli;
    $ cargo fmt
    $ cargo fmt --check # just check
    The project is formatted using rustfmt and applies custom rules through .rustfmt.toml. A pre-commit hook is set up to check rust file formatting.
  • format nix code
    $ treefmt -f nix .
    $ treefmt -f nix . --fail-on-change # just check
    A pre-commit hook is set up to check nix file formatting.
  • lint rust
    $ pushd cli;
    $ cargo clippy --all
  • lint all files (including for formatting):
    $ pre-commit run -a

Setting up C/C++ IDE support

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 and c++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 as compile_commands.json.
    • For VSCode, this is available under the Advanced drop down under C++ Configuration and can be set to ${workspaceFolder}/compile_commands.json.

Setting up rust IDE support

  • Install the rust-analyzer plugin for your editor
  • 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 for rust-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,
    

Testing

Unit tests

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

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

Mock catalog responses for use with integration tests are generated by:

Continuous testing

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

bats arguments

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.

Running subsets of tests

You can specify which tests to run by passing arguments.

Running a specific file

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.

Running tagged tests

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.

Running tests in a Linux container

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

Man Pages

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.

Rust Style Guidelines

  • In general, structs should derive Clone and Debug.

Merges

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).