#!/usr/bin/env nu # Generate NixOS flake for Hetzner server from provisioning config # Combines workspace Nickel config + os-nixos taskserv to generate target flake use std # Validate input files def validate-inputs [servers_ncl: string, output_dir: string] { if not ($servers_ncl | path exists) { error make {msg: $"servers.ncl not found: ($servers_ncl)"} } if not ($output_dir | path exists) { mkdir $output_dir } } # Export Nickel servers config to JSON def export-servers-config [servers_ncl: string] { let export_result = (nickel export $servers_ncl 2>&1 | complete) if $export_result.exit_code != 0 { error make {msg: $"nickel export failed: ($export_result.stderr)"} } $export_result.stdout | from json } # Generate flake.nix from server config def generate-flake-nix [server: record, taskservs_dir: string, output_path: string] { let hostname = $server.hostname let server_type = $server.server_type let os_type = ($server.os_type? // "debian") let flake_content = $" { description = \"NixOS configuration for Hetzner server ($hostname)\"; inputs = { nixpkgs.url = \"github:NixOS/nixpkgs/nixos-24.11\"; nixos-anywhere = { url = \"github:nix-community/nixos-anywhere\"; inputs.nixpkgs.follows = \"nixpkgs\"; }; provisioning-os-nixos = { url = \"path:($taskservs_dir)/infrastructure/os-nixos\"; }; }; outputs = { self, nixpkgs, nixos-anywhere, provisioning-os-nixos }: { nixosConfigurations.default = nixpkgs.lib.nixosSystem { system = if (builtins.elem \"cax\" [\"$server_type\"]) then \"aarch64-linux\" else \"x86_64-linux\"; modules = [ provisioning-os-nixos.nixosModules.default provisioning-os-nixos.nixosModules.hetzner-$server_type ./hardware-configuration.nix ./configuration.nix ]; }; }; } " $flake_content | save --force $output_path print $"Generated flake.nix at ($output_path)" } # Generate hardware-configuration.nix based on server type def generate-hardware-config [server: record, output_path: string] { let server_type = $server.server_type let arch = if ($server_type | str contains "cax") { "aarch64" } else { "x86_64" } let hardware_config = $" # Auto-generated hardware configuration for Hetzner ($server_type) { config, lib, pkgs, ... }: { imports = [ ]; boot.initrd.availableKernelModules = [ \"ata_piix\" \"uhci_hcd\" \"ahci\" \"virtio_pci\" \"virtio_blk\" ]; boot.kernelModules = [ ]; boot.extraModulePackages = [ ]; fileSystems.\"/\" = { device = \"/dev/sda1\"; fsType = \"ext4\"; }; swapDevices = [ ]; hardware.cpu.intel.updateMicrocode = lib.mkDefault (pkgs.stdenv.hostPlatform.isx86_64); system.stateVersion = \"24.11\"; } " $hardware_config | save --force $output_path print $"Generated hardware-configuration.nix at ($output_path)" } # Generate base configuration.nix def generate-configuration [server: record, output_path: string] { let hostname = $server.hostname let domain = "librecloud.online" let config = $" # NixOS configuration for ($hostname) { config, lib, pkgs, ... }: { networking.hostName = \"$hostname\"; networking.domain = \"$domain\"; networking.fqdn = \"$hostname.$domain\"; time.timeZone = \"UTC\"; i18n.defaultLocale = \"en_US.UTF-8\"; services.openssh = { enable = true; settings = { PasswordAuthentication = false; PubkeyAuthentication = true; }; }; users.users.devadm = { isNormalUser = true; home = \"/home/devadm\"; group = \"devadm\"; groups = [ \"wheel\" ]; shell = pkgs.bash; openssh.authorizedKeys.keys = [ ]; }; users.groups.devadm = { }; security.sudo.wheelNeedsPassword = false; system.activationScripts.mkdir-provisioning = '' mkdir -p /nix/provisioning chown devadm:devadm /nix/provisioning ''; nix.settings.experimental-features = [ \"nix-command\" \"flakes\" ]; system.stateVersion = \"24.11\"; } " $config | save --force $output_path print $"Generated configuration.nix at ($output_path)" } # Main function export def main [ servers_ncl: string # Path to servers.ncl --output-dir: string # Output directory for flakes --taskservs-dir: string # Path to provisioning/extensions/taskservs (for imports) --filter: string # Filter servers by hostname pattern ] { let output_base = ($output_dir? // "./nixos") let taskservs_base = ($taskservs_dir? // "./provisioning/extensions/taskservs") validate-inputs $servers_ncl $output_base let servers_config = (export-servers-config $servers_ncl) let servers_list = if ($servers_config | has "servers") { $servers_config.servers } else { [$servers_config] } print $"Generating flakes for ($servers_list | length) server(s)..." for server in $servers_list { let hostname = $server.hostname let os_type = ($server.os_type? // "debian") # Skip Debian servers if $os_type != "nixos" { print $"⊘ ($hostname): os_type is ($os_type), skipping (not NixOS)" continue } # Apply filter if specified if ($filter != null) and not ($hostname | str contains $filter) { print $"⊘ ($hostname): filtered out" continue } let server_dir = $"($output_base)/($hostname)" mkdir $server_dir generate-flake-nix $server $taskservs_base $"($server_dir)/flake.nix" generate-hardware-config $server $"($server_dir)/hardware-configuration.nix" generate-configuration $server $"($server_dir)/configuration.nix" print $"✓ ($hostname): flake generated at ($server_dir)" } print "All flakes generated successfully" }