344 lines
12 KiB
Org Mode
344 lines
12 KiB
Org Mode
#+title: P’undrak’s Litterate Nix Config
|
||||
|
|
#+setupfile: headers
|
|||
|
|
#+property: header-args:emacs-lisp :lexical t :exports none :tangle no
|
|||
|
|
|
|||
|
|
* Index
|
|||
|
|
Hi, I’m P’undrak (pronounced /PUN-drak/, or more exactly {{{phon(pʰynɖak̚)}}}),
|
|||
|
|
also known as Lucien Cartier-Tilet. If you want to know more about me,
|
|||
|
|
you can head to my [[https://phundrak.com/en][main website]].
|
|||
|
|
|
|||
|
|
This website documents my entire Linux configuration, managed through
|
|||
|
|
NixOS. Most of my programs are configured through Nix, following the
|
|||
|
|
[[https://github.com/Doc-Steve/dendritic-design-with-flake-parts][dendritic pattern]].
|
|||
|
|
|
|||
|
|
To give you a tl;dr, this basically means that each aspect of my
|
|||
|
|
configuration (be it a concept or an application) tries to only have a
|
|||
|
|
single source of truth. But as you can see with my Emacs
|
|||
|
|
configuration, some aspects can be quite extensive and require several
|
|||
|
|
pages to be properly organised.
|
|||
|
|
|
|||
|
|
** License
|
|||
|
|
See [[https://labs.phundrak.com/phundrak/dotfiles/src/branch/master/LICENSE.org][the repository’s license file]].
|
|||
|
|
|
|||
|
|
** Note on What a Literate Configuration Is
|
|||
|
|
As implied by the title of this website, my configuration is a
|
|||
|
|
litterate one. This means that every page of this website comes from
|
|||
|
|
an Emacs org-mode file, and the code you see as code blocks are the
|
|||
|
|
actual source of my configuration.
|
|||
|
|
|
|||
|
|
Org-mode offers to “tangle” these code blocks into full files, which
|
|||
|
|
can then be used as any other source file. If you visit this website’s
|
|||
|
|
repository, you will find the source org files alongside their
|
|||
|
|
resulting Nix file if any is generated by said file. For instance,
|
|||
|
|
this very page generates my =modules/default.nix= file, as we’ll see
|
|||
|
|
below.
|
|||
|
|
|
|||
|
|
This also implies heavy usage of the [[https://orgmode.org/manual/Noweb-Reference-Syntax.html][noweb]] syntax. If you encounter
|
|||
|
|
some code that looks ~<<like-this>>~, org-mode will replace this snippet
|
|||
|
|
with another code snippet declared elsewhere in my configuration. If
|
|||
|
|
you see some code that looks ~<<like-this()>>~, some generating code
|
|||
|
|
will run and replace this piece of text with the text generated. A
|
|||
|
|
quick example:
|
|||
|
|
#+begin_src elisp
|
|||
|
|
(defun hello ()
|
|||
|
|
<<generate-docstring()>>
|
|||
|
|
<<print-hello>>)
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
Will instead appear as
|
|||
|
|
#+begin_src emacs-lisp :noweb yes
|
|||
|
|
(defun hello ()
|
|||
|
|
<<generate-docstring()>>
|
|||
|
|
<<print-hello>>)
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
This is because I have the block of code below named
|
|||
|
|
~generate-docstring~ which generates an output, which replaces its noweb
|
|||
|
|
tag. You can recognize noweb snippets generating code with the
|
|||
|
|
parenthesis. Often, such blocks aren’t visible in my HTML exports, but
|
|||
|
|
you can still see them if you open the actual org source file.
|
|||
|
|
#+name: generate-docstring
|
|||
|
|
#+begin_src emacs-lisp
|
|||
|
|
(concat "\""
|
|||
|
|
"Print \\\"Hello World!\\\" in the minibuffer."
|
|||
|
|
"\"")
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
On the other hand, noweb snippets without parenthesis simply replace
|
|||
|
|
the snippet with the equivalent named code block. For instance the one
|
|||
|
|
below is named ~print-hello~ and is placed as-is in the target source
|
|||
|
|
block.
|
|||
|
|
#+name: print-hello
|
|||
|
|
#+begin_src emacs-lisp
|
|||
|
|
(message "Hello World!")
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
** Root Nix Configuration
|
|||
|
|
As this configuration uses [[https://flake.parts/][flake-parts]] and [[https://flake-file.denful.dev/][flake-file]], I need a
|
|||
|
|
=modules/default.nix= which defines how to manage my config.
|
|||
|
|
|
|||
|
|
For the record, I use [[https://github.com/nix-community/nh][nh]] to compile my configuration and switch to
|
|||
|
|
newer generations of my OS and home. This allows me to simply run =nh
|
|||
|
|
os switch= to upgrade my system, or =nh home switch= to upgrade my
|
|||
|
|
[[https://nix-community.github.io/home-manager/][home-manager]] configuration. This, therefore, requires to have [[https://nixos.wiki/wiki/flakes][flakes]]
|
|||
|
|
outputs in the form of =nixosConfigurations.marpa= (with =marpa= being the
|
|||
|
|
hostname of a machine) and =homeConfigurations.phundrak= or
|
|||
|
|
=homeConfigurations.phundrak@marpa= (with =phundrak= being the username of
|
|||
|
|
one of the users of a machine).
|
|||
|
|
|
|||
|
|
But first things first, let’s declare the closure that will
|
|||
|
|
encapsulate the rest of the file:
|
|||
|
|
#+begin_src nix :tangle default.nix
|
|||
|
|
{
|
|||
|
|
inputs,
|
|||
|
|
lib,
|
|||
|
|
config,
|
|||
|
|
...
|
|||
|
|
}: {
|
|||
|
|
<<modules-imports>>
|
|||
|
|
|
|||
|
|
<<options-home>>
|
|||
|
|
|
|||
|
|
config = {
|
|||
|
|
<<flake-inputs>>
|
|||
|
|
<<dev-arch>>
|
|||
|
|
|
|||
|
|
flake.lib = {
|
|||
|
|
<<lib-mkNixos>>
|
|||
|
|
<<lib-mkHome>>
|
|||
|
|
<<lib-mkPinetab>>
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
<<configs-decl>>
|
|||
|
|
<<devshell>>
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
To get our configuration to work, we need to import the modules from
|
|||
|
|
flake-parts and flake-file.
|
|||
|
|
#+name: modules-imports
|
|||
|
|
#+begin_src nix
|
|||
|
|
imports = [
|
|||
|
|
inputs.flake-parts.flakeModules.modules
|
|||
|
|
inputs.flake-file.flakeModules.default
|
|||
|
|
];
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
Now, we can declare an option to make =homeConfigurations= available as
|
|||
|
|
an output for our flakes.
|
|||
|
|
#+name: options-home
|
|||
|
|
#+begin_src nix
|
|||
|
|
options.flake.homeConfigurations = lib.mkOption {
|
|||
|
|
type = lib.types.lazyAttrsOf lib.types.raw;
|
|||
|
|
default = {};
|
|||
|
|
};
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
*** Setting Things Up
|
|||
|
|
We need to declare a few inputs for our flake, thanks to
|
|||
|
|
flake-file. The first one is =flake-utils=, which allows me to easily
|
|||
|
|
declare my development shell for this repository both for my x86-64
|
|||
|
|
machines and my aarch64 tablet, a PineTab 2. Then, speaking of the
|
|||
|
|
PineTab, I need some specific nixpkgs input, to handle a bug in the
|
|||
|
|
compilation of the kernel, as well as the flake =rockchip= to support
|
|||
|
|
said kernel.
|
|||
|
|
#+name: flake-inputs
|
|||
|
|
#+begin_src nix
|
|||
|
|
flake-file.inputs = {
|
|||
|
|
flake-utils.url = "github:numtide/flake-utils";
|
|||
|
|
nixpkgsPinetab2Kernel.url = "github:nixos/nixpkgs/e73de5be04e0eff4190a1432b946d469c794e7b4";
|
|||
|
|
rockchip = {
|
|||
|
|
url = "github:raboof/nixos-rockchip/pinetab-linux-7.0";
|
|||
|
|
inputs.utils.follows = "flake-utils";
|
|||
|
|
inputs.nixpkgsStable.follows = "nixpkgsStable";
|
|||
|
|
inputs.nixpkgsUnstable.follows = "nixpkgsPinetab2Kernel";
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
As I said, I support two architectures for my development shell, so
|
|||
|
|
let’s declare them.
|
|||
|
|
#+name: dev-arch
|
|||
|
|
#+begin_src nix
|
|||
|
|
systems = ["x86_64-linux" "aarch64-linux"];
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
Now, we can declare some functions for our flake. These three
|
|||
|
|
functions’ role is to create the output of each machine and user as
|
|||
|
|
required. First, let’s create the function to get a machine’s
|
|||
|
|
configuration based on its name.
|
|||
|
|
#+name: lib-mkNixos
|
|||
|
|
#+begin_src nix
|
|||
|
|
mkNixos = system: name: {
|
|||
|
|
${name} = inputs.nixpkgs.lib.nixosSystem {
|
|||
|
|
modules = [
|
|||
|
|
config.flake.modules.nixos.${name}
|
|||
|
|
{nixpkgs.hostPlatform = lib.mkDefault system;}
|
|||
|
|
];
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
What this does is basically declare a Nix system named after the
|
|||
|
|
host’s name, created with its module (located in =modules/hosts/=) and
|
|||
|
|
the related nixpkgs with the appropriate architecture. For now, the
|
|||
|
|
only architecture used with this function is x86-64, but I’m not
|
|||
|
|
excluding the posibility to have other hosts using an ARM CPU.
|
|||
|
|
|
|||
|
|
=mkHome= is somewhat similar: it declares a home-manager module,
|
|||
|
|
importing the module related to the user and the machine it will be
|
|||
|
|
deployed on.
|
|||
|
|
#+name: lib-mkHome
|
|||
|
|
#+begin_src nix
|
|||
|
|
mkHome = system: userName: hostName: {
|
|||
|
|
"${userName}@${hostName}" = inputs.home-manager.lib.homeManagerConfiguration {
|
|||
|
|
pkgs = inputs.nixpkgs.legacyPackages.${system};
|
|||
|
|
extraSpecialArgs = {
|
|||
|
|
inherit inputs;
|
|||
|
|
bunBaseline = config.flake.packages.${system}.bun-baseline;
|
|||
|
|
};
|
|||
|
|
modules = [config.flake.modules.homeManager."${userName}-${hostName}"];
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
You may notice the declaration of =bunBaseline=. This is because of my
|
|||
|
|
Thinkpad x220; the default binary distributed for Bun uses CPU
|
|||
|
|
instructions that are more recent than this laptop’s CPU, which
|
|||
|
|
results in fatal errors trying to run it. Therefore, =bunBaseline= is
|
|||
|
|
here to either compile Bun on my Thinkpad with the correct instruction
|
|||
|
|
set, or simply use a prepackaged Bun if the host supports it.
|
|||
|
|
|
|||
|
|
Lastly, I have a function dedicated to building NixOS on my PineTab 2.
|
|||
|
|
#+name: lib-mkPinetab
|
|||
|
|
#+begin_src nix
|
|||
|
|
mkPinetab = buildPlatform: variantModule: {
|
|||
|
|
pinetab2 = inputs.nixpkgs.lib.nixosSystem {
|
|||
|
|
system = "aarch64-linux";
|
|||
|
|
modules = [
|
|||
|
|
inputs.rockchip.nixosModules.sdImageRockchip
|
|||
|
|
inputs.rockchip.nixosModules.dtOverlayPCIeFix
|
|||
|
|
inputs.rockchip.nixosModules.noZFS
|
|||
|
|
config.flake.modules.nixos.pinetab2-base
|
|||
|
|
variantModule
|
|||
|
|
{
|
|||
|
|
rockchip.uBoot = inputs.rockchip.packages.${buildPlatform}.uBootPineTab2;
|
|||
|
|
boot.kernelPackages =
|
|||
|
|
inputs.rockchip.legacyPackages.${buildPlatform}.kernel_linux_7_0_pinetab_unstable;
|
|||
|
|
hardware.firmware = [inputs.rockchip.packages.aarch64-linux.bes2600];
|
|||
|
|
nixpkgs.config.allowUnfreePredicate = pkg:
|
|||
|
|
builtins.elem (inputs.nixpkgs.lib.getName pkg) ["bes2600-firmware"];
|
|||
|
|
}
|
|||
|
|
];
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
I won’t go into too much details here, but it mostly imports the
|
|||
|
|
modules required to run NixOS on the Pinetab as well as explicitly
|
|||
|
|
import the bes2600 firmware module to make Bluetooth and Wi-Fi
|
|||
|
|
available on the tablet.
|
|||
|
|
|
|||
|
|
*** Declaring the Actual Outputs
|
|||
|
|
With all that being said, we can now actually declare our
|
|||
|
|
configurations. You can see below the table of hosts I have, with
|
|||
|
|
their CPU architecture, and which users are present on the system.
|
|||
|
|
|
|||
|
|
#+name: table-hosts
|
|||
|
|
| Host | Architecture | Users | Comment |
|
|||
|
|
|----------+---------------+-----------------+------------------|
|
|||
|
|
| marpa | x86_64-linux | phundrak | Main workstation |
|
|||
|
|
| gampo | x86_64-linux | phundrak | Thinkpad x220 |
|
|||
|
|
| tilo | x86_64-linux | phundrak | Home Server |
|
|||
|
|
| elcafe | x86_64-linux | phundrak, creug | Server |
|
|||
|
|
| NaroMk3 | x86_64-linux | phundrak | Cloud Server |
|
|||
|
|
| pinetab2 | aarch64-linux | phundrak | PineTab 2 tablet |
|
|||
|
|
|
|||
|
|
#+name: make-hosts
|
|||
|
|
#+begin_src emacs-lisp :exports none :var hosts=table-hosts :cache yes
|
|||
|
|
(mapconcat
|
|||
|
|
(lambda (line)
|
|||
|
|
(let ((hostname (car line))
|
|||
|
|
(arch (nth 1 line)))
|
|||
|
|
(format "(config.flake.lib.mkNixos \"%s\" \"%s\")"
|
|||
|
|
arch
|
|||
|
|
hostname)))
|
|||
|
|
(-filter (lambda (host) (not (string= "pinetab2" (car host))))
|
|||
|
|
hosts)
|
|||
|
|
"\n")
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
#+RESULTS[f5f8b3a89622e7526a83cbf722c4b7c77ff7bd86]: make-hosts
|
|||
|
|
: (config.flake.lib.mkNixos "x86_64-linux" "marpa")
|
|||
|
|
: (config.flake.lib.mkNixos "x86_64-linux" "gampo")
|
|||
|
|
: (config.flake.lib.mkNixos "x86_64-linux" "tilo")
|
|||
|
|
: (config.flake.lib.mkNixos "x86_64-linux" "elcafe")
|
|||
|
|
: (config.flake.lib.mkNixos "x86_64-linux" "NaroMk3")
|
|||
|
|
|
|||
|
|
#+name: make-home
|
|||
|
|
#+begin_src emacs-lisp :exports none :var hosts=table-hosts :cache yes
|
|||
|
|
(require 's)
|
|||
|
|
|
|||
|
|
(mapconcat
|
|||
|
|
(lambda (line)
|
|||
|
|
(let ((hostname (car line))
|
|||
|
|
(arch (nth 1 line))
|
|||
|
|
(users (mapcar #'s-trim (s-split "," (nth 2 line) t))))
|
|||
|
|
(mapconcat (lambda (user)
|
|||
|
|
(format "(config.flake.lib.mkHome \"%s\" \"%s\" \"%s\")"
|
|||
|
|
arch user hostname))
|
|||
|
|
users
|
|||
|
|
"\n")))
|
|||
|
|
hosts
|
|||
|
|
"\n")
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
#+RESULTS[301fccb07591fffc8d7bdfd7d2958602150aa688]: make-home
|
|||
|
|
: (config.flake.lib.mkHome "x86_64-linux" "phundrak" "marpa")
|
|||
|
|
: (config.flake.lib.mkHome "x86_64-linux" "phundrak" "gampo")
|
|||
|
|
: (config.flake.lib.mkHome "x86_64-linux" "phundrak" "tilo")
|
|||
|
|
: (config.flake.lib.mkHome "x86_64-linux" "phundrak" "elcafe")
|
|||
|
|
: (config.flake.lib.mkHome "x86_64-linux" "creug" "elcafe")
|
|||
|
|
: (config.flake.lib.mkHome "x86_64-linux" "phundrak" "NaroMk3")
|
|||
|
|
: (config.flake.lib.mkHome "aarch64-linux" "phundrak" "pinetab2")
|
|||
|
|
|
|||
|
|
This translates into this Nix code.
|
|||
|
|
#+name: configs-decl
|
|||
|
|
#+begin_src nix :noweb yes
|
|||
|
|
flake.nixosConfigurations = lib.mkMerge [
|
|||
|
|
<<make-hosts()>>
|
|||
|
|
(config.flake.lib.mkPinetab "x86_64-linux" config.flake.modules.nixos.pinetab2-gnome)
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
flake.homeConfigurations = lib.mkMerge [
|
|||
|
|
<<make-home()>>
|
|||
|
|
];
|
|||
|
|
#+end_src
|
|||
|
|
|
|||
|
|
*** Development Shell
|
|||
|
|
Lastly, here is the declaration of my development shell. It really is
|
|||
|
|
only useful if I’m on a new machine or a machine that is not quite up
|
|||
|
|
to date and misses some packages I rely on to work on my dotfiles.
|
|||
|
|
Namely, these are =nh= (which I mentioned above), Jujutsu, =jj-cz= (a
|
|||
|
|
Commitizen alternative for Jujutsu I’m working on), and Git itself as
|
|||
|
|
a fallback.
|
|||
|
|
#+name: devshell
|
|||
|
|
#+begin_src nix
|
|||
|
|
perSystem = {
|
|||
|
|
pkgs,
|
|||
|
|
system,
|
|||
|
|
...
|
|||
|
|
}: {
|
|||
|
|
formatter = pkgs.alejandra;
|
|||
|
|
devShells.default = pkgs.mkShell {
|
|||
|
|
buildInputs = [
|
|||
|
|
pkgs.nh
|
|||
|
|
pkgs.jujutsu
|
|||
|
|
pkgs.git
|
|||
|
|
inputs.jj-cz.packages.${system}.default
|
|||
|
|
];
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
#+end_src
|