Skip to content

Commit

Permalink
Merge pull request #4 from nixos-asia/callout
Browse files Browse the repository at this point in the history
Import `nix-rapid.md` post + use Emanote callout PR
  • Loading branch information
srid authored Dec 5, 2023
2 parents 2e1b23c + 44d6ebf commit feb2b9d
Show file tree
Hide file tree
Showing 4 changed files with 298 additions and 5 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
name: "Publish"
name: "Build & Publish"
on:
push:
branches:
- master
pull_request:
jobs:
emanote:
runs-on: ubuntu-latest
Expand All @@ -21,6 +22,7 @@ jobs:
nix build -j 4
- name: Deploy to gh-pages 🚀
uses: peaceiris/actions-gh-pages@v3
if: github.ref == 'refs/heads/master'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./result/
Expand Down
290 changes: 290 additions & 0 deletions en/blog/nix-rapid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
---
date: 2023-03-01
---

# Rapid Introduction to Nix

The goal of this mini-tutorial is to introduce you to #[[nix]] as quickly as possible while also preparing the motivated learner to dive deeper into [the whole Nix ecosystem][zero-to-nix]. At the end of this introduction, you will be able to create a #[[flakes|flake]] for building a package and providing a developer environment shell.

If you are already experienced in [purely functional programming](https://en.wikipedia.org/wiki/Purely_functional_programming), it is highly recommended to read [Nix - taming Unix with functional programming](https://www.tweag.io/blog/2022-07-14-taming-unix-with-nix/) to gain a foundational perspective into Nix being purely functional but in the context of *file system* (as opposed to values stored in memory).

> Dolstra proposed that we can treat the file system in an operating system like memory in a running program, and equate package management to memory management
## Install

Nix can be [[install|installed on Linux and macOS]]. If you are using [[nixos]], it already comes with Nix pre-installed.

## Attrset

>[!info] To learn more
> - [Official manual](https://nixos.org/manual/nix/stable/language/values.html#attribute-set)
> - [nix.dev on attrsets](https://nix.dev/tutorials/nix-language#attribute-set)
The [Nix programming language][nix-lang] provides a lot of general constructs. But at its most basic use, it makes heavy use of *nested hash maps* otherwise called an "attrset". They are equivalent to [`Map Text a`](https://hackage.haskell.org/package/containers-0.6.7/docs/Data-Map-Strict.html#t:Map) in Haskell. The following is a simple example of an attrset:

```nix
{
foo = {
bar = 1;
};
}
```

We have an outer attrset with a single key `foo`, whose value is another attrset with a single key `bar` and a value of `1`.

## repl

Nix expressions can be readily evaluated in the [Nix repl][nix-repl]. To start the repl, run `nix repl`.

```sh
$ nix repl
Welcome to Nix 2.12.0. Type :? for help.

nix-repl>
```

You can then evaluate expressions:

```nix
nix-repl> 2+3
5
nix-repl> x = { foo = { bar = 1; }; }
nix-repl> x
{ foo = { ... }; }
nix-repl> x.foo
{ bar = 1; }
nix-repl> x.foo.bar
1
nix-repl>
```

## Flakes

>[!info] To learn more
> - [Serokell Blog: Basic flake structure](https://serokell.io/blog/practical-nix-flakes#basic-flake-structure)
A Nix flake is defined in the `flake.nix` file, which denotes an attrset containing two keys `inputs` and `outputs`. *Outputs* can reference *inputs*. Thus, changing an *input* can change the *outputs*. The following is a simple example of a flake:

```nix
{
inputs = { };
outputs = inputs: {
foo = 42;
};
}
```

This flake has zero `inputs`. `outputs` is a [function][nix-function] that takes the (realised) inputs as an argument and returns the final output attrset. This output attrset, in our example, has a single key `foo` with a value of `42`.

We can use the [`nix flake show`][nix-flake-show] command to see the output structure of a flake:

```sh
$ nix flake show
path:/Users/srid/code/nixplay?lastModified=1675373998&narHash=sha256-ifNiFGU1VV784kVcssn2rXIil%2feHfMLhPfmvaELefwA=
└───foo: unknown
$
```

We can use [`nix eval`][nix-eval] to evaluate any output. For example,

```sh
$ nix eval .#foo
42
```

### Graph

A flake can refer to other flakes in its inputs. Phrased differently, a flake's outputs can be used as inputs in other flakes. The most common example is the [nixpkgs](https://zero-to-nix.com/concepts/nixpkgs) flake which gets used as an input in most flakes. Intuitively, you may visualize a flake to be a node in a larger graph, with inputs being the incoming arrows and outputs being the outgoing arrows.

### Inputs

> [!info] To learn more
> - [URL-like syntax][flake-url] used by the `url` attribute
Let's do something more interesting with our `flake.nix` by adding the nixpkgs input:

```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
};
outputs = inputs: {
# Note: If you are macOS, substitute `x86_64-linux` with `aarch64-darwin`
foo = inputs.nixpkgs.legacyPackages.x86_64-linux.cowsay;
};
}
```

The nixpkgs flake has an output called `legacyPackages`, which is indexed by the platform (called "system" in Nix-speak), further containing all the packages for that system. We assign that package to our flake output key `foo`.

>[!tip] You can use [`nix repl`][nix-repl] to explore the outputs of any flake, using TAB completion:
>
> ```sh
> $ nix repl --extra-experimental-features 'flakes repl-flake' github:nixos/nixpkgs/nixpkgs-unstable
> Welcome to Nix 2.12.0. Type :? for help.
>
> Loading installable 'github:nixos/nixpkgs/nixpkgs-unstable#'...
> Added 5 variables.
> nix-repl> legacyPackages.aarch64-darwin.cowsay
> «derivation /nix/store/0s2vdpkpdiljmh8y06xgdw5vg2cqfs0m-cowsay-3.7.0.drv»
>
> nix-repl>
> ```
### Predefined outputs
Nix commands treat certain outputs as special. These are:
| Output | Nix command | Description |
| ----------- | ----------------- | ------------------------------- |
| `packages` | `nix build` | [[drv]] output |
| `devShells` | `nix develop` | [Development](../dev.md) shells |
| `apps` | `nix run` | Runnable applications |
| `checks` | `nix flake check` | Tests and checks |
All of these predefined outputs are further indexed by the "system" value.
#### Packages
>[!info] To learn more
> - [`pkgs.stdenv.mkDerivation`](https://nixos.org/manual/nixpkgs/stable/#sec-using-stdenv) can be used to build a custom package from scratch
`packages` is the most often used output. Let us extend our previous `flake.nix` to use it:
```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
};
outputs = inputs: {
foo = 42;
packages.x86_64-linux = {
cowsay = inputs.nixpkgs.legacyPackages.x86_64-linux.cowsay;
};
};
}
```
Here, we are producing an output named `packages` that is an attrset of systems (currently, only `x86_64-linux`) to attrsets of packages. We are definining exactly one package, `cowsay`, for the `x86_64-linux` system.
```sh
$ nix flake show
path:/Users/srid/code/nixplay?lastModified=1675374260&narHash=sha256-FRven09fX3hutGa8+dagOCSQKVYAsHI6BsnCSEQ7PG8=
├───foo: unknown
└───packages
└───aarch64-darwin
└───cowsay: package 'cowsay-3.7.0'
```
Notice that `nix flake show` recognizes the *type* of `packages`. With `foo`, it couldn't (hence type is `unknown`) but with `packages`, it can (hence type is "package").
The `packages` output is recognized by `nix build`.
```sh
$ nix build .#cowsay
```
The [`nix build`][nix-build] command takes as argument a value of the form `<flake-url>#<package-name>`. By default, `.` (which is a [flake URL][flake-url]) refers to the current flake. Thus, `nix build .#cowsay` will build the `cowsay` package from the current flake under the current system. `nix build` produces a `./result` symlink that points to the Nix store path containing the package:
```sh
$ ./result/bin/cowsay hello
_______
< hello >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```
If you run `nix build` without arguments, it will default to `.#default`.
#### DevShells
> [!info] To learn more
> - [Official Nix manual][mkShell]
> - [NixOS Wiki](https://nixos.wiki/wiki/Development_environment_with_nix-shell)
Like `packages`, another predefined flake output is `devShells` - which is used to provide a development shell aka. a nix shell or devshell. A devshell is a sandboxed environment containing the packages and other shell environment you specify. nixpkgs provides a function called [`mkShell`][mkShell] that can be used to create devshells.
As an example, we will update our `flake.nix` to provide a devshell that contains the [jq](https://github.com/stedolan/jq) tool.
```nix
{
inputs = {
nixpkgs = {
url = "github:NixOS/nixpkgs/nixos-unstable";
};
};
outputs = inputs: {
foo = 42;
devShells = { # nix develop
aarch64-darwin = {
default =
let
pkgs = inputs.nixpkgs.legacyPackages.aarch64-darwin;
in pkgs.mkShell {
nativeBuildInputs = [
pkgs.jq
];
};
};
};
};
}
```
`nix flake show` will recognize this output as a "development environmenet":
```sh
$ nix flake show
path:/Users/srid/code/nixplay?lastModified=1675448105&narHash=sha256-dikTfYD1wbjc+vQ+IUTMXWv%2fm%2f7qb91Hk3ip5MNefeU=
├───devShells
│ └───aarch64-darwin
│ └───default: development environment 'nix-shell'
└───foo: unknown
```
Just as `packages` can be built using `nix build`, you can enter the devshell using [`nix develop`][nix-develop]:
```sh
$ nix develop
❯ which jq
/nix/store/33n0kx526i5dnv2gf39qv1p3a046p9yd-jq-1.6-bin/bin/jq
❯ echo '{"foo": 42}' | jq .foo
42
```
Typing `Ctrl+D` or `exit` will exit the devshell.
## Conclusion
This mini tutorial provided a rapid introduction to Nix flakes, enabling you to get started with writing simple flake for your projects. Consult the links above for more information. There is a lot more to Nix than the concepts presented here! You can also read [Zero to Nix][zero-to-nix] for a highlevel introduction to all things Nix and flakes.
## See also
- [A (more or less) one page introduction to Nix, the language](https://github.com/tazjin/nix-1p)
- [Nix - taming Unix with functional programming](https://www.tweag.io/blog/2022-07-14-taming-unix-with-nix/)
[zero-to-nix]: https://zero-to-nix.com/
[nix-lang]: https://nixos.org/manual/nix/stable/language/index.html
[nix-repl]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-repl.html
[nix-flake-show]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake-show.html
[nix-eval]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-eval.html
[nix-function]: https://nixos.org/manual/nix/stable/language/constructs.html#functions
[flake-url]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html#url-like-syntax
[mkShell]: https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell
[exa]: https://github.com/ogham/exa
[nix-build]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-build.html
[nix-develop]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-develop.html
[NixOS]: https://nixos.org/
7 changes: 4 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
};

inputs = {
emanote.url = "github:srid/emanote";
emanote.url = "github:srid/emanote/callout"; # https://github.com/srid/emanote/pull/466
nixpkgs.follows = "emanote/nixpkgs";
flake-parts.follows = "emanote/flake-parts";
};
Expand Down

0 comments on commit feb2b9d

Please sign in to comment.