-
-
Notifications
You must be signed in to change notification settings - Fork 265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add shell.nix tutorial #671
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Learning Journey | ||
|
||
This collection of tutorials guides you through your first steps with Nix. | ||
This series is a work in progress and will have some overlap with existing tutorials. | ||
The intention is to unify these tutorials over time. | ||
|
||
```{toctree} | ||
:maxdepth: 1 | ||
shell-dot-nix.md | ||
``` |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,152 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
# Creating shell environments | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
<!-- Include any foreward you want here --> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
## Overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
### What will you learn? | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
<!-- Give a brief description of what the reader will learn so that they know whether the topic interests them. --> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
How to create and configure reproducible shell environments | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
### How long will it take? | ||||||||||||||||||||||||||||||||||||||||||||||||||||
30 minutes | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
<!-- Give some indication of how long it will take to complete the tutorial so that the reader knows whether to continue. --> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
### What will you need? | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
<!-- List any prerequisite knowledge or tools the reader will need to complete the tutorial. --> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
A basic understanding of the Nix language | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
## Entering a shell with Python installed | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Suppose we wanted to enter a shell in which Python 3 was installed. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
The simplest possible way to accomplish this is via the `nix-shell -p` command: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
$ nix-shell -p python3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
This command works, but there's a number of inefficiences: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
- You have to type out `-p python3` every time you enter the shell. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
- It doesn't scale to an arbitrary number of packages (you would have to type out each package name each time). | ||||||||||||||||||||||||||||||||||||||||||||||||||||
- 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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
The `nix-shell` command by default looks for a file called `shell.nix` in the current directory and tries to build a shell environment by evaluating the Nix expression in this file. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
So, if you properly describe the shell environment you want in a `shell.nix` file, you can enter it with just the `nix-shell` command without any further arguments. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
No more specifying packages on the command line. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Here's what a basic `shell.nix` looks like that installs Python 3.10 as before: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
```nix | ||||||||||||||||||||||||||||||||||||||||||||||||||||
let | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs = import <nixpkgs> {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
in | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs.mkShell { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
packages = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs.python3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
zmitchell marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
where `mkShell` is a function that when called produces a shell environment. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
If you save this into a file called `shell.nix` and call `nix-shell` in the directory containing this `shell.nix` file, you'll enter a shell with Python 3 installed. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
## Adding packages | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Additional executable packages are added to the shell by adding them to the `packages` attribute. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
For example, let's say we wanted to add `curl` to our shell environment. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
The new `shell.nix` would look like this: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
```nix | ||||||||||||||||||||||||||||||||||||||||||||||||||||
let | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs = import <nixpkgs> {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
in | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs.mkShell { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
packages = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs.python3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs.curl # new package | ||||||||||||||||||||||||||||||||||||||||||||||||||||
]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
:::{note} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
`nix-shell` was originally conceived as a way to construct a shell environment containing the tools needed to *develop software*; only later was it widely used as a general way to construct temporary environments for other purposes. Also note that `mkShell` is a [wrapper around `mkDerivation`](https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell) so strictly speaking you can provide any attributes to `mkShell` that you could to `mkDerivation` such as `buildInputs`. However, the `packages` attribute provided to `mkShell` is an alias for `buildInputs`, so you shouldn't need to provide both `packages` and `buildInputs`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
::: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
## Environment variables | ||||||||||||||||||||||||||||||||||||||||||||||||||||
It's common to want to automatically export certain environment variables when you enter a shell environment. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
For example, you could have a database that depends on an environment variable to set the default authentication credentials during development. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
Setting an environment variable in via `shell.nix` is trivial. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Any attribute in the `mkShell` function call that `mkShell` doesn't recognize as a reserved attribute name will be set to an environment variable in the shell environment. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
The attributes that are reserved are listed in the [Nixpkgs manual][mkshell_attrs] and include `packages`, `name`, and several others. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
[mkshell_attrs]: https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell-attributes | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
Let's say you wanted to set the database user (`DB_USER`) and password (`DB_PASSWORD`) via environment variables in your `shell.nix` file. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
This is how that would look: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
```nix | ||||||||||||||||||||||||||||||||||||||||||||||||||||
let | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs = import <nixpkgs> {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
in | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs.mkShell { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
packages = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs.python310 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs.curl | ||||||||||||||||||||||||||||||||||||||||||||||||||||
]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
env = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
# Database credentials | ||||||||||||||||||||||||||||||||||||||||||||||||||||
DB_USER = "db_user"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
DB_PASSWORD = "super secret don't look"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
:::{warning} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Some variables are protected from being overridden via the `env` attribute as described above. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Discussed online: mention this as a warning rather than guidance. Mention that you can use |
||||||||||||||||||||||||||||||||||||||||||||||||||||
For example, the shell prompt format for most shells is set by the `PS1` environment variable, but `nix-shell` already overrides this by default, and will ignore a `PS1` attribute listed in `env`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
If you _really_ need to override these protected environment variables you can use the `shellHook` feature discussed in the next section and `export MYVAR="value"` in the hook script. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
It's generally discouraged to set environment variables this way. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It shouldn't be called discouraged, it's very valid for secret environment variables. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
::: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
## Startup commands | ||||||||||||||||||||||||||||||||||||||||||||||||||||
You may want to perform some initialization before entering the shell environment (for example, maybe you want to ensure that a file exists). | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Commands you'd like to run before entering the shell environment can be placed in the `shellHook` attribute of the attribute set provided to the `mkShell` function. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
To ensure that a file `should_exist.txt` exists, the `shell.nix` file would look like this: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
zmitchell marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
```nix | ||||||||||||||||||||||||||||||||||||||||||||||||||||
let | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs = import <nixpkgs> {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
in | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs.mkShell { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
packages = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs.python310 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
pkgs.curl | ||||||||||||||||||||||||||||||||||||||||||||||||||||
]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
env = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
# Database credentials | ||||||||||||||||||||||||||||||||||||||||||||||||||||
DB_USER = "db_user"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
DB_PASSWORD = "super secret don't look"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
# Set shell prompt format, ensure that 'should_exist.txt' exists | ||||||||||||||||||||||||||||||||||||||||||||||||||||
shellHook = '' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
export PS1="\u@\h >>> " | ||||||||||||||||||||||||||||||||||||||||||||||||||||
touch should_exist.txt | ||||||||||||||||||||||||||||||||||||||||||||||||||||
''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Discussed online: Provide other examples such as initializing a data directory for a database, getting secrets, etc. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
Some other common use cases for `shellHook` are: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
- Initializing a local data directory for a database used in a development environment | ||||||||||||||||||||||||||||||||||||||||||||||||||||
- Running commands to load secrets into environment variables | ||||||||||||||||||||||||||||||||||||||||||||||||||||
- Installing pre-commit-hooks | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
## Where to next? | ||||||||||||||||||||||||||||||||||||||||||||||||||||
- [`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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.