From f669845bf7b82014d985f7c4fbb02036075113fd Mon Sep 17 00:00:00 2001 From: UGA Innovation Factory Date: Tue, 27 Jan 2026 13:59:57 -0500 Subject: [PATCH] refactor: Move sw into properly nested modules with unconditional import --- .gitea/workflows/ci.yml | 36 +++++ .github/workflows/docs.yml | 34 +++++ .nixd.json | 18 +++ flake.nix | 1 + fleet/common.nix | 20 ++- fleet/fleet-option.nix | 63 +++++--- fleet/fs.nix | 27 +++- fleet/user-config.nix | 5 +- hw/nix-desktop.nix | 2 +- hw/nix-ephemeral.nix | 2 +- hw/nix-laptop.nix | 2 +- hw/nix-lxc.nix | 2 +- hw/nix-surface.nix | 2 +- hw/nix-wsl.nix | 10 +- hw/nix-zima.nix | 2 +- installer/modules.nix | 2 +- inventory.nix | 38 ++--- parts/docs.nix | 132 +++++++++++++++++ sw/builders/default.nix | 126 +++++++++++++--- sw/builders/programs.nix | 4 +- sw/default.nix | 209 ++++++++------------------- sw/desktop/default.nix | 65 ++++++--- sw/desktop/programs.nix | 1 - sw/desktop/services.nix | 1 - sw/gc.nix | 27 +++- sw/ghostty.nix | 2 - sw/headless/default.nix | 62 +++++--- sw/headless/programs.nix | 1 - sw/headless/services.nix | 3 - sw/stateless-kiosk/default.nix | 108 +++++++++----- sw/stateless-kiosk/kiosk-browser.nix | 1 - sw/tablet-kiosk/default.nix | 91 ++++++++---- sw/tablet-kiosk/services.nix | 2 +- sw/theme.nix | 1 - sw/updater.nix | 28 +++- templates/user/user.nix | 5 +- 36 files changed, 788 insertions(+), 347 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 .nixd.json create mode 100644 parts/docs.nix diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index e163074..cccd46c 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -79,3 +79,39 @@ jobs: echo "Evaluating artifact ${{ matrix.artifact }}" nix eval .#${{ matrix.artifact }}.drvPath \ --show-trace + + build-docs: + name: Build and Publish Documentation + runs-on: [self-hosted, nix-builder] + needs: [flake-check] + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build documentation + run: | + echo "Building Athenix documentation" + nix build .#docs --print-build-logs + + - name: Clone wiki repository + run: | + git clone https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.wiki.git wiki + cd wiki + git config user.name "Athenix CI" + git config user.email "ci@athenix.factory.uga.edu" + + - name: Update wiki with documentation + run: | + # Copy documentation to wiki + cp -r result/* wiki/ + + # Commit and push changes + cd wiki + git add . + if git diff --staged --quiet; then + echo "No documentation changes to commit" + else + git commit -m "Update documentation from commit ${{ github.sha }}" + git push + fi diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..7f5c4be --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,34 @@ +name: Build Documentation + +on: + push: + branches: [main] + pull_request: + +jobs: + build-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: DeterminateSystems/nix-installer-action@main + + - uses: DeterminateSystems/magic-nix-cache-action@main + + - name: Build documentation + run: | + nix build .#docs + nix build .#athenix-options + + - name: Upload documentation + uses: actions/upload-artifact@v4 + with: + name: athenix-docs + path: result/ + + - name: Deploy to GitHub Pages + if: github.ref == 'refs/heads/main' + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./result diff --git a/.nixd.json b/.nixd.json new file mode 100644 index 0000000..5f76258 --- /dev/null +++ b/.nixd.json @@ -0,0 +1,18 @@ +{ + "eval": { + "target": { + "installable": ".#nixosConfigurations.nix-desktop1.options" + } + }, + "formatting": { + "command": ["nixfmt"] + }, + "options": { + "nixos": { + "expr": "(builtins.getFlake \"/home/engr-ugaif/athenix\").nixosConfigurations.nix-desktop1.options" + }, + "home-manager": { + "expr": "(builtins.getFlake \"/home/engr-ugaif/athenix\").nixosConfigurations.nix-desktop1.config.home-manager.users.engr-ugaif.options" + } + } +} \ No newline at end of file diff --git a/flake.nix b/flake.nix index 5e509ff..1801d7d 100644 --- a/flake.nix +++ b/flake.nix @@ -80,6 +80,7 @@ ./parts/nixos-modules.nix ./parts/packages.nix ./parts/templates.nix + ./parts/docs.nix ./inventory.nix ./users.nix ]; diff --git a/fleet/common.nix b/fleet/common.nix index 089ba3c..1671060 100644 --- a/fleet/common.nix +++ b/fleet/common.nix @@ -24,14 +24,28 @@ default = null; description = '' Convenience option to configure a host for a specific user. - Automatically enables the user (sets athenix.users.username.enable = true). - Value should be a username from athenix.users.accounts. + + When set, automatically: + - Enables the user account (athenix.users..enable = true) + - Sets as default WSL user (on WSL systems) + + The username must exist in athenix.users (defined in users.nix). ''; + example = "engr-ugaif"; }; host.useHostPrefix = lib.mkOption { type = lib.types.bool; default = true; - description = "Whether to prepend the host prefix to the hostname (used in inventory and hosts/default.nix)."; + description = '' + Whether to prepend the hardware type prefix to the hostname. + + When true: + - "nix-laptop" with device "1" → hostname "nix-laptop1" + - "nix-wsl" with device "alice" → hostname "nix-wsl-alice" + + When false: + - Device name becomes the full hostname (useful for custom names) + ''; }; }; diff --git a/fleet/fleet-option.nix b/fleet/fleet-option.nix index 6f75082..cd32642 100644 --- a/fleet/fleet-option.nix +++ b/fleet/fleet-option.nix @@ -1,8 +1,8 @@ # ============================================================================ # Fleet Option Definition # ============================================================================ -# This module only defines the athenix.fleet option without any dependencies. -# Used by fleet/default.nix to evaluate inventory data without circular dependencies. +# This module defines the athenix.fleet and athenix.hwTypes options. +# Self-contained fleet management without dependencies on user configuration. { inputs, lib, ... }: let fleetDefinition = lib.mkOption { @@ -59,40 +59,67 @@ let ) ); }; - # Submodule defining the structure of a user account + + # Forward declaration for user options (full definition in user-config.nix) + # This allows users.nix to be evaluated at flake level userSubmodule = lib.types.submodule { options = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether this user account is enabled on this system."; + }; isNormalUser = lib.mkOption { type = lib.types.bool; default = true; + description = "Whether this is a normal user account (vs system user)."; }; description = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; + description = "Full name or description of the user (GECOS field)."; + example = "John Doe"; }; extraGroups = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; + description = "Additional groups for the user (wheel, docker, etc.)."; + example = [ + "wheel" + "networkmanager" + "docker" + ]; }; hashedPassword = lib.mkOption { type = lib.types.str; default = "!"; + description = '' + Hashed password for the user account. + Generate with: mkpasswd -m sha-512 + Default "!" means account is locked (SSH key only). + ''; }; extraPackages = lib.mkOption { type = lib.types.listOf lib.types.package; default = [ ]; + description = "Additional system packages available to this user."; + example = lib.literalExpression "[ pkgs.vim pkgs.git ]"; }; excludePackages = lib.mkOption { type = lib.types.listOf lib.types.package; default = [ ]; + description = "System packages to exclude for this user."; }; homePackages = lib.mkOption { type = lib.types.listOf lib.types.package; default = [ ]; + description = "Packages to install in the user's home-manager profile."; + example = lib.literalExpression "[ pkgs.firefox pkgs.vscode ]"; }; extraImports = lib.mkOption { type = lib.types.listOf lib.types.path; default = [ ]; + description = "Additional home-manager modules to import for this user."; }; external = lib.mkOption { type = lib.types.nullOr ( @@ -104,21 +131,22 @@ let ); default = null; description = '' - External user configuration module. Can be: - - A path to a local module directory - - A fetchGit/fetchTarball result pointing to a repository + External user configuration module from Git or local path. - The external module can contain: - - user.nix (optional): Sets athenix.users. options AND home-manager config - - nixos.nix (optional): System-level NixOS configuration - - Example: builtins.fetchGit { url = "https://github.com/user/dotfiles"; rev = "..."; } + Should contain user.nix (user options + home-manager config) + and optionally nixos.nix (system-level config). ''; + example = lib.literalExpression '' + builtins.fetchGit { + url = "https://github.com/username/dotfiles"; + rev = "abc123..."; + }''; }; opensshKeys = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; - description = "List of SSH public keys for the user."; + description = "SSH public keys for the user (authorized_keys)."; + example = [ "ssh-ed25519 AAAAC3Nza... user@host" ]; }; shell = lib.mkOption { type = lib.types.nullOr ( @@ -130,7 +158,7 @@ let ] ); default = "bash"; - description = "The shell for this user."; + description = "Default shell for the user."; }; editor = lib.mkOption { type = lib.types.nullOr ( @@ -143,23 +171,18 @@ let ] ); default = "neovim"; - description = "The default editor for this user."; + description = "Default text editor for the user (sets EDITOR)."; }; useZshTheme = lib.mkOption { type = lib.types.bool; default = true; - description = "Whether to apply the system Zsh theme."; + description = "Whether to apply the system Zsh theme (Oh My Posh)."; }; useNvimPlugins = lib.mkOption { type = lib.types.bool; default = true; description = "Whether to apply the system Neovim configuration."; }; - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Whether this user account is enabled on this system."; - }; }; }; in diff --git a/fleet/fs.nix b/fleet/fs.nix index 97fd6d0..04987ec 100644 --- a/fleet/fs.nix +++ b/fleet/fs.nix @@ -13,17 +13,38 @@ device = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; - description = "The main disk device to use for installation."; + description = '' + The main disk device to use for automated partitioning and installation. + + When set, enables disko for declarative disk management with: + - 1GB EFI boot partition + - Optional swap partition (see swapSize) + - Root partition using remaining space + + Leave null for systems that don't need disk partitioning (containers, WSL). + ''; + example = "/dev/nvme0n1"; }; useSwap = lib.mkOption { type = lib.types.bool; default = true; - description = "Whether to create and use a swap partition."; + description = '' + Whether to create and use a swap partition. + Disable for systems with ample RAM or SSDs where swap is undesirable. + ''; }; swapSize = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; - description = "The size of the swap partition."; + description = '' + Size of the swap partition (e.g., "16G", "32G"). + + Recommended sizes: + - 8-16GB for desktops with 16GB+ RAM + - 32GB for laptops (enables hibernation) + - Match RAM size for systems <8GB RAM + ''; + example = "32G"; }; }; }; diff --git a/fleet/user-config.nix b/fleet/user-config.nix index 199fa4c..821fd7b 100644 --- a/fleet/user-config.nix +++ b/fleet/user-config.nix @@ -9,9 +9,8 @@ # ============================================================================ # User Configuration Module # ============================================================================ -# This module defines the schema for user accounts and handles their creation. -# It bridges the gap between the data in 'users.nix' and the actual NixOS -# and Home Manager configuration. +# This module implements user account creation and home-manager setup. +# Options are defined in fleet-option.nix for early availability. let # Helper: Resolve external module path from fetchGit/fetchTarball/path diff --git a/hw/nix-desktop.nix b/hw/nix-desktop.nix index d7116f9..7d9182e 100644 --- a/hw/nix-desktop.nix +++ b/hw/nix-desktop.nix @@ -46,5 +46,5 @@ # ========== Software Profile ========== athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "desktop"; + athenix.sw.desktop.enable = lib.mkDefault true; } diff --git a/hw/nix-ephemeral.nix b/hw/nix-ephemeral.nix index 3d71a81..6f76fad 100644 --- a/hw/nix-ephemeral.nix +++ b/hw/nix-ephemeral.nix @@ -62,5 +62,5 @@ hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "stateless-kiosk"; + athenix.sw.stateless-kiosk.enable = lib.mkDefault true; } diff --git a/hw/nix-laptop.nix b/hw/nix-laptop.nix index ec0e60e..d39db37 100644 --- a/hw/nix-laptop.nix +++ b/hw/nix-laptop.nix @@ -59,5 +59,5 @@ }; athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "desktop"; + athenix.sw.desktop.enable = lib.mkDefault true; } diff --git a/hw/nix-lxc.nix b/hw/nix-lxc.nix index ff8a938..a1b47a6 100644 --- a/hw/nix-lxc.nix +++ b/hw/nix-lxc.nix @@ -56,5 +56,5 @@ ]; athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "headless"; + athenix.sw.headless.enable = lib.mkDefault true; } diff --git a/hw/nix-surface.nix b/hw/nix-surface.nix index 36544f0..d0c3855 100644 --- a/hw/nix-surface.nix +++ b/hw/nix-surface.nix @@ -65,5 +65,5 @@ in # ========== Software Profile ========== athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "tablet-kiosk"; # Touch-optimized kiosk mode + athenix.sw.tablet-kiosk.enable = lib.mkDefault true; # Touch-optimized kiosk mode } diff --git a/hw/nix-wsl.nix b/hw/nix-wsl.nix index 6e9e6e4..018c29c 100644 --- a/hw/nix-wsl.nix +++ b/hw/nix-wsl.nix @@ -20,7 +20,13 @@ options.athenix.host.wsl.user = lib.mkOption { type = lib.types.str; default = "engr-ugaif"; - description = "The default user to log in as in WSL."; + description = '' + The default user to automatically log in as when starting WSL. + + This user must be enabled via athenix.users..enable = true. + Tip: Use athenix.forUser = "username" as a shortcut to set both. + ''; + example = "alice"; }; config = { @@ -32,7 +38,7 @@ # ========== Software Profile ========== athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "headless"; + athenix.sw.headless.enable = lib.mkDefault true; # ========== Remote Development ========== services.vscode-server.enable = true; diff --git a/hw/nix-zima.nix b/hw/nix-zima.nix index a87ef60..70c6bef 100644 --- a/hw/nix-zima.nix +++ b/hw/nix-zima.nix @@ -45,5 +45,5 @@ # ========== Software Profile ========== athenix.sw.enable = lib.mkDefault true; - athenix.sw.type = lib.mkDefault "desktop"; + athenix.sw.desktop.enable = lib.mkDefault true; } diff --git a/installer/modules.nix b/installer/modules.nix index 9f59981..b152028 100644 --- a/installer/modules.nix +++ b/installer/modules.nix @@ -20,7 +20,7 @@ let in { # Software configuration module - main module with all athenix.sw options - # Use athenix.sw.type to select profile: "desktop", "tablet-kiosk", "headless", "stateless-kiosk" + # Use athenix.sw..enable to enable software profiles: desktop, tablet-kiosk, headless, stateless-kiosk, builders hw = hostTypes; sw = { diff --git a/inventory.nix b/inventory.nix index 7f9a6bd..3ae8054 100644 --- a/inventory.nix +++ b/inventory.nix @@ -92,10 +92,10 @@ nix-surface = { defaultCount = 3; devices = { - "1".athenix.sw.kioskUrl = "https://google.com"; + "1".athenix.sw.tablet-kiosk.kioskUrl = "https://google.com"; }; overrides = { - athenix.sw.kioskUrl = "https://yahoo.com"; + athenix.sw.tablet-kiosk.kioskUrl = "https://yahoo.com"; }; }; @@ -106,24 +106,24 @@ "nix-builder" = { # Gitea Actions self-hosted runner configuration athenix.sw = { - type = [ - "headless" - "builders" - ]; - builders.giteaRunner = { + headless.enable = true; + builders = { enable = true; - url = "https://git.factory.uga.edu"; - # Token file must be created manually at this path with a Gitea runner token - # Generate in repository settings: Settings > Actions > Runners > Create new Runner - # echo "TOKEN=YOUR_TOKEN_HERE" | sudo tee /var/lib/gitea-runner-token > /dev/null - tokenFile = "/var/lib/gitea-runner-token"; - # Labels to identify this runner in workflows - extraLabels = [ - "self-hosted" - "nix-builder" - ]; - # Runner service name - name = "athenix"; + giteaRunner = { + enable = true; + url = "https://git.factory.uga.edu"; + # Token file must be created manually at this path with a Gitea runner token + # Generate in repository settings: Settings > Actions > Runners > Create new Runner + # echo "TOKEN=YOUR_TOKEN_HERE" | sudo tee /var/lib/gitea-runner-token > /dev/null + tokenFile = "/var/lib/gitea-runner-token"; + # Labels to identify this runner in workflows + extraLabels = [ + "self-hosted" + "nix-builder" + ]; + # Runner service name + name = "athenix"; + }; }; }; }; diff --git a/parts/docs.nix b/parts/docs.nix new file mode 100644 index 0000000..36505d6 --- /dev/null +++ b/parts/docs.nix @@ -0,0 +1,132 @@ +# Documentation generation +{ + inputs, + self, + lib, + ... +}: +let + pkgs = inputs.nixpkgs.legacyPackages.x86_64-linux; + + # Extract options from a sample configuration + getAthenixOptions = + configName: + let + nixosConfig = self.nixosConfigurations.${configName}; + evaledOptions = nixosConfig.options; + + # Filter to just athenix namespace + athenixOptions = evaledOptions.athenix or { }; + in + athenixOptions; + + # Generate markdown documentation from options + optionsToMarkdown = + options: + pkgs.writeText "options.md" '' + # Athenix Configuration Options + + This document describes all available configuration options in the Athenix namespace. + + ## Quick Reference + + - **athenix.sw** - Software configuration (desktop, headless, kiosk modes) + - **athenix.users** - User account management + - **athenix.host** - Host-specific settings (filesystem, build methods) + - **athenix.fleet** - Fleet inventory definitions + - **athenix.forUser** - Convenience option to enable a user + - **athenix.system.gc** - Garbage collection settings + + ## Detailed Options + + For detailed option information, use: + ```bash + # View all athenix options + nix eval .#nixosConfigurations.nix-desktop1.options.athenix --apply builtins.attrNames + + # View specific option description + nix eval .#nixosConfigurations.nix-desktop1.options.athenix.sw.desktop.enable.description + + # Export all options to JSON + nix build .#athenix-options + cat result | jq + ``` + + ## Software Types + + Enable different system configurations: + + - **desktop** - Full KDE Plasma desktop environment + - **headless** - Server/container configuration + - **tablet-kiosk** - Touch-optimized kiosk for tablets + - **stateless-kiosk** - Diskless PXE boot kiosk + - **builders** - Build server with optional Gitea Actions runner + + See the individual option descriptions for detailed information. + ''; +in +{ + perSystem = + { system, ... }: + lib.mkIf (system == "x86_64-linux") { + packages = { + # Generate option documentation in markdown + docs = + pkgs.runCommand "athenix-docs" + { + nativeBuildInputs = [ pkgs.jq ]; + } + '' + mkdir -p $out + + # Copy existing documentation + ${if builtins.pathExists ../README.md then "cp ${../README.md} $out/README.md" else ""} + ${if builtins.pathExists ../docs then "cp -r ${../docs} $out/guides" else ""} + + # Generate options reference + cat > $out/OPTIONS.md << 'EOF' + ${builtins.readFile (optionsToMarkdown (getAthenixOptions "nix-desktop1"))} + EOF + + echo "Documentation generated in $out" + ''; + + # Extract just the athenix namespace options as JSON + athenix-options = + let + nixosConfig = + self.nixosConfigurations.nix-desktop1 + or (builtins.head (builtins.attrValues self.nixosConfigurations)); + + # Recursively extract option information + extractOption = + opt: + if opt ? _type && opt._type == "option" then + { + inherit (opt) description; + type = opt.type.description or (opt.type.name or "unknown"); + default = + if opt ? default then + if builtins.isAttrs opt.default && opt.default ? _type then "" else opt.default + else + null; + example = + if opt ? example then + if builtins.isAttrs opt.example && opt.example ? _type then "" else opt.example + else + null; + } + else if builtins.isAttrs opt then + lib.mapAttrs (name: value: extractOption value) ( + # Filter out internal attributes + lib.filterAttrs (n: _: !lib.hasPrefix "_" n) opt + ) + else + null; + + athenixOpts = nixosConfig.options.athenix or { }; + in + pkgs.writeText "athenix-options.json" (builtins.toJSON (extractOption athenixOpts)); + }; + }; +} diff --git a/sw/builders/default.nix b/sw/builders/default.nix index 96f8aa2..c6a3684 100644 --- a/sw/builders/default.nix +++ b/sw/builders/default.nix @@ -11,21 +11,111 @@ ... }: -lib.mkMerge [ - (import ./programs.nix { - inherit - config - lib - pkgs - inputs - ; - }) - (import ./services.nix { - inherit - config - lib - pkgs - inputs - ; - }) -] +with lib; + +let + cfg = config.athenix.sw.builders; +in +{ + options.athenix.sw.builders = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable build server configuration. + + Includes: + - SSH host keys for common Git servers (factory.uga.edu, github.com) + - Gitea Actions runner support (optional) + - Build tools and dependencies + + Recommended for: CI/CD servers, build containers, development infrastructure + ''; + example = true; + }; + + giteaRunner = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable Gitea Actions self-hosted runner. + + This runner will connect to a Gitea instance and execute CI/CD workflows. + Requires manual setup of the token file before the service will start. + ''; + example = true; + }; + + url = mkOption { + type = types.str; + description = '' + URL of the Gitea instance to connect to. + This should be the base URL without any path components. + ''; + example = "https://git.factory.uga.edu"; + }; + + tokenFile = mkOption { + type = types.path; + default = "/var/lib/gitea-runner-token"; + description = '' + Path to file containing Gitea runner registration token. + + To generate: + 1. Go to your Gitea repository settings + 2. Navigate to Actions > Runners + 3. Click "Create new Runner" + 4. Save the token to this file: + echo "TOKEN=your-token-here" | sudo tee /var/lib/gitea-runner-token > /dev/null + + The service will not start until this file exists. + ''; + example = "/var/secrets/gitea-runner-token"; + }; + + extraLabels = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + Additional labels to identify this runner in workflow files. + Use labels to target specific runners for different job types. + ''; + example = [ + "self-hosted" + "nix" + "x86_64-linux" + ]; + }; + + name = mkOption { + type = types.str; + default = "athenix"; + description = '' + Unique name for this runner instance. + Shown in Gitea's runner list and logs. + ''; + example = "nix-builder-1"; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + (import ./programs.nix { + inherit + config + lib + pkgs + inputs + ; + }) + (import ./services.nix { + inherit + config + lib + pkgs + inputs + ; + }) + ]); +} diff --git a/sw/builders/programs.nix b/sw/builders/programs.nix index b6e9f78..98cd2cd 100644 --- a/sw/builders/programs.nix +++ b/sw/builders/programs.nix @@ -1,8 +1,6 @@ { config, lib, - pkgs, - inputs, ... }: @@ -10,7 +8,7 @@ with lib; let cfg = config.athenix.sw; - basePackages = with pkgs; [ + basePackages = [ # Build-related packages can be added here if needed ]; in diff --git a/sw/default.nix b/sw/default.nix index 8ae4d39..f70fabc 100644 --- a/sw/default.nix +++ b/sw/default.nix @@ -10,19 +10,14 @@ # Software Module Entry Point # ============================================================================ # This module manages the software configuration for the system. It provides -# options to select the system type ('desktop' or 'kiosk') and handles -# the conditional importation of the appropriate sub-modules. +# enable options for each system type (desktop, headless, builders, etc.) +# that can be enabled independently or in combination. Each type is a proper +# NixOS submodule with its own enable flag and type-specific options. with lib; let cfg = config.athenix.sw; - - # Normalize type to always be a list - swTypes = if isList cfg.type then cfg.type else [ cfg.type ]; - - # Helper to check if a type is enabled - hasType = type: elem type swTypes; in { imports = [ @@ -31,169 +26,83 @@ in ./gc.nix ./updater.nix ./update-ref.nix + ./desktop + ./headless + ./builders + ./tablet-kiosk + ./stateless-kiosk inputs.home-manager.nixosModules.home-manager inputs.agenix.nixosModules.default inputs.disko.nixosModules.disko ]; options.athenix.sw = { - enable = mkEnableOption "Standard Workstation Configuration"; + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable standard workstation configuration with base packages. + Provides: + - Base CLI tools (htop, git, binutils) + - Shell configuration (Zsh) + - Secret management (agenix) + - Oh My Posh shell theme + + This is typically enabled automatically when any sw type is enabled. + ''; + }; + + # DEPRECATED: Backwards compatibility for external modules + # Use athenix.sw..enable instead type = mkOption { - type = types.oneOf [ - (types.enum [ - "desktop" - "tablet-kiosk" - "headless" - "stateless-kiosk" - "builders" - ]) - (types.listOf ( - types.enum [ - "desktop" - "tablet-kiosk" - "headless" - "stateless-kiosk" - "builders" - ] - )) - ]; - default = "desktop"; - description = "Type(s) of system configuration. Can be a single type or a list of types to combine multiple configurations."; + type = types.nullOr (types.either types.str (types.listOf types.str)); + default = null; + description = "DEPRECATED: Use athenix.sw..enable instead. Legacy type selection."; + visible = false; }; extraPackages = mkOption { type = types.listOf types.package; default = [ ]; - description = "Extra packages to install."; + description = '' + Additional system packages to install beyond the defaults. + These packages are added to environment.systemPackages. + ''; + example = lib.literalExpression "[ pkgs.vim pkgs.wget pkgs.curl ]"; }; excludePackages = mkOption { type = types.listOf types.package; default = [ ]; - description = "Packages to exclude from the default list."; - }; - - kioskUrl = mkOption { - type = types.str; - default = "https://ha.factory.uga.edu"; - description = "URL to open in Chromium kiosk mode."; - }; - - # Builders-specific options - builders = mkOption { - type = types.submodule { - options = { - giteaRunner = { - enable = mkEnableOption "Gitea Actions self-hosted runner"; - - url = mkOption { - type = types.str; - description = "Gitea instance URL for the runner"; - }; - - tokenFile = mkOption { - type = types.path; - default = "/var/lib/gitea-runner-token"; - description = '' - Path to file containing Gitea runner token. - Generate in Gitea repository settings under Actions > Runners. - The token must have runner registration access. - ''; - }; - - extraLabels = mkOption { - type = types.listOf types.str; - default = [ ]; - description = "Extra labels to identify this runner in workflows"; - }; - - name = mkOption { - type = types.str; - default = "athenix"; - description = "Name of the Gitea runner service"; - }; - }; - }; - }; - default = { }; - description = "Builder-specific configuration options"; + description = '' + Packages to exclude from the default package list. + Useful for removing unwanted default packages. + ''; + example = lib.literalExpression "[ pkgs.htop ]"; }; }; - config = mkIf cfg.enable (mkMerge [ - { - # ========== System-Wide Configuration ========== - nixpkgs.config.allowUnfree = true; + config = mkIf cfg.enable { + # ========== System-Wide Configuration ========== + nixpkgs.config.allowUnfree = true; - # ========== Shell Configuration ========== - programs.zsh.enable = true; - programs.nix-ld.enable = true; # Allow running non-NixOS binaries + # ========== Shell Configuration ========== + programs.zsh.enable = true; + programs.nix-ld.enable = true; # Allow running non-NixOS binaries - # ========== Base Packages ========== - environment.systemPackages = - with pkgs; - subtractLists cfg.excludePackages [ - htop # System monitor - binutils # Binary utilities - zsh # Z shell - git # Version control - oh-my-posh # Shell prompt theme - age # Simple file encryption tool - age-plugin-fido2-hmac # age FIDO2 support - inputs.agenix.packages.${stdenv.hostPlatform.system}.default # Secret management - ]; - } - # ========== Software Profile Imports ========== - (mkIf (hasType "desktop") ( - import ./desktop { - inherit - config - lib - pkgs - inputs - ; - } - )) - (mkIf (hasType "tablet-kiosk") ( - import ./tablet-kiosk { - inherit - config - lib - pkgs - inputs - ; - } - )) - (mkIf (hasType "headless") ( - import ./headless { - inherit - config - lib - pkgs - inputs - ; - } - )) - (mkIf (hasType "stateless-kiosk") ( - import ./stateless-kiosk { - inherit - config - lib - pkgs - inputs - ; - } - )) - (mkIf (hasType "builders") ( - import ./builders { - inherit - config - lib - pkgs - inputs - ; - } - )) - ]); + # ========== Base Packages ========== + environment.systemPackages = + with pkgs; + subtractLists cfg.excludePackages [ + htop # System monitor + binutils # Binary utilities + zsh # Z shell + git # Version control + oh-my-posh # Shell prompt theme + age # Simple file encryption tool + age-plugin-fido2-hmac # age FIDO2 support + inputs.agenix.packages.${stdenv.hostPlatform.system}.default # Secret management + ]; + }; } diff --git a/sw/desktop/default.nix b/sw/desktop/default.nix index bce1715..c806969 100644 --- a/sw/desktop/default.nix +++ b/sw/desktop/default.nix @@ -10,21 +10,50 @@ inputs, ... }: -lib.mkMerge [ - (import ./programs.nix { - inherit - config - lib - pkgs - inputs - ; - }) - (import ./services.nix { - inherit - config - lib - pkgs - inputs - ; - }) -] + +with lib; + +let + cfg = config.athenix.sw.desktop; +in +{ + options.athenix.sw.desktop = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable full desktop environment with KDE Plasma 6. + + Includes: + - KDE Plasma 6 desktop with SDDM display manager + - Full graphical software suite (Firefox, Chromium, LibreOffice) + - Printing and scanning support (CUPS) + - Virtualization (libvirt, virt-manager) + - Bluetooth and audio (PipeWire) + - Video conferencing (Zoom, Teams) + + Recommended for: Workstations, development machines, user desktops + ''; + example = true; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + (import ./programs.nix { + inherit + config + lib + pkgs + inputs + ; + }) + (import ./services.nix { + inherit + config + lib + pkgs + inputs + ; + }) + ]); +} diff --git a/sw/desktop/programs.nix b/sw/desktop/programs.nix index 63d3811..c4a804b 100644 --- a/sw/desktop/programs.nix +++ b/sw/desktop/programs.nix @@ -2,7 +2,6 @@ config, lib, pkgs, - inputs, ... }: diff --git a/sw/desktop/services.nix b/sw/desktop/services.nix index 11a654c..226f045 100644 --- a/sw/desktop/services.nix +++ b/sw/desktop/services.nix @@ -1,5 +1,4 @@ { - config, lib, pkgs, ... diff --git a/sw/gc.nix b/sw/gc.nix index e679c02..ad3903f 100644 --- a/sw/gc.nix +++ b/sw/gc.nix @@ -1,7 +1,6 @@ { config, lib, - pkgs, ... }: { @@ -10,22 +9,40 @@ enable = lib.mkOption { type = lib.types.bool; default = true; - description = "Whether to enable automatic garbage collection."; + description = '' + Enable automatic garbage collection of old NixOS generations. + Helps keep disk usage under control on long-running systems. + ''; }; frequency = lib.mkOption { type = lib.types.str; default = "weekly"; - description = "How often to run garbage collection (systemd timer format)."; + description = '' + How often to run garbage collection (systemd timer format). + + Common values: "daily", "weekly", "monthly" + Advanced: "*-*-* 03:00:00" (daily at 3 AM) + ''; + example = "daily"; }; retentionDays = lib.mkOption { type = lib.types.int; default = 30; - description = "Number of days to keep old generations before deletion."; + description = '' + Number of days to keep old system generations before deletion. + + Older generations allow rolling back system changes. + Recommended: 30-90 days for workstations, 7-14 for servers. + ''; + example = 60; }; optimise = lib.mkOption { type = lib.types.bool; default = true; - description = "Whether to automatically optimize the Nix store."; + description = '' + Whether to automatically hard-link identical files in the Nix store. + Can save significant disk space but uses CPU during optimization. + ''; }; }; diff --git a/sw/ghostty.nix b/sw/ghostty.nix index 282f263..677d9c9 100644 --- a/sw/ghostty.nix +++ b/sw/ghostty.nix @@ -1,6 +1,4 @@ { - config, - lib, pkgs, ... }: diff --git a/sw/headless/default.nix b/sw/headless/default.nix index 9a62788..c87fe94 100644 --- a/sw/headless/default.nix +++ b/sw/headless/default.nix @@ -11,21 +11,47 @@ ... }: -lib.mkMerge [ - (import ./programs.nix { - inherit - config - lib - pkgs - inputs - ; - }) - (import ./services.nix { - inherit - config - lib - pkgs - inputs - ; - }) -] +with lib; + +let + cfg = config.athenix.sw.headless; +in +{ + options.athenix.sw.headless = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable minimal headless server configuration. + + Includes: + - SSH server with password authentication + - Minimal CLI tools (tmux, man) + - Systemd-networkd for networking + - No graphical environment + + Recommended for: Servers, containers (LXC), WSL, remote systems + ''; + example = true; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + (import ./programs.nix { + inherit + config + lib + pkgs + inputs + ; + }) + (import ./services.nix { + inherit + config + lib + pkgs + inputs + ; + }) + ]); +} diff --git a/sw/headless/programs.nix b/sw/headless/programs.nix index 99b78ca..4110a28 100644 --- a/sw/headless/programs.nix +++ b/sw/headless/programs.nix @@ -2,7 +2,6 @@ config, lib, pkgs, - inputs, ... }: diff --git a/sw/headless/services.nix b/sw/headless/services.nix index 2b981c1..d8ced32 100644 --- a/sw/headless/services.nix +++ b/sw/headless/services.nix @@ -1,7 +1,4 @@ { - config, - lib, - pkgs, ... }: diff --git a/sw/stateless-kiosk/default.nix b/sw/stateless-kiosk/default.nix index dedfad4..a79a68d 100644 --- a/sw/stateless-kiosk/default.nix +++ b/sw/stateless-kiosk/default.nix @@ -7,37 +7,77 @@ inputs, ... }: -lib.mkMerge [ - (import ./kiosk-browser.nix { - inherit - config - lib - pkgs - inputs - ; - }) - (import ./services.nix { - inherit - config - lib - pkgs - inputs - ; - }) - (import ./net.nix { - inherit - config - lib - pkgs - inputs - ; - }) - (import ./programs.nix { - inherit - config - lib - pkgs - inputs - ; - }) -] + +with lib; + +let + cfg = config.athenix.sw.stateless-kiosk; +in +{ + options.athenix.sw.stateless-kiosk = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable stateless kiosk mode for diskless PXE boot systems. + + Includes: + - Sway (Wayland compositor) + - Chromium in fullscreen kiosk mode + - MAC address-based URL routing + - Network-only boot (no local storage) + - Auto-start browser on boot + + Recommended for: Assembly line stations, diskless kiosks, PXE boot displays + ''; + example = true; + }; + + kioskUrl = mkOption { + type = types.str; + default = "https://ha.factory.uga.edu"; + description = '' + Default URL to display in the kiosk browser. + + Note: For stateless-kiosk, MAC address-based routing may override this. + See sw/stateless-kiosk/mac-hostmap.nix for MAC-to-URL mappings. + ''; + example = "https://homeassistant.lan:8123/lovelace/dashboard"; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + (import ./kiosk-browser.nix { + inherit + config + lib + pkgs + inputs + ; + }) + (import ./services.nix { + inherit + config + lib + pkgs + inputs + ; + }) + (import ./net.nix { + inherit + config + lib + pkgs + inputs + ; + }) + (import ./programs.nix { + inherit + config + lib + pkgs + inputs + ; + }) + ]); +} diff --git a/sw/stateless-kiosk/kiosk-browser.nix b/sw/stateless-kiosk/kiosk-browser.nix index b1ff685..dee864b 100644 --- a/sw/stateless-kiosk/kiosk-browser.nix +++ b/sw/stateless-kiosk/kiosk-browser.nix @@ -1,7 +1,6 @@ # This module configures Chromium for kiosk mode under Sway. # It includes a startup script that determines the kiosk URL based on the machine's MAC address. { - config, lib, pkgs, ... diff --git a/sw/tablet-kiosk/default.nix b/sw/tablet-kiosk/default.nix index 50e14fa..93872bd 100644 --- a/sw/tablet-kiosk/default.nix +++ b/sw/tablet-kiosk/default.nix @@ -5,29 +5,68 @@ inputs, ... }: -lib.mkMerge [ - (import ./programs.nix { - inherit - config - lib - pkgs - inputs - ; - }) - (import ./services.nix { - inherit - config - lib - pkgs - inputs - ; - }) - (import ./gsettings.nix { - inherit - config - lib - pkgs - inputs - ; - }) -] + +with lib; + +let + cfg = config.athenix.sw.tablet-kiosk; +in +{ + options.athenix.sw.tablet-kiosk = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable tablet kiosk mode with touch-optimized interface. + + Includes: + - Phosh mobile desktop environment + - Chromium in fullscreen kiosk mode + - On-screen keyboard (Squeekboard) + - Auto-login and auto-start browser + - Touch gesture support + - Optimized for Surface Pro tablets + + Recommended for: Surface tablets, touchscreen kiosks, interactive displays + ''; + example = true; + }; + + kioskUrl = mkOption { + type = types.str; + default = "https://ha.factory.uga.edu"; + description = '' + URL to display in the kiosk browser on startup. + The browser will automatically navigate to this URL in fullscreen mode. + ''; + example = "https://dashboard.example.com"; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + (import ./programs.nix { + inherit + config + lib + pkgs + inputs + ; + }) + (import ./services.nix { + inherit + config + lib + pkgs + inputs + ; + }) + (import ./gsettings.nix { + inherit + config + lib + pkgs + inputs + ; + }) + ]); +} diff --git a/sw/tablet-kiosk/services.nix b/sw/tablet-kiosk/services.nix index 3f2d84b..821bcba 100644 --- a/sw/tablet-kiosk/services.nix +++ b/sw/tablet-kiosk/services.nix @@ -155,7 +155,7 @@ --noerrdialogs \ --disable-session-crashed-bubble \ --disable-infobars \ - ${config.athenix.sw.kioskUrl} + ${config.athenix.sw.tablet-kiosk.kioskUrl} ''; }; }; diff --git a/sw/theme.nix b/sw/theme.nix index 7d785f4..dc27483 100644 --- a/sw/theme.nix +++ b/sw/theme.nix @@ -1,7 +1,6 @@ { pkgs, config, - osConfig, lib, ... }: diff --git a/sw/updater.nix b/sw/updater.nix index 4481a72..81ce317 100644 --- a/sw/updater.nix +++ b/sw/updater.nix @@ -14,22 +14,42 @@ with lib; hosts = mkOption { type = types.listOf types.str; default = [ "engr-ugaif@192.168.11.133 x86_64-linux" ]; - description = "List of remote build hosts for system rebuilding."; + description = '' + List of remote build hosts for system rebuilding. + + Format: "user@hostname architecture" + Each host must have SSH access and nix-daemon available. + + Useful for offloading builds from low-power devices (tablets, laptops) + to more powerful build servers. + ''; + example = lib.literalExpression '' + [ + "builder@nix-builder x86_64-linux" + "user@192.168.1.100 aarch64-linux" + ]''; }; enable = mkOption { type = types.bool; default = false; - description = "Whether to enable remote build for 'update-system' command."; + description = '' + Whether to enable remote builds for the 'update-system' command. + + When enabled, 'update-system' will use the configured remote hosts + to build the new system configuration instead of building locally. + + Automatically enabled for tablet-kiosk systems. + ''; }; }; }; default = { }; - description = "Remote build configuration"; + description = "Remote build configuration for system updates."; }; config = { - athenix.sw.remoteBuild.enable = lib.mkDefault (config.athenix.sw.type == "tablet-kiosk"); + athenix.sw.remoteBuild.enable = lib.mkDefault (config.athenix.sw.tablet-kiosk.enable); environment.systemPackages = [ (pkgs.writeShellScriptBin "update-system" '' diff --git a/templates/user/user.nix b/templates/user/user.nix index 36e8cb0..3a9bfb5 100644 --- a/templates/user/user.nix +++ b/templates/user/user.nix @@ -1,4 +1,4 @@ -{ inputs, ... }: +{ ... }: # ============================================================================ # User Configuration @@ -15,7 +15,6 @@ # nixos-systems configuration (nixpkgs, home-manager, etc.). { - config, lib, pkgs, osConfig ? null, # Only available in home-manager context @@ -60,7 +59,7 @@ fd bat ] - ++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox; + ++ lib.optional (osConfig.athenix.sw.desktop.enable or false) firefox; # Conditionally add packages based on system type # ========== Programs ========== -- 2.39.5