diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..ca22a7f --- /dev/null +++ b/.envrc @@ -0,0 +1,11 @@ +#!/bin/sh + +source "$( + nix eval \ + --no-update-lock-file \ + --no-write-lock-file \ + --no-warn-dirty \ + --accept-flake-config \ + .#__std.direnv_lib 2>/dev/null +)" +use std comb //_QUEEN/devshells:default diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6205b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# direnv/direnv + divnix/std +.std + +# numtide/devshell +.data + +# nixos/nix +result* + +# nixago: ignore-linked-files +.conform.yaml +lefthook.yml +treefmt.toml diff --git a/comb/home/colmenaConfigurations.nix b/comb/home/colmenaConfigurations.nix new file mode 100644 index 0000000..e32c9c7 --- /dev/null +++ b/comb/home/colmenaConfigurations.nix @@ -0,0 +1,10 @@ +{ + nom = { + networking.hostName = "nom"; + deployment = { + allowLocalDeployment = true; + targetHost = null; + }; + imports = [cell.nixosConfigurations.nom]; + }; +} diff --git a/comb/home/hardwareProfiles.nix b/comb/home/hardwareProfiles.nix new file mode 100644 index 0000000..e6c333e --- /dev/null +++ b/comb/home/hardwareProfiles.nix @@ -0,0 +1,29 @@ +{ + nom = { + pkgs, + config, + lib, + ... + }: { + imports = [ + inputs.disko.nixosModules.disko + {disko.devices = cell.diskoConfigurations.nom;} + inputs.nixos-hardware.nixosModules.common-pc-laptop + inputs.nixos-hardware.nixosModules.common-pc-laptop-ssd + inputs.nixos-hardware.nixosModules.common-cpu-amd-pstate + inputs.nixos-hardware.nixosModules.common-gpu-amd + ]; + + boot.initrd.availableKernelModules = ["nvme" "xhci_pci" "usb_storage" "sd_mod" "sdhci_pci"]; + boot.initrd.kernelModules = []; + boot.kernelModules = ["kvm-amd"]; + boot.extraModulePackages = []; + + networking.useDHCP = lib.mkDefault true; + + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + hardware.enableRedistributableFirmware = true; + hardware.enableAllFirmware = true; + boot.kernelPackages = pkgs.linuxPackages_latest; + }; +} diff --git a/comb/home/nixosConfigurations.nix b/comb/home/nixosConfigurations.nix new file mode 100644 index 0000000..b753ec4 --- /dev/null +++ b/comb/home/nixosConfigurations.nix @@ -0,0 +1,162 @@ +{ + nom = {pkgs, ...}: { + bee.system = "x86_64-linux"; + bee.pkgs = import inputs.nixos { + inherit (inputs.nixpkgs) system; + config.allowUnfree = true; + overlays = []; + }; + imports = [ + cell.hardwareProfiles.nom + ]; + + # Disable unnecessary stuff from the nixos defaults. + services.udisks2.enable = false; + networking.dhcpcd.enable = false; + networking.firewall.enable = false; + security.sudo.enable = false; + + documentation.dev.enable = true; + + # swapDevices = [ + # { + # device = "/.swapfile"; + # size = 8192; # ~8GB - will be autocreated + # } + # ]; + # Use the systemd-boot EFI boot loader. + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + nix.settings = { + auto-optimise-store = true; + allowed-users = ["@wheel"]; + trusted-users = ["root" "@wheel"]; + experimental-features = [ + "flakes" + "nix-command" + ]; + accept-flake-config = true; + }; + + # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. + + time.timeZone = "Europe/Berlin"; + + networking.useDHCP = false; + networking.interfaces.wlp2s0.useDHCP = true; + networking.networkmanager.enable = true; + systemd.services.NetworkManager-wait-online = { + enable = false; + serviceConfig.TimeoutSec = 15; + wantedBy = ["network-online.target"]; + }; + + # Configure network proxy if necessary + # networking.proxy.default = "http://user:password@proxy:port/"; + # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; + + # Select internationalisation properties. + i18n.defaultLocale = "C.UTF-8"; + console = { + font = "Lat2-Terminus16"; + keyMap = "de-latin1-nodeadkeys"; + }; + + services.sshd.enable = true; + + # Enable sound. + sound.enable = true; + sound.mediaKeys.enable = true; + + # Define a user account. Don't forget to set a password with ‘passwd’. + users = { + users.lar = { + shell = pkgs.zsh; + isNormalUser = true; + initialPassword = "password123"; + extraGroups = ["wheel"]; # Enable ‘sudo’ for the user. + }; + }; + + # List packages installed in system profile. To search, run: + # $ nix search wget + environment.systemPackages = with pkgs; [ + xclip + tty-share + alacritty + element-desktop + firefox + chromium + enpass + # Office + libreoffice + onlyoffice-bin + beancount + fava + direnv + # Git & Tools + git + gh + gitoxide + ghq + # Nix + # nil # nix language server + rnix-lsp # nix language server + alejandra # nix formatter + # Python + (python3Full.withPackages (p: + with p; [ + numpy + pandas + ptpython + requests + scipy + ])) + poetry # python project files + black # python formatter + ]; + + # Programs configuration + programs.starship.enable = true; + programs.nix-ld.enable = true; # quality of life for downloaded programs + programs.zsh = { + enable = true; + enableCompletion = true; + autosuggestions.enable = true; + autosuggestions.async = true; + syntaxHighlighting.enable = true; + shellInit = '' + eval "$(direnv hook zsh)" + ''; + }; + programs.git = { + enable = true; + config = { + init.defaultBranch = "main"; + core.autocrlf = "input"; + pull.rebase = true; + rebase.autosquash = true; + rerere.enable = true; + }; + }; + programs.ssh = { + extraConfig = '' + Host github.com + User git + Hostname github.com + IdentityFile ~/.ssh/lar + Host gitlab.com + PreferredAuthentications publickey + IdentityFile ~/.ssh/lar + ''; + }; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "22.11"; # Did you read the comment? + }; +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..597f074 --- /dev/null +++ b/flake.nix @@ -0,0 +1,122 @@ +{ + description = "The Hive - The secretly open NixOS-Society"; + inputs.std.url = "github:divnix/std"; + inputs.std.inputs.nixpkgs.follows = "nixpkgs"; + inputs.std.inputs.mdbook-kroki-preprocessor.follows = "std/blank"; + inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + + # tools + inputs = { + nixos-generators.url = "github:nix-community/nixos-generators"; + nixos-generators.inputs.nixpkgs.follows = "nixpkgs"; + nixos-generators.inputs.nixlib.follows = "nixpkgs"; + colmena.url = "github:zhaofengli/colmena"; + colmena.inputs.nixpkgs.follows = "nixpkgs"; + colmena.inputs.stable.follows = "std/blank"; + colmena.inputs.flake-utils.follows = "std/flake-utils"; + disko.url = "github:nix-community/disko"; + disko.inputs.nixpkgs.follows = "nixpkgs"; + nixos-hardware.url = "github:nixos/nixos-hardware"; + }; + + # nixpkgs & home-manager + inputs = { + nixos.follows = "nixpkgs"; + home.url = "github:nix-community/home-manager"; + }; + + outputs = { + std, + self, + ... + } @ inputs: + std.growOn { + inherit inputs; + cellsFrom = ./comb; + # debug = ["cells" "x86_64-linux"]; + cellBlocks = with std.blockTypes; [ + # modules implement + (functions "nixosModules") + (functions "homeModules") + (functions "devshellModules") + + # profiles activate + (functions "hardwareProfiles") + (functions "nixosProfiles") + (functions "homeProfiles") + (functions "devshellProfiles") + + # suites aggregate profiles + (functions "nixosSuites") + (functions "homeSuites") + + # configurations can be deployed + (data "nixosConfigurations") + (data "colmenaConfigurations") + (data "homeConfigurations") + (data "diskoConfigurations") + + # devshells can be entered + (devshells "devshells") + + # jobs can be run + (runnables "jobs") + + # library holds shared knowledge made code + (functions "library") + ]; + nixpkgsConfig = { + allowUnfree = true; + }; + } + # soil + { + packages.x86_64-linux = {inherit (inputs.disko.packages.x86_64-linux) disko;}; + devShells = std.harvest self ["_QUEEN" "devshells"]; + } + { + # tool: colmena -- "fill the jar on the soil with the honey!" + colmenaHive = let + makeHoneyFrom = import ./make-honey.nix { + inherit (inputs) colmena nixpkgs; + cellBlock = "colmenaConfigurations"; + }; + in + makeHoneyFrom self; + + # tool: nixos-generators -- "get drunk like a bear!" + nixosConfigurations = let + makeMeadFrom = import ./make-mead.nix { + inherit (inputs) nixpkgs; + cellBlock = "nixosConfigurations"; + }; + in + makeMeadFrom self; + + # tool: home-manager -- "drunken sailor, sunken sailor; honeymoon pantaloon." + homeConfigurations = let + makeMoonshineFrom = import ./make-moonshine.nix { + inherit (inputs) nixpkgs; + cellBlock = "homeConfigurations"; + }; + in + makeMoonshineFrom self; + + # tool: disko -- "Tiganizatia, tiganizatia - disko, disko partizani." + diskoConfigurations = let + makeShantyFrom = import ./make-shanty.nix { + inherit (inputs) nixpkgs; + cellBlock = "diskoConfigurations"; + }; + in + makeShantyFrom self; + }; + + # --- Flake Local Nix Configuration ---------------------------- + # TODO: adopt spongix + nixConfig = { + extra-substituters = []; + extra-trusted-public-keys = []; + }; + # -------------------------------------------------------------- +} diff --git a/make-honey.nix b/make-honey.nix new file mode 100644 index 0000000..fe3e846 --- /dev/null +++ b/make-honey.nix @@ -0,0 +1,50 @@ +{ + colmena, + nixpkgs, + cellBlock ? "colmenaConfigurations", +}: let + l = nixpkgs.lib // builtins; + inherit (import ./pasteurize.nix {inherit nixpkgs cellBlock;}) pasteurize stir beeOptions; + + colmenaModules = [ + colmena.nixosModules.assertionModule + colmena.nixosModules.keyChownModule + colmena.nixosModules.keyServiceModule + colmena.nixosModules.deploymentOptions + beeOptions # still present, but we dont care + ]; +in + self: let + comb = pasteurize self; + evalNode = extra: name: config: let + inherit (stir config) evalConfig system; + in + evalConfig { + inherit system; + modules = colmenaModules ++ [extra config]; + specialArgs = {inherit name;}; + }; + in + # Exported attributes + l.fix (this: { + __schema = "v0"; + + nodes = l.mapAttrs (evalNode {_module.check = true;}) comb; + toplevel = l.mapAttrs (_: v: v.config.system.build.toplevel) this.nodes; + deploymentConfig = l.mapAttrs (_: v: v.config.deployment) this.nodes; + deploymentConfigSelected = names: l.filterAttrs (name: _: l.elem name names) this.deploymentConfig; + evalSelected = names: l.filterAttrs (name: _: l.elem name names) this.toplevel; + evalSelectedDrvPaths = names: l.mapAttrs (_: v: v.drvPath) (this.evalSelected names); + metaConfig = { + name = "divnix/hive"; + inherit (import ./flake.nix) description; + machinesFile = null; + allowApplyAll = false; + }; + introspect = f: + f { + lib = nixpkgs.lib // builtins; + pkgs = nixpkgs.legacyPackages.${builtins.currentSystem}; + nodes = l.mapAttrs (evalNode {_module.check = false;}) comb; + }; + }) diff --git a/make-mead.nix b/make-mead.nix new file mode 100644 index 0000000..4bd8ed3 --- /dev/null +++ b/make-mead.nix @@ -0,0 +1,18 @@ +{ + nixpkgs, + cellBlock ? "nixosConfigurations", +}: let + l = nixpkgs.lib // builtins; + inherit (import ./pasteurize.nix {inherit nixpkgs cellBlock;}) pasteurize stir beeOptions; +in + self: let + comb = pasteurize self; + evalNode = extra: name: config: let + inherit (stir config) evalConfig system; + in + evalConfig { + inherit system; + modules = [extra beeOptions config]; + }; + in + l.mapAttrs (evalNode {}) comb diff --git a/make-moonshine.nix b/make-moonshine.nix new file mode 100644 index 0000000..4cb6d9f --- /dev/null +++ b/make-moonshine.nix @@ -0,0 +1,26 @@ +{ + nixpkgs, + cellBlock ? "homeConfigurations", +}: let + l = nixpkgs.lib // builtins; + inherit (import ./pasteurize.nix {inherit nixpkgs cellBlock;}) cure shake showAssertions; +in + self: let + comb = cure self; + res = name: config: let + inherit + (shake config {}) + evaled + ; + asserted = showAssertions evaled; + in { + # __schema = "v0"; + inherit (asserted) options config; + inherit (asserted.config.home) activationPackage; + newsDisplay = evaled.config.news.display; + newsEntries = l.sort (a: b: a.time > b.time) ( + l.filter (a: a.condition) evaled.config.news.entries + ); + }; + in + l.mapAttrs res comb diff --git a/make-shanty.nix b/make-shanty.nix new file mode 100644 index 0000000..5510d61 --- /dev/null +++ b/make-shanty.nix @@ -0,0 +1,8 @@ +{ + nixpkgs, + cellBlock ? "diskoConfigurations", +}: let + l = nixpkgs.lib // builtins; + inherit (import ./pasteurize.nix {inherit nixpkgs cellBlock;}) sing; +in + sing diff --git a/pasteurize.nix b/pasteurize.nix new file mode 100644 index 0000000..9d281e3 --- /dev/null +++ b/pasteurize.nix @@ -0,0 +1,233 @@ +{ + nixpkgs, + cellBlock, +}: let + l = nixpkgs.lib // builtins; + evalModulesMinimal = + (import (nixpkgs + /nixos/lib/default.nix) { + inherit (nixpkgs) lib; + # don't show the warning. + featureFlags.minimalModules = {}; + }) + .evalModules; + + beeOptions = {config, ...}: { + options.bee = { + system = l.mkOption { + type = l.types.str; + description = "divnix/hive requires you to set the host's system via 'config.bee.system = \"x86_64-linux\";'"; + }; + home = l.mkOption { + type = l.mkOptionType { + name = "input"; + description = "home-manager input"; + check = x: (l.isAttrs x) && (l.hasAttr "sourceInfo" x); + }; + description = "divnix/hive requires you to set the home-manager input via 'config.bee.home = inputs.home-22-05;'"; + }; + pkgs = l.mkOption { + type = l.mkOptionType { + name = "packages"; + description = "instance of nixpkgs"; + check = x: (l.isAttrs x) && (l.hasAttr "path" x); + }; + description = "divnix/hive requires you to set the nixpkgs instance via 'config.bee.pkgs = inputs.nixos-22.05.legacyPackages;'"; + apply = x: + if (l.hasAttr "${config.bee.system}" x) + then x.${config.bee.system} + else x; + }; + }; + }; + + combCheckModule = let + erase = optionName: {options, ...}: let + opt = l.getAttrFromPath optionName options; + in { + options = l.setAttrByPath optionName (l.mkOption {visible = false;}); + config._hive_erased = [ + { + assertion = !opt.isDefined; + message = '' + The option definition `${l.showOption optionName}' in ${l.showFiles opt.files} is not supported by divnix/hive. + + This is a Standard simplification. + + - Please set 'config.bee.pkgs' to an instantiated version of nixpkgs. + - Also declare the host system via 'config.bee.system'. + ''; + } + ]; + }; + in + {config, ...}: { + imports = [ + (erase ["nixpkgs" "config"]) + (erase ["nixpkgs" "overlays"]) + (erase ["nixpkgs" "system"]) + (erase ["nixpkgs" "localSystem"]) + (erase ["nixpkgs" "crossSystem"]) + (erase ["nixpkgs" "pkgs"]) + ]; + config._module = { + freeformType = l.types.unspecified; + check = true; + }; + options._hive_erased = l.mkOption { + type = l.types.listOf l.types.unspecified; + internal = true; + default = []; + }; + }; + + checkAndTransformConfigFor = user: target: out: config: let + _file = "github:divnix/hive: ./comb/${user}; target: ${target}"; + locatedConfig = { + imports = [config]; + inherit _file; + }; + checked = (evalModulesMinimal {modules = [combCheckModule beeOptions locatedConfig];}).config; + asserted = let + failedAsserts = map (x: x.message) (l.filter (x: !x.assertion) checked._hive_erased); + in + if failedAsserts != [] + then throw "\nFailed assertions:\n${l.concatStringsSep "\n" (map (x: "- ${x}") failedAsserts)}" + else checked; + in + locatedConfig // (out asserted); + + /* + + We start with: + ${system}.${user}.${cellBlock}.${machine} = config; + + We want: + ${user}.${machine} = config; (filtered by system) + + */ + pasteurize = self: + l.pipe + ( + l.mapAttrs (system: + l.mapAttrs (user: blocks: ( + l.pipe blocks [ + (l.attrByPath [cellBlock] {}) + (l.mapAttrs (machine: + checkAndTransformConfigFor user machine ( + asserted: { + environment.etc."nixos/configuration.nix".text = '' + throw ''' + This machine is not managed by nixos-rebuild, but by colmena. + ''' + ''; + nixpkgs = { + inherit (asserted.bee) system pkgs; + inherit (asserted.bee.pkgs) config; # nixos modules don't load this + }; + } + ))) + (l.filterAttrs (_: config: config.nixpkgs.system == system)) + (l.mapAttrs (machine: l.nameValuePair "${user}.${machine}")) + ] + ))) + (l.intersectAttrs (l.genAttrs l.systems.doubles.all (_: null)) self) + ) [ + (l.collect (x: x ? name && x ? value)) + l.listToAttrs + ]; + + stir = config: { + # we consume the already transformed contract here + evalConfig = import (config.nixpkgs.pkgs.path + "/nixos/lib/eval-config.nix"); + system = config.nixpkgs.system; + }; + + # same as pasteurize, but for home manager configs + cure = self: + l.pipe + ( + l.mapAttrs (system: + l.mapAttrs (user: blocks: ( + l.pipe blocks [ + (l.attrByPath [cellBlock] {}) + (l.mapAttrs (homecfg: + checkAndTransformConfigFor user homecfg ( + # We switched off the home-manager nimpkgs module since it + # does a re-import (and we don't tolerate that interface) + # so we re-use bee to communicate with the shake function + # below + asserted: {bee = {inherit (asserted.bee) system pkgs home;};} + ))) + (l.filterAttrs (_: config: config.bee.system == system)) + (l.mapAttrs (homecfg: l.nameValuePair "${user}.${homecfg}")) + ] + ))) + (l.intersectAttrs (l.genAttrs l.systems.doubles.all (_: null)) self) + ) [ + (l.collect (x: x ? name && x ? value)) + l.listToAttrs + ]; + + # same as stir, but for home manager configs + shake = config: extra: let + # we consume the already transformed contract here + lib = import (config.bee.home + /modules/lib/stdlib-extended.nix) l; + hmModules = import (config.bee.home + /modules/modules.nix) { + inherit (config.bee) pkgs; + inherit lib; + check = true; + # we switch off the nixpkgs module, package instantiation needs + # to happen on the `std` layer + useNixpkgsModule = false; + }; + evaled = + # need to use the extended lib + lib.evalModules { + modules = [config beeOptions extra] ++ hmModules; + specialArgs = { + modulesPath = l.toString (config.bee.home + /modules); + }; + }; + in { + inherit evaled; + # system = config.bee.system; # not actually used + }; + + # same as pasteurize, but for disko where the system doesn't matter + sing = self: + l.pipe + ( + l.mapAttrs (system: + l.mapAttrs (user: blocks: ( + l.pipe blocks [ + (l.attrByPath [cellBlock] {}) + (l.filterAttrs (_: _: "x86_64-linux" == system)) # pick one + (l.mapAttrs (disko: l.nameValuePair "${user}.${disko}")) + ] + ))) + (l.intersectAttrs (l.genAttrs l.systems.doubles.all (_: null)) self) + ) [ + (l.collect (x: x ? name && x ? value)) + l.listToAttrs + ]; + + # Error reporting + showAssertions = let + collectFailed = cfg: + l.map (x: x.message) (l.filter (x: !x.assertion) cfg.assertions); + showWarnings = res: let + f = w: x: l.trace "warning: ${w}" x; + in + l.fold f res res.config.warnings; + in + evaled: + showWarnings ( + let + failed = collectFailed evaled.config; + failedStr = l.concatStringsSep "\n" (map (x: "- ${x}") failed); + in + if failed == [] + then evaled + else throw "\nFailed assertions:\n${failedStr}" + ); +in {inherit pasteurize stir cure shake sing showAssertions beeOptions;}