Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions devenv.nix
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,29 @@
popd
rm -rf "$tmp"

# Test devenv integrated into Nix flake
# Test devenv integrated into bare Nix flake
tmp="$(mktemp -d)"
pushd "$tmp"
nix flake init --template ''${DEVENV_ROOT}#simple
nix flake update \
--override-input devenv ''${DEVENV_ROOT}
nix develop --command echo nix-develop started succesfully
nix develop --command echo nix-develop started succesfully |& tee ./console
grep -F 'nix-develop started succesfully' <./console
grep -F "$(${lib.getExe pkgs.hello})" <./console
popd
rm -rf "$tmp"

# Test devenv integrated into flake-parts Nix flake
tmp="$(mktemp -d)"
pushd "$tmp"
nix flake init --template ''${DEVENV_ROOT}#flake-parts
nix flake update \
--override-input devenv ''${DEVENV_ROOT}
nix develop --command echo nix-develop started succesfully |& tee ./console
grep -F 'nix-develop started succesfully' <./console
grep -F "$(${lib.getExe pkgs.hello})" <./console
# Test that a container can be built
nix build .#container-processes
popd
rm -rf "$tmp"

Expand Down
164 changes: 164 additions & 0 deletions docs/guides/using-with-flake-parts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
`devenv` can be used without the `devenv` CLI by integrating into [Nix Flakes](https://www.tweag.io/blog/2020-05-25-flakes/), if you're more familiar with the Nix language/ecosystem.

Some usecases for using devenv configuration inside flakes is for projects that want to define other Nix flake features, apart from the development shell.
These include a Nix package for the project, NixOS and home-manager modules related to the project.
Usually you want to use the same lock file for the development shell as well as the Nix package and others, so that everything is based on the same nixpkgs.

A Nix flake includes the inputs from `devenv.yaml` as well as the devenv configuration that you'd usually find in `devenv.nix`. `flake.lock` is the lock file for Nix flakes, the equivalent to `devenv.lock`.
Comment on lines +1 to +7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using devenv via the flake module, assuming that the devenv CLI isn't available, how would I run processes/services defined in configuration?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On that note, it would be pretty amazing to expose the process runners and/or the direct commands they execute via flake.apps

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assuming that the devenv CLI isn't available

Bad assumption -- I just checked out this branch (after rebasing onto main) and I see that the CLI is available. However, when I run devenv up:

✦ ❯ devenv up
File devenv.nix does not exist. To get started, run:

  $ devenv init

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I accidentally shadowed the shim by adding the whole devenv package to the packages. That was old code from before the shim existed. Now the module is on par with the existing flake integration.


## Getting started

To quickly set a project up with Nix flakes, use of `nix flake init`, like:

```console
$ nix flake init --template github:cachix/devenv#flake-parts
```

This will create a `flake.nix` with devenv configuration, as well as a `.envrc` direnv configuration.

Open the devenv shell using:

```console
$ nix develop
```

This will create a lock file and open up a new shell that adheres to the devenv configuration stated in `flake.nix`.

## flake.nix

A minimal flake.nix that includes devenv is for example:

```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.11";
devenv.url = "github:cachix/devenv";
};

outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.devenv.flakeModule
];
systems = [ "x86_64-linux" "aarch64-darwin" ];

perSystem = { config, self', inputs', pkgs, system, ... }: {
# Per-system attributes can be defined here. The self' and inputs'
# module parameters provide easy access to attributes of the same
# system.

# Equivalent to inputs'.nixpkgs.legacyPackages.hello;
packages.default = pkgs.hello;

devenv.shells.default = {
# https://devenv.sh/reference/options/
packages = [ config.packages.default ];

enterShell = ''
hello
'';
};
};
};
}
```

Here a single shell is defined. It is defined for all listed systems. The shell includes a single devenv configuration module.
Inside the module is where you put the devenv configuration, the one you usually will find in `devenv.nix`. See https://devenv.sh/reference/options/ for the possible options to use here.

## direnv

To make use of `direnv` in your Nix flake project, you'll need [nix-direnv](https://github.com/nix-community/nix-direnv).

To configure `direnv` in your project make sure you have a file called `.envrc` that includes the following line:

```text
nix flake --impure
```

In normal `nix flake` projects, `--impure` is not needed. When using `devenv` in your flake, you _do_ need this option.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why does devenv require --impure?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've also been curiously wondering about this.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related: #330

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unclear to me too, but this is just inherited from the existing flake integration.


## Multiple shells

Defining multiple development shells using flakes can be useful depending on your projects structure. We will handle 2 use-cases here.

### Single project with multiple shells

Some projects lend themselves to define multiple development shells. For instance, a mono-repo where you want to define multiple development shells in a central flake.nix in the root of the repository. There you can centrally define the development shells for the different sub-projects in the repository.

In this case you want to define a `flake.nix` file that contains multiple `devShells`. For example:

```nix
# inside perSystem

devenv.shells.projectA = {
# https://devenv.sh/reference/options/
packages = [ config.packages.default ];

enterShell = ''
echo this is project A
hello
'';
};

devenv.shells.projectB = {
# https://devenv.sh/reference/options/
packages = [ config.packages.default ];

enterShell = ''
echo this is project A
hello
'';
};

# If you'd like to pick a default
devShells.default = config.devShells.projectA;
```

Here you can see that there are 2 shells defined. Each one with a devenv configuration with differently defined `enterShell`.

To enter the shell of `projectA`:

```console
$ nix develop .#projectA
this is project A
(devenv) $
```

To enter the shell of `projectB`:

```console
$ nix develop .#projectB
this is project B
(devenv) $
```

The last line makes `projectA` the default shell:

```console
$ nix develop .
this is project A
(devenv) $
```

### Projects with an external flake

Whenever you have projects where you cannot (or don't want to) add a flake.nix to its repository, you can refer to external flakes.

You can create a repository with a flake.nix like the one above. However, in a different project you can now refer to this flake using:

```console
$ nix develop file:/path/to/central/flake#projectA
this is project A
(devenv) $
```

You can also add this to the `direnv` configuration of the project. Just make sure the following line is in `.envrc`:

```text
nix flake --impure file:/path/to/central/flake#projectA
```

Note that instead of referring to a directory on local file system that includes the `flake.nix`, like `/path/to/central/flake`, it is also possible to use different references to a flake. For instance `github:` or `git:`. See [Nix flake references](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html#flake-references) for more information.

One big caveat with this method is that there is no lock file. It is not 100% clear which version of the flake is used when referring to it this way. A local project flake file will give more control which version of the flake is used.
58 changes: 58 additions & 0 deletions flake-module.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
devenvFlake: { flake-parts-lib, lib, inputs, ... }: {
options.perSystem = flake-parts-lib.mkPerSystemOption ({ config, pkgs, system, ... }:

let
devenvType = (devenvFlake.lib.mkEval {
inherit inputs pkgs;
modules = [{
config = {
# Add flake-parts-specific config here if necessary
};
}];
}).type;

shellPrefix = shellName: if shellName == "default" then "" else "${shellName}-";
in

{
options.devenv.shells = lib.mkOption {
type = lib.types.lazyAttrsOf devenvType;
description = ''
The [devenv.sh](https://devenv.sh) settings, per shell.

Each definition `devenv.shells.<name>` results in a value for
[`devShells.<name>`](flake-parts.html#opt-perSystem.devShells).

Define `devenv.shells.default` for the default `nix develop`
invocation - without an argument.
'';
example = lib.literalExpression ''
{
# create devShells.default
default = {
# devenv settings, e.g.
languages.elm.enable = true;
};
}
'';
default = { };
};
config.devShells = lib.mapAttrs (_name: devenv: devenv.shell) config.devenv.shells;

config.packages =
lib.concatMapAttrs
(shellName: devenv:
lib.concatMapAttrs
(containerName: container:
{ "${shellPrefix shellName}container-${containerName}" = container.derivation; }
)
devenv.containers
)
config.devenv.shells;
});

# the extra parameter before the module make this module behave like an
# anonymous module, so we need to manually identify the file, for better
# error messages, docs, and deduplication.
_file = __curPos.file;
}
22 changes: 19 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@

templates =
let

flake-parts = {
path = ./templates/flake-parts;
description = "A flake with flake-parts, direnv and devenv.";
welcomeText = ''
# `.devenv` should be added to `.gitignore`
```sh
echo .devenv >> .gitignore
```
'';
};

simple = {
path = ./templates/simple;
description = "A direnv supported Nix flake with devenv integration.";
Expand All @@ -89,7 +101,7 @@
};
in
{
inherit simple;
inherit simple flake-parts;
terraform = {
path = ./templates/terraform;
description = "A Terraform Nix flake with devenv integration.";
Expand All @@ -103,8 +115,12 @@
default = simple;
};

flakeModule = import ./flake-module.nix self;

lib = {
mkConfig = { pkgs, inputs, modules }:
mkConfig = args@{ pkgs, inputs, modules }:
(self.lib.mkEval args).config;
mkEval = { pkgs, inputs, modules }:
let
moduleInputs = { inherit pre-commit-hooks; } // inputs;
project = inputs.nixpkgs.lib.evalModules {
Expand All @@ -124,7 +140,7 @@
] ++ modules;
};
in
project.config;
project;
mkShell = args:
let
config = self.lib.mkConfig args;
Expand Down
1 change: 1 addition & 0 deletions src/modules/update-check.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ in
flakesIntegration = lib.mkOption {
type = lib.types.bool;
default = false;
defaultText = lib.literalMD ''`true` when devenv is invoked via the flake integration; `false` otherwise.'';
description = ''
Tells if devenv is being imported by a flake.nix file
'';
Expand Down
46 changes: 46 additions & 0 deletions templates/flake-parts/flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
description = "Description for the project";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
devenv.url = "github:cachix/devenv";
nix2container.url = "github:nlewo/nix2container";
nix2container.inputs.nixpkgs.follows = "nixpkgs";
mk-shell-bin.url = "github:rrbutani/nix-mk-shell-bin";
};

outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.devenv.flakeModule
];
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];

perSystem = { config, self', inputs', pkgs, system, ... }: {
# Per-system attributes can be defined here. The self' and inputs'
# module parameters provide easy access to attributes of the same
# system.

# Equivalent to inputs'.nixpkgs.legacyPackages.hello;
packages.default = pkgs.hello;

devenv.shells.default = {
name = "my-project";

# https://devenv.sh/reference/options/
packages = [ config.packages.default ];

enterShell = ''
hello
'';
};

};
flake = {
# The usual flake attributes can be defined here, including system-
# agnostic ones like nixosModule and system-enumerating ones, although
# those are more easily expressed in perSystem.

};
};
}