devenv && direnv

Updated: September 28, 2024

Provides containerized packages and session variables to a devShell environment.

There is also Direnv for just when inside a specific directory.
First we will go over a development environment.
We can just make various types of shells.

mkShell

This will produce a shell default for when we type nix-shell
This will not work for nix shell because it requires a flake.nix

{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  name = "node";  # name of the shell
  packages = with pkgs; [
    git
    node
  ];
}

direnv

To use direnv in the nix flake we need nix-direnv.
This makes it so when we enter the directory where a .envrc file is, direnv will activate. The direnv is configured by the .envrc file:

use flake . --impure

In order to use a .envrc file we need permissions: direnv allow
This ties the .envrc environment into our shell environment.

devenv

Devenvs can be implemented through a flake or imported as a module.
Devenv takes mkShell to next level. It will activate itself using hooks, instead of us typing nix-shell.
Key notes regarding devenv:

# Can use inside a flake with .envrc file, just uses `use flake` instead of `use nix`
# For inside a flake add this to inputs
devenv.url = "github:cachix/devenv";

# then inside `mkFlake`:
imports = [
  inputs.devenv.flakeModule
];

# create a lockfile, then all set
nix flake lock

# open the `devenv` with --impure to allow host env and configuration into the shell
# (this would also create a lock file) devenv reuires --impure and in direnv via devenv
nix develop --impure

Devenv as a module (configuration file) like devenv-hugo.nix
if you use definitions from the flake it is no longer standalone

# inside perSystem = { ... }: {

devenv.shells.default = {
  # here we added imports inside with default as the name
  imports = [ ./devenv-foo.nix ];

  enterShell = ''
    hello
  '';
};

The devenv-hugo module could looks something like this:

{ config, lib, ... }:
let cfg = config.services.foo;
in {
  options = {
    services.foo = {
      package = lib.mkOption {
        type = lib.types.package;
        defaultText = lib.literalMD "defined internally";
        description = "The foo package to use.";
      };
      # ...
    };
  };
  config = lib.mkIf cfg.enable {
    processes.foo.exec = "${cfg.package}/bin/foo";
  };
}