Skip to content

Sample repository to show various nix package manager usecases

Notifications You must be signed in to change notification settings

corpix/nix-showcase

Repository files navigation

nix showcase

Repository with examples around Nix package manager and NixOS.

I use it to present this wonderful piece of technology to other people.

Here is a QR code with the address of this repository https://github.com/corpix/nix-showcase

███████████████████████████████
█ ▄▄▄▄▄ ██▄▄ ▀ ██▀▄▀███ ▄▄▄▄▄ █
█ █   █ █▀▄  █▀▄▄▀▀▄▄██ █   █ █
█ █▄▄▄█ █▄▀ █▄█▀█ ▄ ▄ █ █▄▄▄█ █
█▄▄▄▄▄▄▄█▄▀▄█ █ ▀▄█▄▀ █▄▄▄▄▄▄▄█
█▄ ▀▀█▄▄▄▀▄ █▄▀▀ █ █ █▀█▀ ▄▀▀██
█▄ ▀   ▄▀█▄▀ ▄▄█  █ ▀▄▀██▄▀████
█▄██ ▀█▄  ▄ █▀ ▄ ▀▀   █  ▀██▀ █
█▀ █▄█ ▄▄▄▄██▀   ▄▄█▀▄▄█▀ ▄ █▀█
█▀▄█ ▀ ▄█▀▄▄█▄█▄▀▀ ▀ █▄▀ ▀▄ ▀▀█
█ ▄▀█▀ ▄ ▀▄▄▀▄▄ ▄▄█▀▄▀█▀█▄▄██▄█
█▄█▄▄▄█▄▄▀█▀▄▀▀█▀█ ▀▄ ▄▄▄ ▀▄▄▄█
█ ▄▄▄▄▄ █ ███▀ ▄ ▄█▄  █▄█ ▄▄▀▀█
█ █   █ ██▄▀▄▄▄█ ▀▀█   ▄▄▄ ▀█ █
█ █▄▄▄█ █▀ ▀ ▄██▄▄█ ▄▄▄  ▄  ▄▀█
█▄▄▄▄▄▄▄█▄▄▄▄█████▄▄▄██▄▄▄█▄███

preparations

For all commands inside this repository you will need a Nix package manager or NixOS in some cases (this requirement will be mentioned explicitly).

I recommend to use Nix package manager inside docker container:

$ make run/docker/shell

Also I should mention there is an official guide which will help you to install Nix package manager to you system. But if you don’t use Linux and you don’t feel yourself fluent with Nix then i recommend you to use dockerized environment from this repository.

language

Useful links:

repl

$ nix repl
Welcome to Nix version 2.3.15. Type :? for help.

nix-repl> :?
The following commands are available:

 <expr>        Evaluate and print expression
 <x> = <expr>  Bind expression to variable
 :a <expr>     Add attributes from resulting set to scope
 :b <expr>     Build derivation
 :i <expr>     Build derivation, then install result into current profile
 :l <path>     Load Nix expression and add it to scope
 :p <expr>     Evaluate and print expression recursively
 :q            Exit nix-repl
 :r            Reload all files
 :s <expr>     Build dependencies of derivation, then start nix-shell
 :t <expr>     Describe result of evaluation
 :u <expr>     Build derivation, then start nix-shell

what available

  • variables
  • functions
  • booleans
  • null
  • numbers
    • integers
    • floats
  • strings
  • regular expressions
  • paths
  • uri
  • attribute sets
  • lists
  • control flow
  • errors
  • laziness
  • derivations

variables

Lexical scoping:

nix-repl> let a = "hello"; b = "world"; in a + b
"helloworld"

All variables in let are declarative, so order is not enforced:

nix-repl> let a = b; b = "thing"; in a + b
"thingthing"

nix-repl> x = 1
nix-repl> let y = x; x = 2; in y
2

functions

let
  add = a: b: a + b;
in add 1 2

This will output 3.

Function like a: b: ... has two positional arguments, but:

  • you could define one or more positional arguments, like a: b: c: d: ...
  • you could partially apply function, like ((add 1) 2) is the same as add 1 2

So, each function could be partially applied:

nix-repl> fn = a: b: c: a + b + c

nix-repl> fn 1
«lambda @ (string):1:5»

nix-repl> fn 1 2
«lambda @ (string):1:8»

nix-repl> fn 1 2 3
6

In addition basic destructuring is supported:

nix-repl> fn = { a, b, c }: a + b + c

nix-repl> fn { a = 1; b = 2; c = 3; }
6

While using destructuring a default values could be specified:

nix-repl> fn = { a, b ? 2, c ? 3 }: a + b + c

nix-repl> fn { a = 1; }
6

nix-repl> fn { a = 1; b = 1; }
5

Destructuring results could be accessed as an attribute set:

nix-repl> fn = { a, b, c } @ attrs: attrs
nix-repl> fn { a = 1; b = 2; c = 3; }
{ a = 1; b = 2; c = 3; }

nix-repl> fn = { a, b, c ? 3 } @ attrs: attrs
nix-repl> fn { a = 1; b = 2; }
{ a = 1; b = 2; }

nix-repl> fn { a = 1; b = 2; c = 3; }
{ a = 1; b = 2; c = 3; }

nix-repl> fn = { a, b, c, ... } @ attrs: attrs
nix-repl> fn { a = 1; b = 2; c = 3; d = 4; }
{ a = 1; b = 2; c = 3; d = 4; }

booleans

For boolean true/false there are separate keywords:

  • true
  • false
boolean operators
  • ! is boolean not, !true is false
  • && is boolean and, true && false is false
  • || is boolean or, false || true is true
  • -> is boolean impl, true -> true is true, equivalent of !true || true

All this expressions must evaluate to boolean, thus true && 1 is not valid.

equality
  • ==== equal, 1==1 is true, 1==2 is false
  • != not equal, 2!=2 is true

You could check for equality between values of different type:

  • there is no error
  • but there is no implicit type conversion
  • so different types are always not equal

Here is an examples for types we have not talked about, but they are worth metioning:

nix-repl> {} == {}
true

nix-repl> { a = 1; } == { a = 1; }
true

nix-repl> { a = { b = 1; }; } == { a = { b = 1; }; }
true

nix-repl> [ 1 2 3 ] == [ 1 2 3 ]
true

nix-repl> [ 1 2 3 ] == [ 1 2 ]
false

null

Null is just null, like in other languages.

nix-repl> fn = { var ? null }: if var == null then "user has not provide us a `var` value" else var

nix-repl> fn {}
"user has not provide us with `var` value"

nix-repl> fn { var = 1; }
1

numbers

There are numeric operators:

All are left associative

  • + addition, 2 + 2 is 4
  • - subtraction, 4 - 2 is 2
  • * multiplication, 3 * 3 is 9
  • / division, 9 / 3 is 3

There is a caveat with division operator, you should always add space in between. Otherwise it will be interpreted as a different type - path, we will talk about this bellow

You could mix integers with floats, this is valid. But in this case result is always float.

In case you need an integer division use builtins.div, for example:

More about builtins

nix-repl> builtins.div 4 2
2

nix-repl> builtins.div 7 4
1

You could find functional analogs for other operators under builtins.

strings

Strings support interpolation.

nix-repl> "hello"
"hello"

nix-repl> who = "world"

nix-repl> "hello ${who}"
"hello world"

Where is no implicit type conversion, so every expression under interpolation should be string:

nix-repl> who = 1

nix-repl> "hello ${who}"
error: cannot coerce an integer to a string, at (string):1:2

nix-repl> "hello ${builtins.toString who}"
"hello 1"

Strings could be multiline:

''
hello
world
''

regular expressions

There is no separate type for regular expressions, they are represented with usual strings and applied with builtins.match:

match is using extended POSIX regular expressions

nix-repl> builtins.match "http" "http://ya.ru/hello"
null

nix-repl> builtins.match ".+" "http://ya.ru/hello"
[ ]

nix-repl> builtins.match "(.+)" "http://ya.ru/hello"
[ "http://ya.ru/hello" ]

nix-repl> builtins.match "http://([^/]+).*" "http://ya.ru/hello"
[ "ya.ru" ]

paths

Path is a filesystem object representing files and directories which is a separate type:

nix-repl> builtins.isPath /foo
true

nix-repl> builtins.isPath "/foo"
false

nix-repl> builtins.typeOf /foo
"path"

nix-repl> /foo + /bar
/foo/bar

nix-repl> /foo + /bar + "/baz"
/foo/bar/baz
nix-repl> builtins.readDir ./.
{
  ".cache"               =  "directory";
  ".config"              =  "directory";
  ".fish.conf"           =  "regular";
  ".git"                 =  "directory";
  ".gitignore"           =  "regular";
  ".local"               =  "directory";
  ".personal.fish.conf"  =  "regular";
  ".personal.tmux.conf"  =  "regular";
  ".tmux"                =  "unknown";
  ".tmux.conf"           =  "regular";
  Makefile               =  "regular";
  "README.org"           =  "regular";
  container              =  "directory";
  dotfiles               =  "directory";
  "nix-cage.json"        =  "regular";
  "nixpkgs.nix"          =  "regular";
  "shell.nix"            =  "regular";
  "tools.nix"            =  "regular";
}

nix-repl> builtins.readDir "./."
error: string './.' doesn't represent an absolute path, at (string):1:1

uri

URI’s are also supported which are syntactic sugar, but not a separate type:

nix-repl> http://example.org/foo.tar.bz2
"http://example.org/foo.tar.bz2"

nix-repl> http://example.org/foo.tar.bz2?foo=bar
"http://example.org/foo.tar.bz2?foo=bar"

nix-repl> builtins.typeOf http://example.org/foo.tar.bz2
"string"

attribute sets

This is crucial data type of the whole language.

Attribute sets are like hash-maps, but a bit more advanced.

nix-repl> {}
{ }

nix-repl> { a = 1; }
{ a = 1; }

Attribute sets have a shortcut to define nested keys:

:p is a repl helper which prints value expanding expression recursively

nix-repl> { a.b.c = 1; }
{ a = { ... }; }

nix-repl> :p { a.b.c = 1; }
{ a = { b = { c = 1; }; }; }

Attribute sets could be recursive, to define a recursive attribute set prepend it with rec keyword:

nix-repl> rec { a = 1; b = a; }
{ a = 1; b = 1; }

Given this we could say that let is just an attribute set underneath.

Attribute sets could inherit keys of each other:

nix-repl> baseSet = { a = 1; b = 2; }

nix-repl> { inherit (baseSet) a b; c = 3; }
{ a = 1; b = 2; c = 3; }

We could also use inherit inside let (because it is just an attribute set).

To access individual keys of the attribute set dot notation is used (. is an operator called select):

nix-repl> a = { foo = 1; bar = 2; }
nix-repl> a.foo
1
nix-repl> a.bar
2

nix-repl> x = { a.b = 1; }
nix-repl> x . a . b
1

Attribute set keys could be defined from variable or with a string:

nix-repl> key = "keyName"
nix-repl> :p { foo.${key} = 1; }
{ foo = { keyName = 1; }; }

nix-repl> :p { foo."${key}Interpolated" = 1; }
{ foo = { keyNameInterpolated = 1; }; }

You could merge attribute sets with // operator, thus deep-merge is not providen by builtins (nixpkgs library has lib.recursiveUpdate):

nix-repl> { a = 1; } // { a = 2; b = 3; }
{ a = 2; b = 3; }

nix-repl> { a = 1; } // { a = 2; b = 3; } // { c = 4; }
{ a = 2; b = 3; c = 4; }

nix-repl> :p { a = { b = 1; }; } // { a = { b = 2; }; }
{ a = { b = 2; }; }

Also you could get values & names of the attribute sets (order is guaranteed):

nix-repl> builtins.attrValues { a = 1; b = 2; }
[ 1 2 ]

nix-repl> builtins.attrNames { a = 1; b = 2; }
[ "a" "b" ]

Attribute sets could be used in combination with with operator to define lexical scopes from attribute sets:

nix-repl> with { a = 1; b = 2; }; a + b
3

lists

Basic lists:

nix-repl> [1 2 3]
[ 1 2 3 ]

nix-repl> [1 2 (3 + 1)]
[ 1 2 4 ]

List concatenation:

nix-repl> [1 2] ++ [3 4]
[ 1 2 3 4 ]

List elements:

nix-repl> builtins.head [1 2 3]
1

nix-repl> builtins.tail [1 2 3]
[ 2 3 ]

nix-repl> builtins.elem 1 [0 1 2]
true

nix-repl> builtins.elem 1 [0 2]
false

nix-repl> builtins.elemAt [0 1] 1
1

loops

nix-repl> map (item: item + 1) [1 2 3]
[ 2 3 4 ]

Defining fold (reduce) via recursion (this function is available in nixpkgs):

  nix-repl> fold = op: nul: list:
                     with builtins; let
			  len = length list;
			  loop = n: if n == len
			            then nul
				    else op (elemAt list n) (loop (n + 1));
			in loop 0

  nix-repl> fold (value: acc: value ++ acc) [] [[1 2][3 4]]
  [ 1 2 3 4 ]

For attribute sets there is builtins.mapAttrs:

nix-repl> :p builtins.mapAttrs (name: value: [name value]) { a = 1; b = 2; }
{ a = [ "a" 1 ]; b = [ "b" 2 ]; }

control flow

We have seen if:

nix-repl> if 1 == 1 then "equal" else "not equal"
"equal"

And thats all you have to control the execution :)

errors

Throwing an error breaks the execution:

nix-repl> throw "oops"
error: oops

There is a syntactic sugar which allows to check prerequisites in expressions, just prepend expression with assert expr;:

nix-repl> assert true; "everything is ok"
"everything is ok"

nix-repl> assert false; "everything is ok"
error: assertion false failed at (string):1:1

tracing

Simple tracing expression is available:

nix-repl> builtins.trace "value" "expression"
trace: value
"expression"

nix-repl> map (value: builtins.trace value value) [1 2 3 4]
trace: 1
trace: 2
trace: 3
trace: 4
[ 1 2 3 4 ]

laziness

Every expression is lazy:

nix-repl> x = builtins.trace "i am lazy" "result"

nix-repl> x
trace: i am lazy
"result"

derivations

Derivations is a fancy name for term «package».

Well… not quite, but it could be comfortable to think about derivation as a package

We will discuss a low-level derivations which usualy not used directly (nixpkgs provides high-level tools to build packages).

Here is an example:

We will use some packages from nixpkgs here to keep things simple. You should restart nix repl with <nixpkgs> argument, exit the repl and type: =nix repl ‘<nixpkgs>’=

 nix-repl> :b derivation {
                name = "foo";
		    system = "x86_64-linux";
		    builder = pkgs.writeScript "builder.sh" ''
		      #!${pkgs.bash}/bin/bash -e
		      ${coreutils}/bin/mkdir $out
		      ${coreutils}/bin/touch $out/hello
		    '';
		  }
 [2 built, 0.0 MiB DL]

 this derivation produced the following outputs:
   out -> /nix/store/ah2zr4q1s8kvzd134qvkk074nmghj307-foo

This provides one output named out, outputs are atomic parts of the package.

Let’s inspect the filesystem:

$ ls -la /nix/store/ah2zr4q1s8kvzd134qvkk074nmghj307-foo
.r--r--r-- 0 nobody  1 Jan  1970 hello

search for packages

Run REPL with make run/nix/repl, you will see:

Welcome to Nix version 2.3.15. Type :? for help.

Loading '<nixpkgs>'...
Added 14696 variables.

nix-repl>

Packages are available inside pkgs namespace, write:

nix-repl> pkgs.hello

Then press TAB, you will see:

pkgs.hello          pkgs.hello-unfree   pkgs.hello-wayland

To see package description:

nix-repl> pkgs.hello.meta.description
"A program that produces a familiar, friendly greeting"

build container

Nix package manager is able to build containers which conforms OCI format.

We have an example docker container with:

  • bash
  • curl
  • CA certificates
  • coreutils + some additional tools

To build this container:

  • change your working directory to ./container with cd ./container
  • build a container with make nix/build/container

This will output ./build/container.tar.gz symbolic link. This symbolic will point to the object inside /nix/store/.

To import this .tar.gz into docker you will need to copy this file from nix store somewhere where it will be accessible to docker:

$ cp -L ./build/container.tar.gz ./container.tar.gz

Then open separate terminal tab and navigate to ./container directory, after that:

$ docker load -i container.tar.gz

Run container with:

$ docker run -it gitlab.example.com:5050/nix/showcase/showcase:latest

About

Sample repository to show various nix package manager usecases

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages