{ description = "SelfHostBlocks module"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; nix-flake-tests.url = "github:antifuchs/nix-flake-tests"; flake-utils.url = "github:numtide/flake-utils"; nmdsrc = { url = "git+https://git.sr.ht/~rycee/nmd"; flake = false; }; }; outputs = inputs@{ self, nixpkgs, nix-flake-tests, flake-utils, nmdsrc, ... }: flake-utils.lib.eachDefaultSystem ( system: let originPkgs = nixpkgs.legacyPackages.${system}; shbPatches = originPkgs.lib.optionals (system == "x86_64-linux") [ # Get rid of lldap patches when https://github.com/NixOS/nixpkgs/pull/425923 is merged. ./patches/lldap.patch ./patches/0001-nixos-borgbackup-add-option-to-override-state-direct.patch # Leaving commented out as an example. # (originPkgs.fetchpatch { # url = "https://github.com/NixOS/nixpkgs/pull/317107.patch"; # hash = "sha256-hoLrqV7XtR1hP/m0rV9hjYUBtrSjay0qcPUYlKKuVWk="; # }) ]; patchNixpkgs = { nixpkgs, patches, system, }: nixpkgs.legacyPackages.${system}.applyPatches { name = "nixpkgs-patched"; src = nixpkgs; inherit patches; }; patchedNixpkgs = let patched = patchNixpkgs { nixpkgs = inputs.nixpkgs; patches = shbPatches; inherit system; }; in patched // { nixosSystem = args: import "${patched}/nixos/lib/eval-config.nix" args; }; pkgs = import patchedNixpkgs { inherit system; config.allowUnfree = true; }; # The contract dummies are used to show options for contracts. contractDummyModules = [ modules/contracts/backup/dummyModule.nix modules/contracts/ssl/dummyModule.nix ]; in { formatter = pkgs.nixfmt-tree; packages.manualHtml = pkgs.callPackage ./docs { inherit nmdsrc; allModules = self.nixosModules.default.imports ++ [ self.nixosModules.sops ] ++ contractDummyModules; release = builtins.readFile ./VERSION; substituteVersionIn = [ "./manual.md" "./usage.md" ]; modules = { "blocks/authelia" = ./modules/blocks/authelia.nix; "blocks/borgbackup" = ./modules/blocks/borgbackup.nix; "blocks/lldap" = ./modules/blocks/lldap.nix; "blocks/ssl" = { module = ./modules/blocks/ssl.nix; optionRoot = [ "shb" "certs" ]; }; "blocks/mitmdump" = ./modules/blocks/mitmdump.nix; "blocks/monitoring" = ./modules/blocks/monitoring.nix; "blocks/nginx" = ./modules/blocks/nginx.nix; "blocks/postgresql" = ./modules/blocks/postgresql.nix; "blocks/restic" = ./modules/blocks/restic.nix; "blocks/sops" = ./modules/blocks/sops.nix; "services/arr" = ./modules/services/arr.nix; "services/firefly-iii" = ./modules/services/firefly-iii.nix; "services/forgejo" = [ ./modules/services/forgejo.nix (pkgs.path + "/nixos/modules/services/misc/forgejo.nix") ]; "services/home-assistant" = ./modules/services/home-assistant.nix; "services/jellyfin" = ./modules/services/jellyfin.nix; "services/karakeep" = ./modules/services/karakeep.nix; "services/nextcloud-server" = { module = ./modules/services/nextcloud-server.nix; optionRoot = [ "shb" "nextcloud" ]; }; "services/open-webui" = ./modules/services/open-webui.nix; "services/pinchflat" = ./modules/services/pinchflat.nix; "services/vaultwarden" = ./modules/services/vaultwarden.nix; "contracts/backup" = { module = ./modules/contracts/backup/dummyModule.nix; optionRoot = [ "shb" "contracts" "backup" ]; }; "contracts/databasebackup" = { module = ./modules/contracts/databasebackup/dummyModule.nix; optionRoot = [ "shb" "contracts" "databasebackup" ]; }; "contracts/secret" = { module = ./modules/contracts/secret/dummyModule.nix; optionRoot = [ "shb" "contracts" "secret" ]; }; "contracts/ssl" = { module = ./modules/contracts/ssl/dummyModule.nix; optionRoot = [ "shb" "contracts" "ssl" ]; }; }; }; # Documentation redirect generation tool - scans HTML files for anchor mappings packages.generateRedirects = let # Python patch to inject redirect collector pythonPatch = pkgs.writeText "nixos-render-docs-patch.py" '' # Load redirect collector patch try: import sys, os sys.path.insert(0, os.path.dirname(__file__) + '/..') import missing_refs_collector except Exception as e: print(f"Warning: Failed to load redirect collector: {e}", file=sys.stderr) ''; # Patched nixos-render-docs that collects redirects during HTML generation nixos-render-docs-patched = pkgs.writeShellApplication { name = "nixos-render-docs"; runtimeInputs = [ pkgs.nixos-render-docs ]; text = '' TEMP_DIR=$(mktemp -d); trap 'rm -rf "$TEMP_DIR"' EXIT cp -r ${pkgs.nixos-render-docs}/${pkgs.python3.sitePackages}/nixos_render_docs "$TEMP_DIR/" chmod -R +w "$TEMP_DIR" cp ${./docs/generate-redirects-nixos-render-docs.py} "$TEMP_DIR/missing_refs_collector.py" echo '{}' > "$TEMP_DIR/empty_redirects.json" cat ${pythonPatch} >> "$TEMP_DIR/nixos_render_docs/__init__.py" ARGS=() while [[ $# -gt 0 ]]; do case $1 in --redirects) ARGS+=("$1" "$TEMP_DIR/empty_redirects.json"); shift 2 ;; *) ARGS+=("$1"); shift ;; esac done export PYTHONPATH="$TEMP_DIR:''${PYTHONPATH:-}" nixos-render-docs "''${ARGS[@]}" ''; }; in (self.packages.${system}.manualHtml.override { nixos-render-docs = nixos-render-docs-patched; }).overrideAttrs (old: { installPhase = '' ${old.installPhase} ln -sf share/doc/selfhostblocks/redirects.json $out/redirects.json ''; }); lib = (pkgs.callPackage ./lib { }) // { test = pkgs.callPackage ./test/common.nix { }; contracts = pkgs.callPackage ./modules/contracts { shb = self.lib.${system}; }; patches = shbPatches; inherit patchNixpkgs patchedNixpkgs; }; checks = let inherit (pkgs.lib) foldl foldlAttrs mergeAttrs optionalAttrs ; importFiles = files: map ( m: pkgs.callPackage m { shb = self.lib.${system}; } ) files; mergeTests = foldl mergeAttrs { }; flattenAttrs = root: attrset: foldlAttrs ( acc: name: value: acc // { "${root}_${name}" = value; } ) { } attrset; vm_test = name: path: flattenAttrs "vm_${name}" ( removeAttrs (pkgs.callPackage path { shb = self.lib.${system}; }) [ "override" "overrideDerivation" ] ); in (optionalAttrs (system == "x86_64-linux") ( { modules = self.lib.${system}.check { inherit pkgs; tests = mergeTests (importFiles [ ./test/modules/davfs.nix # TODO: Make this not use IFD ./test/modules/lib.nix ]); }; # TODO: Make this not use IFD lib = nix-flake-tests.lib.check { inherit pkgs; tests = pkgs.callPackage ./test/modules/lib.nix { shb = self.lib.${system}; }; }; } // (vm_test "arr" ./test/services/arr.nix) // (vm_test "audiobookshelf" ./test/services/audiobookshelf.nix) // (vm_test "deluge" ./test/services/deluge.nix) // (vm_test "firefly-iii" ./test/services/firefly-iii.nix) // (vm_test "forgejo" ./test/services/forgejo.nix) // (vm_test "grocy" ./test/services/grocy.nix) // (vm_test "hledger" ./test/services/hledger.nix) // (vm_test "immich" ./test/services/immich.nix) // (vm_test "homeassistant" ./test/services/home-assistant.nix) // (vm_test "jellyfin" ./test/services/jellyfin.nix) // (vm_test "karakeep" ./test/services/karakeep.nix) // (vm_test "nextcloud" ./test/services/nextcloud.nix) // (vm_test "open-webui" ./test/services/open-webui.nix) // (vm_test "paperless" ./test/services/paperless.nix) // (vm_test "pinchflat" ./test/services/pinchflat.nix) // (vm_test "vaultwarden" ./test/services/vaultwarden.nix) // (vm_test "authelia" ./test/blocks/authelia.nix) // (vm_test "borgbackup" ./test/blocks/borgbackup.nix) // (vm_test "lldap" ./test/blocks/lldap.nix) // (vm_test "lib" ./test/blocks/lib.nix) // (vm_test "mitmdump" ./test/blocks/mitmdump.nix) // (vm_test "monitoring" ./test/blocks/monitoring.nix) // (vm_test "postgresql" ./test/blocks/postgresql.nix) // (vm_test "restic" ./test/blocks/restic.nix) // (vm_test "ssl" ./test/blocks/ssl.nix) // (vm_test "contracts-backup" ./test/contracts/backup.nix) // (vm_test "contracts-databasebackup" ./test/contracts/databasebackup.nix) // (vm_test "contracts-secret" ./test/contracts/secret.nix) )); # To see the traces, run: # nix run .#playwright -- show-trace $(nix eval .#checks.x86_64-linux.vm_grocy_basic --raw)/trace/0.zip packages.playwright = pkgs.callPackage ( { stdenvNoCC, makeWrapper, playwright, }: stdenvNoCC.mkDerivation { name = "playwright"; src = playwright; nativeBuildInputs = [ makeWrapper ]; # No quotes around the value for LLDAP_PASSWORD because we want the value to not be enclosed in quotes. installPhase = '' makeWrapper ${pkgs.python3Packages.playwright}/bin/playwright $out/bin/playwright \ --set PLAYWRIGHT_BROWSERS_PATH ${pkgs.playwright-driver.browsers} \ --set PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS true ''; } ) { }; # Run "nix run .#update-redirects" to regenerate docs/redirects.json apps.update-redirects = { type = "app"; program = "${ pkgs.writeShellApplication { name = "update-redirects"; runtimeInputs = [ pkgs.nix pkgs.jq ]; text = '' echo "=== SelfHostBlocks Redirects Updater ===" echo "Generating fresh ./docs/redirects.json..." nix build .#generateRedirects || { echo "Error: Failed to generate redirects" >&2; exit 1; } [[ -f result/redirects.json ]] || { echo "Error: Generated redirects file not found" >&2; exit 1; } echo "Generated $(jq 'keys | length' result/redirects.json) redirects" [[ -f docs/redirects.json ]] && cp docs/redirects.json docs/redirects.json.backup && echo "Created backup" cp result/redirects.json docs/redirects.json echo " Updated docs/redirects.json" echo "To verify: nix build .#manualHtml" ''; } }/bin/update-redirects"; }; } ) // { herculesCI.ciSystems = [ "x86_64-linux" ]; nixosModules.default = { imports = [ # blocks self.nixosModules.authelia self.nixosModules.borgbackup self.nixosModules.davfs self.nixosModules.hardcodedsecret self.nixosModules.lldap self.nixosModules.mitmdump self.nixosModules.monitoring self.nixosModules.nginx self.nixosModules.postgresql self.nixosModules.restic self.nixosModules.ssl self.nixosModules.tinyproxy self.nixosModules.vpn self.nixosModules.zfs # services self.nixosModules.arr self.nixosModules.audiobookshelf self.nixosModules.deluge self.nixosModules.firefly-iii self.nixosModules.forgejo self.nixosModules.grocy self.nixosModules.hledger self.nixosModules.immich self.nixosModules.home-assistant self.nixosModules.jellyfin self.nixosModules.karakeep self.nixosModules.nextcloud-server self.nixosModules.open-webui self.nixosModules.pinchflat self.nixosModules.paperless self.nixosModules.vaultwarden ]; }; nixosModules.lib = lib/module.nix; nixosModules.authelia = modules/blocks/authelia.nix; nixosModules.borgbackup = modules/blocks/borgbackup.nix; nixosModules.davfs = modules/blocks/davfs.nix; nixosModules.hardcodedsecret = modules/blocks/hardcodedsecret.nix; nixosModules.lldap = modules/blocks/lldap.nix; nixosModules.mitmdump = modules/blocks/mitmdump.nix; nixosModules.monitoring = modules/blocks/monitoring.nix; nixosModules.nginx = modules/blocks/nginx.nix; nixosModules.postgresql = modules/blocks/postgresql.nix; nixosModules.restic = modules/blocks/restic.nix; nixosModules.ssl = modules/blocks/ssl.nix; nixosModules.sops = modules/blocks/sops.nix; nixosModules.tinyproxy = modules/blocks/tinyproxy.nix; nixosModules.vpn = modules/blocks/vpn.nix; nixosModules.zfs = modules/blocks/zfs.nix; nixosModules.arr = modules/services/arr.nix; nixosModules.audiobookshelf = modules/services/audiobookshelf.nix; nixosModules.deluge = modules/services/deluge.nix; nixosModules.firefly-iii = modules/services/firefly-iii.nix; nixosModules.forgejo = modules/services/forgejo.nix; nixosModules.grocy = modules/services/grocy.nix; nixosModules.hledger = modules/services/hledger.nix; nixosModules.immich = modules/services/immich.nix; nixosModules.home-assistant = modules/services/home-assistant.nix; nixosModules.jellyfin = modules/services/jellyfin.nix; nixosModules.karakeep = modules/services/karakeep.nix; nixosModules.nextcloud-server = modules/services/nextcloud-server.nix; nixosModules.open-webui = modules/services/open-webui.nix; nixosModules.paperless = modules/services/paperless.nix; nixosModules.pinchflat = modules/services/pinchflat.nix; nixosModules.vaultwarden = modules/services/vaultwarden.nix; }; }