From b60c74219574ccd7cfa5c0fd614cbbdc1608a625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christina=20S=C3=B8rensen?= Date: Wed, 28 Feb 2024 08:36:13 +0100 Subject: [PATCH 1/3] build(flake): flakify morph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christina Sørensen --- .envrc | 3 + .gitignore | 8 +++ flake.lock | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 89 +++++++++++++++++++++++++++++ treefmt.nix | 11 ++++ 5 files changed, 273 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 treefmt.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..e6b69f7 --- /dev/null +++ b/.envrc @@ -0,0 +1,3 @@ +if has nix; then + use flake . +fi diff --git a/.gitignore b/.gitignore index db9d098..b6baada 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,11 @@ # nix result links /result* +/repl-result* + +# Direnv +.direnv + +# Generated by nix-pre-commit-hooks +/.pre-commit-config.yaml + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..53c7522 --- /dev/null +++ b/flake.lock @@ -0,0 +1,162 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1708979614, + "narHash": "sha256-FWLWmYojIg6TeqxSnHkKpHu5SGnFP5um1uUjH+wRV6g=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b7ee09cf5614b02d289cd86fcfa6f24d4e078c2a", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1708979614, + "narHash": "sha256-FWLWmYojIg6TeqxSnHkKpHu5SGnFP5um1uUjH+wRV6g=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b7ee09cf5614b02d289cd86fcfa6f24d4e078c2a", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": [ + "flake-utils" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1708018599, + "narHash": "sha256-M+Ng6+SePmA8g06CmUZWi1AjG2tFBX9WCXElBHEKnyM=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "5df5a70ad7575f6601d91f0efec95dd9bc619431", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks", + "treefmt-nix": "treefmt-nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1708897213, + "narHash": "sha256-QECZB+Hgz/2F/8lWvHNk05N6NU/rD9bWzuNn6Cv8oUk=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "e497a9ddecff769c2a7cbab51e1ed7a8501e7a3a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..ef388dc --- /dev/null +++ b/flake.nix @@ -0,0 +1,89 @@ +{ + description = "A very basic flake"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11"; + + flake-utils = { url = "github:numtide/flake-utils"; }; + + pre-commit-hooks = { + url = "github:cachix/pre-commit-hooks.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.flake-utils.follows = "flake-utils"; + }; + + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, pre-commit-hooks, nixpkgs, flake-utils, treefmt-nix }: + flake-utils.lib.eachDefaultSystem (system: + let + # Current Version of Morph + # TODO: this sucks... + version = "dev"; + + pkgs = (import nixpkgs) { inherit system; }; + + # Eval the treefmt modules from ./treefmt.nix + treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; + in { + # for `nix fmt` + formatter = treefmtEval.config.build.wrapper; + + # for `nix flake check` + checks = { + formatting = treefmtEval.config.build.check self; + build = self.packages.${system}.morph; + pre-commit-check = let + # some treefmt formatters are not supported in pre-commit-hooks we + # filter them out for now. + toFilter = [ "yamlfmt" ]; + filterFn = n: _v: (!builtins.elem n toFilter); + treefmtFormatters = + pkgs.lib.mapAttrs (_n: v: { inherit (v) enable; }) + (pkgs.lib.filterAttrs filterFn (import ./treefmt.nix).programs); + in pre-commit-hooks.lib.${system}.run { + src = ./.; + hooks = treefmtFormatters; + }; + }; + + # Acessible through 'nix develop' or 'nix-shell' (legacy) + devShells.default = pkgs.mkShell { + inherit (self.checks.${system}.pre-commit-check) shellHook; + inputsFrom = [ self.packages.${system}.morph ]; + }; + + packages = rec { + default = morph; + morph = pkgs.buildGoModule rec { + name = "morph-unstable-${version}"; + inherit version; + + src = pkgs.nix-gitignore.gitignoreSource [ ] ./.; + + ldflags = [ + "-X main.version=${version}" + "-X main.assetRoot=${placeholder "lib"}" + ]; + + vendorHash = "sha256-zQlMtbXgrH83zrcIoOuFhb2tYCeQ1pz4UQUvRIsLMCE=="; + + postInstall = '' + mkdir -p $lib + cp -v ./data/*.nix $lib + ''; + + outputs = [ "out" "lib" ]; + + meta = { + homepage = "https://github.com/DBCDK/morph"; + description = "Morph is a NixOS host manager written in Golang."; + }; + }; + }; + }); +} diff --git a/treefmt.nix b/treefmt.nix new file mode 100644 index 0000000..c1006d1 --- /dev/null +++ b/treefmt.nix @@ -0,0 +1,11 @@ +{ + projectRootFile = "flake.nix"; + programs = { + nixfmt.enable = true; # nix formatter + statix.enable = true; # nix static analysis + deadnix.enable = true; # find dead nix code + shellcheck.enable = true; # bash/shell + taplo.enable = true; # toml + yamlfmt.enable = true; # yaml + }; +} From 4a3684b18388d727f36a72f736f68ef379dbb209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christina=20S=C3=B8rensen?= Date: Wed, 28 Feb 2024 08:41:15 +0100 Subject: [PATCH 2/3] style: run `nix fmt` treewide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christina Sørensen --- data/eval-machines.nix | 205 ++++++++++++++------------- data/options.nix | 291 +++++++++++++++++++------------------- default.nix | 13 +- examples/healthchecks.nix | 25 ++-- examples/secrets.nix | 22 +-- examples/simple.nix | 36 +++-- nixpkgs.nix | 3 +- shell.nix | 14 +- treefmt.nix | 7 + 9 files changed, 319 insertions(+), 297 deletions(-) diff --git a/data/eval-machines.nix b/data/eval-machines.nix index 0d6874f..42be12e 100644 --- a/data/eval-machines.nix +++ b/data/eval-machines.nix @@ -2,83 +2,88 @@ { networkExpr }: let - network = import networkExpr; - nwPkgs = network.network.pkgs or {}; - lib = network.network.lib or nwPkgs.lib or (import ); - evalConfig = network.network.evalConfig or ((nwPkgs.path or ) + "/nixos/lib/eval-config.nix"); - runCommand = network.network.runCommand or nwPkgs.runCommand or ((import {}).runCommand); -in - with lib; + network = import networkExpr; + nwPkgs = network.network.pkgs or { }; + lib = network.network.lib or nwPkgs.lib or (import ); + evalConfig = network.network.evalConfig or ((nwPkgs.path or ) + + "/nixos/lib/eval-config.nix"); + runCommand = + network.network.runCommand or nwPkgs.runCommand or (import + { }).runCommand; +in with lib; let - modules = { machineName, nodes, check }: [ - # Get the configuration of this machine from each network - # expression, attaching _file attributes so the NixOS module - # system can give sensible error messages. - { imports = [ network.${machineName} ]; } + modules = { machineName, nodes, check }: + [ + # Get the configuration of this machine from each network + # expression, attaching _file attributes so the NixOS module + # system can give sensible error messages. + { imports = [ network.${machineName} ]; } - ({ config, lib, options, ... }: { - key = "deploy-stuff"; - imports = [ ./options.nix ]; - # Make documentation builds deterministic, even with our - # tempdir module imports. - documentation.nixos.extraModuleSources = [ ../. ]; - # Provide a default hostname and deployment target equal - # to the attribute name of the machine in the model. - networking.hostName = lib.mkDefault machineName; - deployment.targetHost = lib.mkDefault machineName; + ({ lib, ... }: { + key = "deploy-stuff"; + imports = [ ./options.nix ]; + # Make documentation builds deterministic, even with our + # tempdir module imports. + documentation.nixos.extraModuleSources = [ ../. ]; + # Provide a default hostname and deployment target equal + # to the attribute name of the machine in the model. + networking.hostName = lib.mkDefault machineName; + deployment.targetHost = lib.mkDefault machineName; - # If network.pkgs is set, mkDefault nixpkgs.pkgs - nixpkgs.pkgs = lib.mkIf (nwPkgs != {}) (lib.mkDefault nwPkgs); + # If network.pkgs is set, mkDefault nixpkgs.pkgs + nixpkgs.pkgs = lib.mkIf (nwPkgs != { }) (lib.mkDefault nwPkgs); - # Avoid the deprecated evalConfig arguments by - # setting them here instead. - _module = { - args = { - name = machineName; - inherit nodes; + # Avoid the deprecated evalConfig arguments by + # setting them here instead. + _module = { + args = { + name = machineName; + inherit nodes; + }; + inherit check; }; - inherit check; - }; - }) - ] ++ optional (network ? _file) { inherit (network) _file; }; + }) + ] ++ optional (network ? _file) { inherit (network) _file; }; - machineNames = attrNames (removeAttrs network [ "network" "defaults" "resources" "require" "_file" ]); + machineNames = attrNames (removeAttrs network [ + "network" + "defaults" + "resources" + "require" + "_file" + ]); in rec { # Unchecked configuration of all machines. # Using unchecked config evaluation allows each machine to access other machines # configuration without recursing as full evaluation is prevented - uncheckedNodes = - listToAttrs (map (machineName: - { name = machineName; - value = import evalConfig { - # Force decide system in module system - system = null; - modules = modules { - inherit machineName; - check = false; - nodes = uncheckedNodes; - }; - }; - } - ) machineNames); + uncheckedNodes = listToAttrs (map (machineName: { + name = machineName; + value = import evalConfig { + # Force decide system in module system + system = null; + modules = modules { + inherit machineName; + check = false; + nodes = uncheckedNodes; + }; + }; + }) machineNames); # Compute the definitions of the machines. - nodes = - listToAttrs (map (machineName: - { name = machineName; - value = import evalConfig { - # Force decide system in module system - system = null; - modules = modules { - inherit machineName; - check = true; - nodes = uncheckedNodes; - }; - }; - } - ) machineNames); + nodes = listToAttrs (map (machineName: { + name = machineName; + value = import evalConfig { + # Force decide system in module system + system = null; + modules = modules { + inherit machineName; + check = true; + nodes = uncheckedNodes; + }; + }; + }) machineNames); deploymentInfoModule = { deployment = { @@ -89,30 +94,33 @@ in rec { }; # Phase 1: evaluate only the deployment attributes. - info = - let - network' = network; - nodes' = nodes; - in rec { + info = let network' = network; + in rec { - machines = - flip mapAttrs nodes (n: v': let v = scrubOptionValue v'; in - { inherit (v.config.deployment) targetHost targetPort targetUser secrets healthChecks buildOnly substituteOnDestination tags; - name = n; - nixosRelease = v.config.system.nixos.release or (removeSuffix v.config.system.nixos.version.suffix v.config.system.nixos.version); - nixConfig = mapAttrs - (n: v: if builtins.isString v then v else throw "nix option '${n}' must have a string typed value") - (network'.network.nixConfig or {}); - } - ); + machines = flip mapAttrs nodes (n: v': + let v = scrubOptionValue v'; + in { + inherit (v.config.deployment) + targetHost targetPort targetUser secrets healthChecks buildOnly + substituteOnDestination tags; + name = n; + nixosRelease = v.config.system.nixos.release or (removeSuffix + v.config.system.nixos.version.suffix v.config.system.nixos.version); + nixConfig = mapAttrs (n: v: + if builtins.isString v then + v + else + throw "nix option '${n}' must have a string typed value") + (network'.network.nixConfig or { }); + }); - machineList = (map (key: getAttr key machines) (attrNames machines)); - network = network'.network or {}; + machineList = map (key: getAttr key machines) (attrNames machines); + network = network'.network or { }; deployment = { hosts = machineList; meta = { description = network.description or ""; - ordering = network.ordering or {}; + ordering = network.ordering or { }; }; }; @@ -123,24 +131,21 @@ in rec { machines = { argsFile, buildTargets ? null }: let fileArgs = builtins.fromJSON (builtins.readFile argsFile); - nodes' = filterAttrs (n: v: elem n fileArgs.Names) nodes; in - runCommand "morph" - { preferLocalBuild = true; } - (if buildTargets == null - then '' - mkdir -p $out - ${toString (mapAttrsToList (nodeName: nodeDef: '' - ln -s ${nodeDef.config.system.build.toplevel} $out/${nodeName} - '') nodes')} - '' - else '' - mkdir -p $out - ${toString (mapAttrsToList (nodeName: nodeDef: '' - mkdir -p $out/${nodeName} - ${toString (mapAttrsToList (buildName: buildFn: '' - ln -s ${buildFn nodeDef} $out/${nodeName}/${buildName} - '') buildTargets)} - '') nodes')} - ''); + nodes' = filterAttrs (n: _v: elem n fileArgs.Names) nodes; + in runCommand "morph" { preferLocalBuild = true; } + (if buildTargets == null then '' + mkdir -p $out + ${toString (mapAttrsToList (nodeName: nodeDef: '' + ln -s ${nodeDef.config.system.build.toplevel} $out/${nodeName} + '') nodes')} + '' else '' + mkdir -p $out + ${toString (mapAttrsToList (nodeName: nodeDef: '' + mkdir -p $out/${nodeName} + ${toString (mapAttrsToList (buildName: buildFn: '' + ln -s ${buildFn nodeDef} $out/${nodeName}/${buildName} + '') buildTargets)} + '') nodes')} + ''); } diff --git a/data/options.nix b/data/options.nix index 743e1d3..64f09fa 100644 --- a/data/options.nix +++ b/data/options.nix @@ -5,167 +5,167 @@ with lib.types; let -ownerOptionsType = submodule ({ ... }: { + ownerOptionsType = submodule (_: { options = { - group = mkOption { - type = str; - description = "Group that will own the secret."; - default = "root"; - }; + group = mkOption { + type = str; + description = "Group that will own the secret."; + default = "root"; + }; - user = mkOption { - type = str; - description = "User who will own the secret."; - default = "root"; - }; + user = mkOption { + type = str; + description = "User who will own the secret."; + default = "root"; + }; }; -}); + }); -keyOptionsType = submodule ({ ... }: { - options = { - destination = mkOption { - type = str; - description = "Remote path"; - }; + keyOptionsType = submodule (_: { + options = { + destination = mkOption { + type = str; + description = "Remote path"; + }; - source = mkOption { - type = str; - description = "Local path"; - }; + source = mkOption { + type = str; + description = "Local path"; + }; - owner = mkOption { - default = {}; - type = ownerOptionsType; - description = '' - Owner of the secret. - ''; - }; + owner = mkOption { + default = { }; + type = ownerOptionsType; + description = '' + Owner of the secret. + ''; + }; - permissions = mkOption { - default = "0400"; - type = str; - description = "Permissions expressed as octal."; - }; + permissions = mkOption { + default = "0400"; + type = str; + description = "Permissions expressed as octal."; + }; - action = mkOption { - default = []; - type = listOf str; - description = "Action to perform on remote host after uploading secret."; - }; + action = mkOption { + default = [ ]; + type = listOf str; + description = + "Action to perform on remote host after uploading secret."; + }; - mkDirs = mkOption { - default = true; - type = bool; - description = '' - Whether to create parent directories to secret destination. - In particular, morph will execute `sudo mkdir -p -m 755 /path/to/secret/destination` - prior to moving the secret in place. - ''; - }; + mkDirs = mkOption { + default = true; + type = bool; + description = '' + Whether to create parent directories to secret destination. + In particular, morph will execute `sudo mkdir -p -m 755 /path/to/secret/destination` + prior to moving the secret in place. + ''; + }; - uploadAt = mkOption { - default = "pre-activation"; - type = enum [ "pre-activation" "post-activation" ]; - description = '' - When to upload the secret. + uploadAt = mkOption { + default = "pre-activation"; + type = enum [ "pre-activation" "post-activation" ]; + description = '' + When to upload the secret. - `pre-activation` (the default) will upload the secret and run any associated action prior to activating the system configuration. - `post-activation` will upload the secret and run any associated action after activating the system configuration. - ''; + `pre-activation` (the default) will upload the secret and run any associated action prior to activating the system configuration. + `post-activation` will upload the secret and run any associated action after activating the system configuration. + ''; + }; }; - }; -}); + }); -healthCheckType = submodule ({ ... }: { - options = { - cmd = mkOption { - type = listOf cmdHealthCheckType; - default = []; - description = "List of command health checks"; - }; - http = mkOption { - type = listOf httpHealthCheckType; - default = []; - description = "List of HTTP health checks"; + healthCheckType = submodule (_: { + options = { + cmd = mkOption { + type = listOf cmdHealthCheckType; + default = [ ]; + description = "List of command health checks"; + }; + http = mkOption { + type = listOf httpHealthCheckType; + default = [ ]; + description = "List of HTTP health checks"; + }; }; - }; -}); + }); -httpHealthCheckType = types.submodule ({ ... }: { - options = { - description = mkOption { + httpHealthCheckType = types.submodule (_: { + options = { + description = mkOption { type = str; description = "Health check description"; + }; + host = mkOption { + type = nullOr str; + description = "Host name"; + default = null; + #default = config.networking.hostName; + }; + scheme = mkOption { + type = str; + description = "Scheme"; + default = "http"; + }; + port = mkOption { + type = int; + description = "Port number"; + }; + path = mkOption { + type = path; + description = "HTTP request path"; + default = "/"; + }; + headers = mkOption { + type = attrsOf str; + description = "HTTP request headers"; + default = { }; + }; + period = mkOption { + type = int; + description = "Seconds between checks"; + default = 2; + }; + timeout = mkOption { + type = int; + description = "Timeout in seconds"; + default = 5; + }; + insecureSSL = mkOption { + type = bool; + description = "Ignore SSL errors"; + default = false; + }; }; - host = mkOption { - type = nullOr str; - description = "Host name"; - default = null; - #default = config.networking.hostName; - }; - scheme = mkOption { - type = str; - description = "Scheme"; - default = "http"; - }; - port = mkOption { - type = int; - description = "Port number"; - }; - path = mkOption { - type = path; - description = "HTTP request path"; - default = "/"; - }; - headers = mkOption { - type = attrsOf str; - description = "HTTP request headers"; - default = {}; - }; - period = mkOption { - type = int; - description = "Seconds between checks"; - default = 2; - }; - timeout = mkOption { - type = int; - description = "Timeout in seconds"; - default = 5; - }; - insecureSSL = mkOption { - type = bool; - description = "Ignore SSL errors"; - default = false; - }; - }; -}); + }); -cmdHealthCheckType = types.submodule ({ ... }: { - options = { - description = mkOption { + cmdHealthCheckType = types.submodule (_: { + options = { + description = mkOption { type = str; description = "Health check description"; - }; - cmd = mkOption { + }; + cmd = mkOption { type = nullOr (listOf str); description = "Command to run as list"; default = null; + }; + period = mkOption { + type = int; + description = "Seconds between checks"; + default = 2; + }; + timeout = mkOption { + type = int; + description = "Timeout in seconds"; + default = 5; + }; }; - period = mkOption { - type = int; - description = "Seconds between checks"; - default = 2; - }; - timeout = mkOption { - type = int; - description = "Timeout in seconds"; - default = 5; - }; - }; -}); + }); -in -{ +in { options.deployment = { targetHost = mkOption { @@ -215,7 +215,7 @@ in }; secrets = mkOption { - default = {}; + default = { }; example = { "nix-cache-signing-key" = { source = "../secrets/very-secret.txt"; @@ -223,7 +223,12 @@ in owner.user = "nginx"; owner.group = "root"; permissions = "0400"; # this is the default - action = ["sudo" "systemctl" "reload" "nginx.service"]; # restart nginx after uploading the secret + action = [ + "sudo" + "systemctl" + "reload" + "nginx.service" + ]; # restart nginx after uploading the secret }; }; type = attrsOf keyOptionsType; @@ -238,12 +243,12 @@ in description = '' Health check configuration. ''; - default = {}; + default = { }; }; tags = mkOption { type = listOf str; - default = []; + default = [ ]; description = '' Host tags. ''; @@ -254,8 +259,8 @@ in # The file will end up linked in /run/current-system along with # all derived dependencies. config.system.extraDependencies = - let - cmds = concatMap (h: h.cmd) config.deployment.healthChecks.cmd; - in - [ (pkgs.writeText "healthcheck-commands.txt" (concatStringsSep "\n" cmds)) ]; + let cmds = concatMap (h: h.cmd) config.deployment.healthChecks.cmd; + in [ + (pkgs.writeText "healthcheck-commands.txt" (concatStringsSep "\n" cmds)) + ]; } diff --git a/default.nix b/default.nix index e165b9f..8a767c1 100644 --- a/default.nix +++ b/default.nix @@ -1,18 +1,13 @@ -{ nixpkgs ? import ./nixpkgs.nix -, pkgs ? import nixpkgs {} -, version ? "dev" -}: +{ nixpkgs ? import ./nixpkgs.nix, pkgs ? import nixpkgs { }, version ? "dev" }: pkgs.buildGoModule rec { name = "morph-unstable-${version}"; inherit version; - src = pkgs.nix-gitignore.gitignoreSource [] ./.; + src = pkgs.nix-gitignore.gitignoreSource [ ] ./.; - ldflags = [ - "-X main.version=${version}" - "-X main.assetRoot=${placeholder "lib"}" - ]; + ldflags = + [ "-X main.version=${version}" "-X main.assetRoot=${placeholder "lib"}" ]; vendorHash = "sha256-zQlMtbXgrH83zrcIoOuFhb2tYCeQ1pz4UQUvRIsLMCE=="; diff --git a/examples/healthchecks.nix b/examples/healthchecks.nix index e3cfbe7..c95ed4d 100644 --- a/examples/healthchecks.nix +++ b/examples/healthchecks.nix @@ -1,27 +1,31 @@ -let - pkgs = import (import ../nixpkgs.nix) {}; -in -{ - network = { +let pkgs = import (import ../nixpkgs.nix) { }; +in { + network = { inherit pkgs; description = "health check demo hosts"; }; - "web01" = { config, pkgs, ... }: { + "web01" = _: { boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; services.nginx.enable = true; fileSystems = { - "/" = { label = "nixos"; fsType = "ext4"; }; - "/boot" = { label = "boot"; fsType = "vfat"; }; + "/" = { + label = "nixos"; + fsType = "ext4"; + }; + "/boot" = { + label = "boot"; + fsType = "vfat"; + }; }; deployment = { healthChecks = { cmd = [{ - cmd = ["true" "one argument" "another argument"]; + cmd = [ "true" "one argument" "another argument" ]; description = "Testing that 'true' works."; }]; @@ -36,7 +40,8 @@ in { scheme = "https"; port = 443; - host = "some-other-host.example.com"; # defaults to the hostname of the host if unset + host = + "some-other-host.example.com"; # defaults to the hostname of the host if unset path = "/health"; description = "Check whether $imaginaryService is running."; } diff --git a/examples/secrets.nix b/examples/secrets.nix index c78ef15..d96e01a 100644 --- a/examples/secrets.nix +++ b/examples/secrets.nix @@ -1,13 +1,11 @@ -let - pkgs = import (import ../nixpkgs.nix) {}; -in -{ - network = { +let pkgs = import (import ../nixpkgs.nix) { }; +in { + network = { inherit pkgs; description = "webserver with secrets"; }; - "web01" = { config, pkgs, ... }: { + "web01" = _: { deployment = { secrets = { "nix-cache-signing-key" = { @@ -16,7 +14,7 @@ in owner.user = "nginx"; owner.group = "root"; permissions = "0400"; # this is the default - action = ["sudo" "systemctl" "reload" "nginx.service"]; + action = [ "sudo" "systemctl" "reload" "nginx.service" ]; }; }; }; @@ -27,8 +25,14 @@ in services.nginx.enable = true; fileSystems = { - "/" = { label = "nixos"; fsType = "ext4"; }; - "/boot" = { label = "boot"; fsType = "vfat"; }; + "/" = { + label = "nixos"; + fsType = "ext4"; + }; + "/boot" = { + label = "boot"; + fsType = "vfat"; + }; }; }; } diff --git a/examples/simple.nix b/examples/simple.nix index 40b36f0..7324e13 100644 --- a/examples/simple.nix +++ b/examples/simple.nix @@ -1,16 +1,12 @@ -let - pkgs = import (import ../nixpkgs.nix) {}; -in -{ - network = { +let pkgs = import (import ../nixpkgs.nix) { }; +in { + network = { inherit pkgs; description = "simple hosts"; - ordering = { - tags = [ "db" "web" ]; - }; + ordering = { tags = [ "db" "web" ]; }; }; - "web01" = { config, pkgs, ... }: { + "web01" = _: { deployment.tags = [ "web" ]; boot.loader.systemd-boot.enable = true; @@ -19,12 +15,18 @@ in services.nginx.enable = true; fileSystems = { - "/" = { label = "nixos"; fsType = "ext4"; }; - "/boot" = { label = "boot"; fsType = "vfat"; }; + "/" = { + label = "nixos"; + fsType = "ext4"; + }; + "/boot" = { + label = "boot"; + fsType = "vfat"; + }; }; }; - "db01" = { config, pkgs, ... }: { + "db01" = _: { deployment.tags = [ "db" ]; boot.loader.systemd-boot.enable = true; @@ -33,8 +35,14 @@ in services.postgresql.enable = true; fileSystems = { - "/" = { label = "nixos"; fsType = "ext4"; }; - "/boot" = { label = "boot"; fsType = "vfat"; }; + "/" = { + label = "nixos"; + fsType = "ext4"; + }; + "/boot" = { + label = "boot"; + fsType = "vfat"; + }; }; }; } diff --git a/nixpkgs.nix b/nixpkgs.nix index e3d708d..461a15a 100644 --- a/nixpkgs.nix +++ b/nixpkgs.nix @@ -1,2 +1,3 @@ # Intentionally impure for CI against nixos-unstable -builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz" +builtins.fetchTarball +"https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz" diff --git a/shell.nix b/shell.nix index c4ebf94..6d9f379 100644 --- a/shell.nix +++ b/shell.nix @@ -1,13 +1,5 @@ -{ nixpkgs ? import ./nixpkgs.nix -, pkgs ? import nixpkgs {} -}: +{ nixpkgs ? import ./nixpkgs.nix, pkgs ? import nixpkgs { } }: -let - morph = pkgs.callPackage ./default.nix {}; -in +let morph = pkgs.callPackage ./default.nix { }; -pkgs.mkShell { - inputsFrom = [ - morph - ]; -} +in pkgs.mkShell { inputsFrom = [ morph ]; } diff --git a/treefmt.nix b/treefmt.nix index c1006d1..0808aef 100644 --- a/treefmt.nix +++ b/treefmt.nix @@ -8,4 +8,11 @@ taplo.enable = true; # toml yamlfmt.enable = true; # yaml }; + settings = { + formatter = { + nixfmt.includes = [ "*.nix" "./data/*" ]; + statix.includes = [ "*.nix" "./data/*" ]; + deadnix.includes = [ "*.nix" "./data/*" ]; + }; + }; } From 9c70784568ce5e894ecbfc07b9b850fbbd68577c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christina=20S=C3=B8rensen?= Date: Wed, 28 Feb 2024 08:42:25 +0100 Subject: [PATCH 3/3] build(git): ignore treewide `nix fmt` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christina Sørensen --- .git-blame-ignore-revs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..247f3a2 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,28 @@ +# This file contains a list of commits that are not likely what you +# are looking for in a blame, such as mass reformatting or renaming. +# You can set this file as a default ignore file for blame by running +# the following command. +# +# $ git config blame.ignoreRevsFile .git-blame-ignore-revs +# +# To temporarily not use this file add +# --ignore-revs-file="" +# to your blame command. +# +# The ignoreRevsFile can't be set globally due to blame failing if the file isn't present. +# To not have to set the option in every repository it is needed in, +# save the following script in your path with the name "git-bblame" +# now you can run +# $ git bblame $FILE +# to use the .git-blame-ignore-revs file if it is present. +# +# #!/usr/bin/env bash +# repo_root=$(git rev-parse --show-toplevel) +# if [[ -e $repo_root/.git-blame-ignore-revs ]]; then +# git blame --ignore-revs-file="$repo_root/.git-blame-ignore-revs" $@ +# else +# git blame $@ +# fi + +# treewide nix fmt after flakification +4a3684b18388d727f36a72f736f68ef379dbb209