Initial commit
This commit is contained in:
commit
06c5be472a
6 changed files with 243 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/result
|
26
flake.lock
Normal file
26
flake.lock
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1720386169,
|
||||||
|
"narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "194846768975b7ad2c4988bdb82572c00222c0d7",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-24.05",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
11
flake.nix
Normal file
11
flake.nix
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
inputs.nixpkgs.url = "nixpkgs/nixos-24.05";
|
||||||
|
outputs = {nixpkgs, ...}: let
|
||||||
|
systems = ["aarch64-linux" "aarch64-darwin" "x86_64-darwin" "x86_64-linux"];
|
||||||
|
pkgsFor = func: (nixpkgs.lib.genAttrs systems (system: (func (import nixpkgs {inherit system;}))));
|
||||||
|
in {
|
||||||
|
packages = pkgsFor (pkgs: {
|
||||||
|
what-is-my-ip = pkgs.callPackage ./what-is-my-ip.nix {};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
200
post.md
Normal file
200
post.md
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
This post is a Flake-based rewrite of [Learn Nix the Fun Way on fzakaria.com]
|
||||||
|
[0]. I really enjoyed the content of the post and wanted to write it as a Nix
|
||||||
|
user who is just using flakes. It does add a few extra steps and complexity,
|
||||||
|
but I think it's still valuable and perhaps reveals a bit more about Nix and why
|
||||||
|
it's pretty fantastic.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
## what-is-my-ip
|
||||||
|
|
||||||
|
Let's walk through a single example of a shell script one may write: _what-is-my-ip_
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#! /usr/bin/env bash
|
||||||
|
curl -s http://httpbin.org/get | \
|
||||||
|
jq --raw-output .origin
|
||||||
|
```
|
||||||
|
|
||||||
|
Sure, it's _sort of portable_, if you tell the person running it to have _curl_ and _jq_. What if you relied on a specific version of either though?
|
||||||
|
|
||||||
|
Nix **guarantees** portability.
|
||||||
|
|
||||||
|
We might leverage _[Nixpkgs' trivial builders](https://ryantm.github.io/nixpkgs/builders/trivial-builders/)_ (specifically, `writeShellScriptBin`) in a basic Nix flake to turn this into a Nix derivation (i.e. build recipe).
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# flake.nix
|
||||||
|
{
|
||||||
|
inputs.nixpkgs.url = "nixpkgs/nixos-24.05";
|
||||||
|
outputs = {nixpkgs, ...}: let
|
||||||
|
systems = ["aarch64-linux" "aarch64-darwin" "x86_64-darwin" "x86_64-linux"];
|
||||||
|
pkgsFor = func: (nixpkgs.lib.genAttrs systems (system: (func (import nixpkgs {inherit system;}))));
|
||||||
|
in {
|
||||||
|
packages = pkgsFor (pkgs: {
|
||||||
|
what-is-my-ip = pkgs.callPackage ./what-is-my-ip.nix {};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# what-is-my-ip.nix
|
||||||
|
{pkgs}:
|
||||||
|
pkgs.writeShellScriptBin "what-is-my-ip" ''
|
||||||
|
${pkgs.curl}/bin/curl -s http://httpbin.org/get | \
|
||||||
|
${pkgs.jq}/bin/jq --raw-output .origin
|
||||||
|
''
|
||||||
|
```
|
||||||
|
|
||||||
|
> 😬 Avoid over-focusing on the fact I just introduced a new language and a good chunk of boilerplate. Just come along for the ride.
|
||||||
|
|
||||||
|
Here we are pinning our package to dependencies which come from NixOS/Nixpkgs release branch 24.05.
|
||||||
|
|
||||||
|
If we `nix build .#what-is-my-ip` and `readlink result` we get:
|
||||||
|
|
||||||
|
**/nix/store/lr6wlz2652r35rwzc79samg77l6iqmii-what-is-my-ip**
|
||||||
|
|
||||||
|
And, of course, we can run our result:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ ./result/bin/what-is-my-ip
|
||||||
|
24.5.113.148
|
||||||
|
```
|
||||||
|
|
||||||
|
Or run it from the Flake:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ nix run .#what-is-my-ip
|
||||||
|
24.5.113.148
|
||||||
|
```
|
||||||
|
|
||||||
|
Now that this is in Nix and we've modeled our dependencies, we can do _fun_ things like generate graph diagrams to view them (click the image to view larger).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ nix-store --query --graph $(readlink result) | nix shell nixpkgs#graphviz -c dot -Tpng -o what-is-my-ip-deps.png
|
||||||
|
```
|
||||||
|
|
||||||
|
[![Image of what-is-my-ip dependencies as a graph](/assets/images/what-is-my-ip-deps.png)](/assets/images/what-is-my-ip-deps.png)
|
||||||
|
|
||||||
|
Let's create a _developer environment_ and bring in our new tool.
|
||||||
|
This is a great way to create developer environment with reproducible tools
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz") {};
|
||||||
|
what-is-my-ip = import ./what-is-my-ip.nix {inherit pkgs;};
|
||||||
|
in
|
||||||
|
pkgs.mkShell {
|
||||||
|
packages = [what-is-my-ip];
|
||||||
|
shellHook = ''
|
||||||
|
echo "Hello, Nix!"
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
❯ nix-shell what-is-my-ip-shell.nix
|
||||||
|
Hello, Nix!
|
||||||
|
|
||||||
|
[nix-shell:~/tutorial]$ which what-is-my-ip
|
||||||
|
/nix/store/lr6wlz2652r35rwzc79samg77l6iqmii-what-is-my-ip/bin/what-is-my-ip
|
||||||
|
```
|
||||||
|
|
||||||
|
🕵️ Notice that the hash **lr6wlz2652r35rwzc79samg77l6iqmii** is _exactly_ the same which we built earlier.
|
||||||
|
|
||||||
|
We can now do binary or source deployments 🚀🛠️📦 since we know the full dependency closure of our tool. We simply copy the necessary _/nix/store_ paths to another machine with Nix installed.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
❯ nix copy --to ssh://nixie.tail9f4b5.ts.net \
|
||||||
|
$(nix-build what-is-my-ip.nix) --no-check-sigs
|
||||||
|
|
||||||
|
❯ ssh nixie.tail9f4b5.ts.net
|
||||||
|
|
||||||
|
[fmzakari@nixie:~]$ /nix/store/lr6wlz2652r35rwzc79samg77l6iqmii-what-is-my-ip/bin/what-is-my-ip
|
||||||
|
98.147.178.19
|
||||||
|
```
|
||||||
|
|
||||||
|
Maybe though you are stuck with Kubernetes or Docker. Let's use Nix to create an OCI compatible image.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz") {};
|
||||||
|
what-is-my-ip = import ./what-is-my-ip.nix {inherit pkgs;};
|
||||||
|
in
|
||||||
|
pkgs.dockerTools.buildImage {
|
||||||
|
name = "what-is-my-ip-docker";
|
||||||
|
config = {
|
||||||
|
Cmd = ["${what-is-my-ip}/bin/what-is-my-ip"];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
❯ docker load < $(nix-build what-is-my-ip-docker.nix)
|
||||||
|
Loaded image: what-is-my-ip-docker:c9g6x30invdq1bjfah3w1aw5w52vkdfn
|
||||||
|
|
||||||
|
❯ docker run -it what-is-my-ip-docker:c9g6x30invdq1bjfah3w1aw5w52vkdfn
|
||||||
|
24.5.113.148
|
||||||
|
```
|
||||||
|
|
||||||
|
Cool! Nix + Docker integration perfectly. The image produced has only the files exactly necessary to run the tool provided, effectively **distroless**.
|
||||||
|
|
||||||
|
Finally, let's take the last step and create a reproducible operating system using NixOS to contain only the programs we want.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz";
|
||||||
|
pkgs = import nixpkgs {};
|
||||||
|
what-is-my-ip = import ./what-is-my-ip.nix {inherit pkgs;};
|
||||||
|
nixos = import "${nixpkgs}/nixos" {
|
||||||
|
configuration = {
|
||||||
|
users.users.alice = {
|
||||||
|
isNormalUser = true;
|
||||||
|
# enable sudo
|
||||||
|
extraGroups = ["wheel"];
|
||||||
|
packages = [
|
||||||
|
what-is-my-ip
|
||||||
|
];
|
||||||
|
initialPassword = "swordfish";
|
||||||
|
};
|
||||||
|
|
||||||
|
system.stateVersion = "24.05";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
nixos.vm
|
||||||
|
```
|
||||||
|
|
||||||
|
```console
|
||||||
|
❯ nix-build what-is-my-ip-vm.nix
|
||||||
|
|
||||||
|
❯ QEMU_KERNEL_PARAMS=console=ttyS0 ./result/bin/run-nixos-vm -nographic; reset
|
||||||
|
|
||||||
|
<<< Welcome to NixOS 24.05pre-git (x86_64) - ttyS0 >>>
|
||||||
|
|
||||||
|
Run 'nixos-help' for the NixOS manual.
|
||||||
|
|
||||||
|
nixos login: alice
|
||||||
|
Password:
|
||||||
|
|
||||||
|
[alice@nixos:~]$ which what-is-my-ip
|
||||||
|
/etc/profiles/per-user/alice/bin/what-is-my-ip
|
||||||
|
|
||||||
|
[alice@nixos:~]$ readlink $(which what-is-my-ip)
|
||||||
|
/nix/store/lr6wlz2652r35rwzc79samg77l6iqmii-what-is-my-ip/bin/what-is-my-ip
|
||||||
|
|
||||||
|
[alice@nixos:~]$ what-is-my-ip
|
||||||
|
24.5.113.148
|
||||||
|
```
|
||||||
|
|
||||||
|
💥 Hash **lr6wlz2652r35rwzc79samg77l6iqmii** present again!
|
||||||
|
|
||||||
|
We took a relatively simple script through a variety of applications in the Nix ecosystem: build recipe, shell, docker image and finally NixOS VM.
|
||||||
|
|
||||||
|
Hopefully, seeing the _fun things_ you can do with Nix might inspire you to push through the hard parts.
|
||||||
|
|
||||||
|
There is a golden pot 💰 at the end of this rainbow 🌈 awaiting you.
|
||||||
|
|
||||||
|
**Learn Nix the fun way.**
|
||||||
|
|
||||||
|
[0]: https://fzakaria.com/2024/07/05/learn-nix-the-fun-way.html
|
BIN
what-is-my-ip-deps.png
Normal file
BIN
what-is-my-ip-deps.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 163 KiB |
5
what-is-my-ip.nix
Normal file
5
what-is-my-ip.nix
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{pkgs}:
|
||||||
|
pkgs.writeShellScriptBin "what-is-my-ip" ''
|
||||||
|
${pkgs.curl}/bin/curl -s http://httpbin.org/get | \
|
||||||
|
${pkgs.jq}/bin/jq --raw-output .origin
|
||||||
|
''
|
Loading…
Reference in a new issue