├── .github └── workflows │ └── run-documentation-tests.yml ├── .gitignore ├── .shellspec ├── README.md ├── flake.lock ├── flake.nix ├── spec ├── nix_spec.sh └── spec_helper.sh └── src ├── aaa.c └── bbb.c /.github/workflows/run-documentation-tests.yml: -------------------------------------------------------------------------------- 1 | # Run tests which document the available behaviour in all stages of the project 2 | 3 | name: Documentation tests 4 | 5 | on: 6 | push: 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | platform: [ubuntu-18.04, macos-latest] 16 | runs-on: ${{ matrix.platform }} 17 | 18 | steps: 19 | 20 | - uses: actions/checkout@v2 21 | with: 22 | # 1. Need to run tests on each branch 23 | # 2. Nix flakes don't work on shallow clones 24 | fetch-depth: 0 25 | 26 | - name: Install Nix 27 | uses: cachix/install-nix-action@v12 28 | with: 29 | install_url: https://github.com/numtide/nix-flakes-installer/releases/download/nix-2.4pre20201221_9fab14a/install 30 | nix_path: nixpkgs=channel:nixos-unstable 31 | extra_nix_config: | 32 | experimental-features = nix-command flakes 33 | 34 | # Ugh! There must be a cheaper way of doing this! 35 | - name: Ensure MacOS uses sensible shell 36 | run: nix-env -f '' -i bash 37 | if: runner.os == 'macOS' 38 | 39 | - name: Run tests 40 | run: | 41 | nix run github:jacg/flakes-learning-curve/shellspec -- --color --shell bash 42 | 43 | git checkout generalize-system 44 | nix run github:jacg/flakes-learning-curve/shellspec -- --color --shell bash 45 | 46 | if [ "$RUNNER_OS" == "Linux" ]; then 47 | git checkout first-flake 48 | nix run github:jacg/flakes-learning-curve/shellspec -- --color --shell bash 49 | fi 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /result 2 | /.direnv/ 3 | -------------------------------------------------------------------------------- /.shellspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | 3 | ## Default kcov (coverage) options 4 | # --kcov-options "--include-path=. --path-strip-level=1" 5 | # --kcov-options "--include-pattern=.sh" 6 | # --kcov-options "--exclude-pattern=/.shellspec,/spec/,/coverage/,/report/" 7 | 8 | ## Example: Include script "myprog" with no extension 9 | # --kcov-options "--include-pattern=.sh,myprog" 10 | 11 | ## Example: Only specified files/directories 12 | # --kcov-options "--include-pattern=myprog,/lib/" 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository tries to document how to write and use Nix flakes by providing a 2 | sequence of steps, starting with just about the simplest flake that can be 3 | written, and gradually building it up by adding more features. 4 | 5 | Crucially, every step should 6 | 7 | + consist of self-contained and correctly working code 8 | + be documented with usage examples which are *automatically tested* for correctness. 9 | 10 | As flake consists of a version-controlled filesystem tree with a `flake.nix` 11 | file in its root, each step is in a separate commit of this repository. These commits are labelled with meaningfully named branches. 12 | 13 | Currently available steps are: 14 | 15 | 1. `first-flake` 16 | 2. `generalize-system` 17 | 3. `defult-package` 18 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "locked": { 5 | "lastModified": 1610051610, 6 | "narHash": "sha256-U9rPz/usA1/Aohhk7Cmc2gBrEEKRzcW4nwPWMPwja4Y=", 7 | "owner": "numtide", 8 | "repo": "flake-utils", 9 | "rev": "3982c9903e93927c2164caa727cd3f6a0e6d14cc", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "numtide", 14 | "repo": "flake-utils", 15 | "type": "github" 16 | } 17 | }, 18 | "nixpkgs": { 19 | "locked": { 20 | "lastModified": 1613980253, 21 | "narHash": "sha256-3jZJAUsTSsKaElPy509a4OX87Vvy1Mu5Z1V3pMg/Pc0=", 22 | "owner": "NixOS", 23 | "repo": "nixpkgs", 24 | "rev": "d4189f68fdbe0b14b7637447cb0efde2711f4abf", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "owner": "NixOS", 29 | "ref": "nixos-20.09", 30 | "repo": "nixpkgs", 31 | "type": "github" 32 | } 33 | }, 34 | "root": { 35 | "inputs": { 36 | "flake-utils": "flake-utils", 37 | "nixpkgs": "nixpkgs" 38 | } 39 | } 40 | }, 41 | "root": "root", 42 | "version": 7 43 | } 44 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | 3 | description = "A simple example of managing a project with a flake"; 4 | 5 | inputs.nixpkgs .url = "github:NixOS/nixpkgs/nixos-20.09"; 6 | inputs.flake-utils.url = "github:numtide/flake-utils"; 7 | 8 | outputs = { self, nixpkgs, flake-utils }: 9 | flake-utils.lib.eachDefaultSystem (system: 10 | let pkgs = import nixpkgs { inherit system; }; in 11 | rec { 12 | packages.aaa-and-bbb = pkgs.stdenv.mkDerivation { 13 | name = "flake-learning-curve"; 14 | src = self; 15 | 16 | buildInputs = [ 17 | pkgs.mdbook 18 | ]; 19 | 20 | buildPhase = '' 21 | $CC -o aaa src/aaa.c 22 | $CC -o bbb src/bbb.c 23 | ''; 24 | 25 | installPhase = '' 26 | mkdir -p $out/bin 27 | install -t $out/bin aaa bbb 28 | ''; 29 | 30 | }; 31 | 32 | defaultPackage = packages.aaa-and-bbb; 33 | 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /spec/nix_spec.sh: -------------------------------------------------------------------------------- 1 | Describe 'nix' 2 | 3 | # -------------------------------------------------------------------------------- 4 | 5 | It 'A sanity check: we have a recent enough version of `nix`' 6 | When call nix --version 7 | The output should include 'nix (Nix) 2.4' 8 | End 9 | 10 | # -------------------------------------------------------------------------------- 11 | 12 | # # TODO how to inspect something inside the shell? 13 | # It 'shell (local default)' 14 | # When call nix shell 15 | # The output should equal 'This is program AAA.' 16 | # End 17 | 18 | # -------------------------------------------------------------------------------- 19 | It 'shell local-flake#package-name --command' 20 | # `nix shell --command ` can be used within the repository (or 21 | # anywhere on the local filesystem) to run executables installed by the 22 | # `defaultPackage`. 23 | When call nix shell .#aaa-and-bbb --command aaa 24 | The output should equal 'This is program AAA.' 25 | End 26 | 27 | It 'shell remote-flake#pakcage-name --command' 28 | # It also works for flakes which are not present on the local machine. 29 | When call nix shell github:jacg/flakes-learning-curve/generalize-system#aaa-and-bbb --command bbb 30 | The output should include 'this is program BBB!' 31 | End 32 | 33 | # TODO point out that `-c 'this | that'` doesn't work, and the `-c bash -c 34 | # 'this | that'` workaround. 35 | 36 | # -------------------------------------------------------------------------------- 37 | 38 | # TODO need to check something about result 39 | # It 'nix build' 40 | # When call nix build 41 | # The status should be success 42 | # End 43 | 44 | # -------------------------------------------------------------------------------- 45 | 46 | It 'nix shell' 47 | When call nix shell --command aaa 48 | The status should be success 49 | The output should equal 'This is program AAA.' 50 | End 51 | 52 | # -------------------------------------------------------------------------------- 53 | 54 | # TODO how to deal with testing something inside the resulting shell 55 | # It 'nix develop' 56 | # When call nix develop 57 | # The status should be failure 58 | # The error should include 'does not provide attribute' 59 | # The error should include 'defaultPackage' 60 | # End 61 | 62 | # -------------------------------------------------------------------------------- 63 | It 'run' 64 | # By default `nix run` tries to execute a binary with a name that is 65 | # identical to the name of the `default Package`. Our current flake does not 66 | # include such a binary, so `nix run` will fail: 67 | When call nix run 68 | The status should be failure 69 | The error should include 'unable to execute' 70 | The error should include "flake-learning-curve/bin/flake-learning-curve" 71 | The error should include "No such file or directory" 72 | End 73 | # Next we'll see two approaches to getting this to work: 74 | # 1. Set a binary with the appropritate name. 75 | # 2. Define a `defaultApp` in the flake's outputs. 76 | 77 | # -------------------------------------------------------------------------------- 78 | 79 | End 80 | -------------------------------------------------------------------------------- /spec/spec_helper.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | # Defining variables and functions here will affect all specfiles. 4 | # Change shell options inside a function may cause different behavior, 5 | # so it is better to set them here. 6 | # set -eu 7 | 8 | # This callback function will be invoked only once before loading specfiles. 9 | spec_helper_precheck() { 10 | # Available functions: info, warn, error, abort, setenv, unsetenv 11 | # Available variables: VERSION, SHELL_TYPE, SHELL_VERSION 12 | : minimum_version "0.28.1" 13 | } 14 | 15 | # This callback function will be invoked after a specfile has been loaded. 16 | spec_helper_loaded() { 17 | : 18 | } 19 | 20 | # This callback function will be invoked after core modules has been loaded. 21 | spec_helper_configure() { 22 | # Available functions: import, before_each, after_each, before_all, after_all 23 | : import 'support/custom_matcher' 24 | } 25 | -------------------------------------------------------------------------------- /src/aaa.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | printf("This is program AAA.\n"); 5 | } 6 | -------------------------------------------------------------------------------- /src/bbb.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | printf("This NOT program AAA; this is program BBB!\n"); 5 | } 6 | --------------------------------------------------------------------------------