1
0
Fork 0
mirror of https://github.com/NixOS/nix.dev.git synced 2024-10-18 14:32:43 -04:00
nix.dev/source/tutorials/first-steps/declarative-shell.md

170 lines
5.8 KiB
Markdown
Raw Normal View History

---
myst:
html_meta:
"keywords": "tutorial, declarative, shell, environment, developer, nix, nixpkgs"
---
(declarative-reproducible-envs)=
# Declarative shell environments with `shell.nix`
## Overview
Declarative shell environments allow you to
- Automatically run bash commands during environment activation
- Automatically set environment variables
- Put the environment definition under version control and reproduce it on other machines
### What will you learn?
In the {ref}`ad-hoc-envs` tutorial, we looked at imperatively creating shell environments using `nix-shell -p`, for when we need a quick way to access some tools without having to install them globally.
We also saw how to execute that command with a specific Nixpkgs revision using a Git commit as an argument, to recreate the same environment used previously.
In this tutorial we'll take a look how to create reproducible shell environments given a declarative configuration in a {term}`Nix file`.
### How long will it take?
30 minutes
### What will you need?
- A basic understanding of the [Nix language](reading-nix-language)
## Entering a shell with Python installed
Suppose we a development environment in which Python 3 was installed.
The simplest possible way to accomplish this is via the `nix-shell -p` command:
```
$ nix-shell -p git neovim nodejs
```
This command works, but there's a number of drawbacks:
- You have to type out `-p git neovim nodejs` every time you enter the shell.
- It doesn't (ergonomically) allow you any further customization of your shell environment.
A better solution is to create our shell environment from a `shell.nix` file.
## A basic `shell.nix` file
Create a file called `shell.nix` with these contents:
```nix
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-22.11";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
pkgs.mkShell {
packages = with pkgs; [
git
neovim
nodejs
];
}
```
::::{dropdown} Detailed explanation
We use a version of [Nixpkgs pinned to a release branch](<ref-pinning-nixpkgs>), and explicitly set configuration options and overlays to avoid them being inadvertently overridden by [global configuration](https://nixos.org/manual/nixpkgs/stable/#chap-packageconfig).
`mkShell` is a function that produces a shell environment.
It takes as argument an attribute set.
Here we give it an attribute `packages` with a list containing one item from the `pkgs` attribute set.
:::{Dropdown} Side note on `packages` and `buildInputs`
You may encounter examples of `mkShell` that add packages to the `buildInputs` or `nativeBuildInputs` attributes instead.
`nix-shell` was originally conceived as a way to construct a shell environment containing the tools needed to debug package builds.
Only later it became widely used as a general way to make temporary environments for other purposes.
`mkShell` is a [wrapper around `mkDerivation`](https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell), so it takes the same arguments as `mkDerivation`, such as `buildInputs` or `nativeBuildInputs`.
The `packages` attribute argument to `mkShell` is simply an alias for `nativeBuildInputs`.
:::
::::
Enter the environment by running `nix-shell` in the same directory as `shell.nix`:
```console
$ nix-shell
[nix-shell]$
```
`nix-shell` by default looks for a file called `shell.nix` in the current directory and builds a shell environment from the Nix expression in this file.
Packages defined in the `packages` attribute will be available in `$PATH`.
Check that the desired packages are indeed available in the expected version as we did in the [previous tutorial](check-package-version).
## Environment variables
You may want to automatically export certain environment variables when you enter a shell environment.
Set your `GIT_EDITOR` to use the `nvim` from the shell environment:
```diff
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-22.11";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
pkgs.mkShell {
packages = with pkgs; [
git
neovim
nodejs
];
+ GIT_EDITOR = "${pkgs.neovim}/bin/nvim";
}
```
Any attribute name passed to `mkShell` that is not [reserved otherwise] and has a value which can be coerced to a string will end up as an environment variable.
:::{dropdown} Detailed explanation
The newly added attribute `GIT_EDITOR` is set to a string composed of the output store path of the `neovim` derivation and the relative path to the `nvim` executable inside that store path.
See the [Nix language tutorial on derivations for details](derivations).
:::{warning}
Some variables are protected from being set as described above.
For example, the shell prompt format for most shells is set by the `PS1` environment variable, but `nix-shell` already sets this by default, and will ignore a `PS1` attribute set in the argument.
If you really need to override these protected environment variables, use the `shellHook` attribute as described in the next section.
:::
## Startup commands
You may want to run some commands before entering the shell environment.
These commands can be placed in the `shellHook` attribute provided to `mkShell`.
Set `shellHook` to output the current repository status:
```diff
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-22.11";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
pkgs.mkShell {
packages = with pkgs; [
git
neovim
nodejs
];
GIT_EDITOR = "${pkgs.neovim}/bin/nvim";
+
+ shellHook = ''
+ git status
+ '';
}
```
## References
- [`mkShell` documentation](https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell)
- Nixpkgs [shell functions and utilities](https://nixos.org/manual/nixpkgs/stable/#ssec-stdenv-functions) documentation
- [`nix-shell` documentation](https://nixos.org/manual/nix/stable/command-ref/nix-shell)