Skip to content

Commit

Permalink
add tutorial for nix-shell in shebang (#325)
Browse files Browse the repository at this point in the history
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
  • Loading branch information
rapenne-s and fricklerhandwerk committed Feb 14, 2023
1 parent 43e79f9 commit 91eda53
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 22 deletions.
22 changes: 1 addition & 21 deletions source/tutorials/ad-hoc-developer-environments.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,27 +128,6 @@ There are two things going on here:
1. The `--pure` flag makes sure that the bash environment from your system is not inherited. That means only the `git` that Nix installed is available inside the shell. This is useful for one-liners and scripts that run, for example, within a CI environment. While developing, however, we'd like to have our editor around and a bunch of other things. Therefore we might skip the flag for development environments but use it in build ones.
2. The `-I` flag pins the Nixpkgs revision to an **exact Git revision**, leaving no doubt about which exact version of Nix packages will be used.

## Reproducible executables

Finally, we can wrap scripts with Nix to provide a reproducible shell environment that we can commit to a Git repository and share with strangers online. As long as they have Nix installed, they'll be able to execute the script without worrying about manually installing (and later uninstalling) dependencies at all.

```python
#! /usr/bin/env nix-shell
#! nix-shell --pure -i python -p "python38.withPackages (ps: [ ps.django ])"
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/2a601aafdc5605a5133a2ca506a34a3a73377247.tar.gz

import django

print(django)
```

This is essentially the same example as in the previous section, but this time declaratively source controlled! All of the required Nix commands are included as `#!` shebang headers in the script itself.

:::{note}
The multiline shebang format is a feature of [nix-shell](https://nixos.org/manual/nix/stable/command-ref/nix-shell.html#use-as-a--interpreter).
All the subsequent `#! nix-shell` lines are used to build up the shell's configuration before building the shell and executing the body of the script.
:::

## Next steps

We've only covered the bare essentials of Nix here. Once you're comfortable with these examples, take a look at:
Expand All @@ -160,3 +139,4 @@ We've only covered the bare essentials of Nix here. Once you're comfortable with
- See `man nix-shell` for all of the options.
- To quickly setup a Nix project read through
[Getting started Nix template](https://github.com/nix-dot-dev/getting-started-nix-template).
- {ref}`reproducible-scripts` to see how to use `nix-shell` as a shebang.
1 change: 1 addition & 0 deletions source/tutorials/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
install-nix.md
ad-hoc-developer-environments.md
reproducible-scripts.md
nix-language.md
towards-reproducibility-pinning-nixpkgs.md
declarative-and-reproducible-developer-environments.md
Expand Down
2 changes: 1 addition & 1 deletion source/tutorials/nix-language.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Read detailed explanations if you want to make sure you fully understand the exa

- Familiarity with software development
- Familiarity with Unix shell, to read command line examples <!-- TODO: link to yet-to-be instructions on "how to read command line examples" -->
- A [Nix installation](./install-nix) to run the examples
- A {ref}`Nix installation <install-nix>` to run the examples

### How to run the examples?

Expand Down
79 changes: 79 additions & 0 deletions source/tutorials/reproducible-scripts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
(reproducible-scripts)=

# Reproducible interpreted scripts

In this tutorial, you will learn how to use Nix to create and run reproducible interpreted scripts.

## Requirements

- A working {ref}`Nix installation <install-nix>`
- Familiarity with [Bash]

## A trivial script with non-trivial dependencies

Take the following script, which fetches the content XML of a URL, converts it to JSON, and formats it for better readability:

```bash
#! /bin/bash

curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .
```

It requires the programs `curl`, `xml2json`, and `jq`.
It also requires the `bash` interpreter.
If any of these dependencies are not present on the system running the script, it will fail partially or altogether.

With Nix, we can declare all dependencies explicitly, and produce a script that will always run on any machine that supports Nix and the required packages taken from Nixpkgs.

## The script

A [shebang] is the first line of a script starting with `#!`.
It determines which program to use for running the script.

[Bash]: https://www.gnu.org/software/bash/
[shebang]: https://en.m.wikipedia.org/wiki/Shebang_(Unix)

We will use the shebang line `#! /usr/bin/env nix-shell`.

`/usr/bin/env` is a program available on most modern Unix-like operating systems.
It takes a command name as argument and will run the first executable by that name it finds in the directories listed in the environment variable `$PATH`.

We use [`nix-shell` as a shebang interpreter].
It takes the following parameters relevant for our use case:

- `-i` tells which program to use for interpreting the rest of the file
- `-p` lists packages that should be present in the interpreter's environment
- `-I` explicitly sets [the search path] for packages

[`nix-shell` as a shebang interpreter]: https://nixos.org/manual/nix/stable/command-ref/nix-shell.html#use-as-a--interpreter
[the search path]: https://nixos.org/manual/nix/unstable/command-ref/opt-common.html#opt-I

Create a file named `nixpkgs-releases.sh` with the following content:

```shell
#!/usr/bin/env nix-shell
#! nix-shell -i bash
#! nix-shell -p curl jq python3Packages.xmljson
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/2a601aafdc5605a5133a2ca506a34a3a73377247.tar.gz

curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .
```

We specify `bash` as the interpreter.

The command `xml2json` is provided by the package `python3Packages.xmljson`, while the commands `jq` and `curl` are provided by packages of the same name.

The parameter of `-I` refers to a specific Git commit of the Nixpkgs repository.
This ensures that the script will always run with the exact same packages versions, everywhere.

Make the script executable:

```console
chmod +x nixpkgs-releases.sh
```
Run the script:

```console
./nixpkgs-releases.sh
```

0 comments on commit 91eda53

Please sign in to comment.