#+Title: How I use nix #+Author: Yann Esposito #+Email: yann@esposito.host #+Date: [2020-06-14 Sun] #+KEYWORDS: nix, programming #+DESCRIPTION: In this article I explain how I use nix. #+DESCRIPTION: As a brew replacement, as home environment manager, #+DESCRIPTION: to have reproductible dev environment. #+LANGUAGE: en #+LANG: en #+OPTIONS: H:5 auto-id:t toc:nil #+STARTUP: showeverything Have you ever written a small script and you update your system and this stop working? Have you copied your tool/script to another machine it doesn't work because some dependency is missing? Have you tried to sync your dotfiles to another env and there are a few details not working? Some missing dependency? If the answer is yes, then [[https://nixos.org/nix][nix]] can help. ** Scripts :PROPERTIES: :CUSTOM_ID: scripts :END: Suppose you want to write a portable script. For example, the script I use to minify my CSS. Here it is: #+begin_src shell #!/usr/bin/env nix-shell #!nix-shell --pure #!nix-shell -i bash #!nix-shell -I nixpkgs="https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz" #!nix-shell -p bash minify minify "$1" > "$2" #+end_src So let's analyze each line of the header block: - ~#!/usr/bin/env nix-shell~ :: basic, use ~nix-shell~ to run the script. - ~#!nix-shell --pure~ :: only use dependencies installed in this nix shell environment. A bit as if the PATH environment variable was emptied. - ~#!nix-shell -i bash~ :: tell ~nix-shell~ to run ~bash~ - ~#!nix-shell -I nixpkgs="https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz"~ :: pin the nixpkgs using this archive. - ~#!nix-shell -p bash minify~ :: install ~bash~ and ~minify~ in the nix shell. Now if the script is run on a machine with ~nix~ installed you can be pretty sure it will work as expected. Even if I update my OS and I forget about this script for a few years. As long as I can install nix on the new system and I could download the tar file the script will be run the same way as the day I wrote it. **Remark**: You can use any shell (like ~fish~, ~zsh~) but also other languages ~python~, ~haskell~, etc... ** Temporary working env :PROPERTIES: :CUSTOM_ID: temporary-working-env :END: Quite often, I need to do something, and run a specific command that need me to install a very specific command. And I'm pretty sure I will not use this tool ever again. For those cases, what I do, is generally run my command directly with a fresh ~nix-shell~. #+begin_src shell > nix-shell -p httpie [nix-shell:~]$ ... here I can use httpie ... #+end_src ** Home Manager :PROPERTIES: :CUSTOM_ID: home-manager :END: A few years ago I used =brew= to install the tools I need. With =nix= you can install a new tool with ~nix-env -i~ instead of ~brew install~. Still recently I prefer to use [[https://github.com/rycee/home-manager][home-manager]]. The main advantage is that it is even more reproductible and can easily be shared accross different machines. Mainly when I need a new binary I add it in a description list in the file =~/.config/nixpkgs/home.nix=. It looks like this: #+begin_src nix home.packages = with pkgs; [ # emacs emacsMacport imagemagick gnupg # shell direnv ... ]; #+end_src then I simply run ~home-manager switch~ and I've got all those tools in my env. *** Pinning the packages :PROPERTIES: :CUSTOM_ID: pinning-the-packages :END: #+begin_src nix { config, pkgs, ... }: let # ... pkgs = import (fetchGit { name = "nixpkgs20"; url = "https://github.com/NixOS/nixpkgs"; # obtained via # git ls-remote https://github.com/NixOS/nixpkgs nixpkgs-20.03-darwin ref = "refs/heads/nixpkgs-20.03-darwin"; rev = "58f884cd3d89f47672e649c6edfb2382d4afff6a"; }) {}; # ... in { # ... } #+end_src *** Specific tools :PROPERTIES: :CUSTOM_ID: specific-tools :END: There are a few noticiable artifact here: The first one is ~weechat~ is a very specify build of weechat with the plugin I need. So here is the block I use: #+begin_src nix let ... weechat-with-weeslack = weechat.override { configure = { availablePlugins, ... }: { # plugins = with availablePlugins; [ python perl guile ]; scripts = with pkgs.weechatScripts; [ wee-slack ]; }; }; ... in { ... home.packages = with pkgs; [... weechat-with-wee-slack ...]; ... } #+end_src Even if this looks cryptic. The important detail is just that there exists a way to say to nix I'd like to use weechat (an IRC client) with the wee-slack client (which uses python). And nix handle the rest for me. *** Another nice tool is =sws= :PROPERTIES: :CUSTOM_ID: another-nice-tool-is--sws- :END: I use macOS so even though I'm using a darwin focused nixpkgs sometimes a few package can be broken and can't be installed. That occurred with [[https://hackage.haskell.org/package/sws][=sws=]] during the upgrade to 20.03 on darwin. This is a simple tool that need haskell to be compiled locally and installed. Here is how I could install it: #+begin_src nix let ... rel19 = import (fetchGit { name = "nixpkgs19"; url = "https://github.com/NixOS/nixpkgs"; ref = "refs/heads/nixpkgs-19.09-darwin"; rev = "2f9bafaca90acd010cccd0e79e5f27aa7537957e"; }) {}; ... in home.packages = with pkgs; [ ... ghc rel19.haskellPackages.sws ... ] #+end_src So I used the older version from 19.09. [fn:desktop]: I'm using macOS, and I have multiple macs. A laptop and two desktop machines. Using the 27″ iMac is my ultimate work station. It really enhance my productivity. Still the laptop is a superior work environment for more casual tasks. I guess I might write someday about my full work environment. ** Install :PROPERTIES: :CUSTOM_ID: install :END: First, let's start by the bad news. Recent macOS security policy made nix a bit harder to install on a mac. See [[https://hydra.nixos.org/build/119559243/download/1/manual/#sect-macos-installation][macOS Installation instructions]]. Once you have nix installed you should update the nix-channel. Mainly a nix-channels is where are the definitions of all the packages. See [[https://hydra.nixos.org/build/119559243/download/1/manual/#sec-channels][nixOS documentation]].