This commit is contained in:
mjallen18
2025-08-22 22:53:29 -05:00
parent 57a079a86f
commit 36ca3ed90e
14 changed files with 683 additions and 0 deletions

130
lib/README.md Normal file
View File

@@ -0,0 +1,130 @@
# mjallen-lib Utility Functions
This directory contains utility functions that can be used to enhance your Nix configuration. These functions are inspired by the khanelinix repository and provide a more explicit and modular approach to building Nix configurations.
## Directory Structure
- `default.nix`: Main entry point that imports and exposes all utility functions
- `module/`: Utilities for module creation and option handling
- `file/`: Utilities for file handling and module discovery
- `system/`: Utilities for system configuration building
## How to Use
### 1. Import the Library
The library is already imported in your flake.nix file through the outputs-builder:
```nix
outputs-builder = channels: {
formatter = inputs.treefmt-nix.lib.mkWrapper channels.nixpkgs ./treefmt.nix;
# Add mjallen-lib to the flake outputs
overlays = {
mjallen-lib = final: prev: {
mjallen-lib = (import ./lib { inherit inputs; }).mjallen-lib;
};
};
};
```
This makes the mjallen-lib available to all your modules through the extended lib.
### 2. Use the Module Utilities
The module utilities provide functions for creating modules with consistent options:
```nix
{ lib, ... }:
let
inherit (lib.mjallen.module) mkModule mkOpt mkBoolOpt;
in
mkModule {
name = "mymodule";
description = "My awesome module";
options = {
setting1 = mkOpt lib.types.str "default" "Description of setting1";
setting2 = mkBoolOpt false "Description of setting2";
};
config = {
# Module implementation
};
}
```
### 3. Use the File Utilities
The file utilities provide functions for file handling and module discovery:
```nix
{ lib, ... }:
let
inherit (lib.mjallen.file) safeImport importModulesRecursive;
in
{
# Import a file with error handling
myConfig = safeImport ./my-config.nix {};
# Import all modules recursively
imports = importModulesRecursive ./modules;
}
```
### 4. Use the System Utilities
The system utilities provide functions for building system configurations:
```nix
{ lib, ... }:
let
inherit (lib.mjallen.system.common) mkHomeManagerConfig;
in
{
# Build home-manager configurations
homeManagerConfig = mkHomeManagerConfig {
extendedLib = lib;
inputs = inputs;
system = "x86_64-linux";
matchingHomes = { ... };
};
}
```
## Available Functions
### Module Utilities
- `mkModule`: Create a module with common options
- `mkOpt`: Create an option with a type, default value, and description
- `mkOpt'`: Create an option with a type and default value (no description)
- `mkBoolOpt`: Create a boolean option with a default value and description
- `mkBoolOpt'`: Create a boolean option with a default value (no description)
- `enabled`: Standard enable pattern
- `disabled`: Standard disable pattern
- `capitalize`: Capitalize a string
- `boolToNum`: Convert a boolean to a number
- `default-attrs`: Apply mkDefault to all attributes
- `force-attrs`: Apply mkForce to all attributes
- `nested-default-attrs`: Apply default-attrs to nested attributes
- `nested-force-attrs`: Apply force-attrs to nested attributes
### File Utilities
- `readFile`: Read a file and return its contents
- `pathExists`: Check if a file exists
- `safeImport`: Import a nix file with error handling
- `scanDir`: Scan a directory and return directory names
- `getFile`: Get a file path relative to the flake root
- `importModulesRecursive`: Recursively discover and import all Nix modules in a directory tree
- `scanSystems`: Recursively scan systems directory structure
- `filterNixOSSystems`: Filter systems for NixOS (Linux)
- `filterDarwinSystems`: Filter systems for Darwin (macOS)
- `scanHomes`: Scan homes directory structure for home configurations
### System Utilities
- `mkExtendedLib`: Extend the nixpkgs lib with mjallen-lib
- `mkNixpkgsConfig`: Create a nixpkgs configuration
- `mkHomeConfigs`: Create home configurations for a system and hostname
- `mkHomeManagerConfig`: Create a home-manager configuration
- `mkSpecialArgs`: Create special arguments for a system configuration

16
lib/default.nix Normal file
View File

@@ -0,0 +1,16 @@
{ inputs, ... }:
{
mjallen-lib = {
# Import module utilities
module = import ./module { inherit inputs; };
# Import file utilities
file = import ./file { inherit inputs; };
# Import system utilities
system = import ./system { inherit inputs; };
# Import examples
examples = import ./examples { inherit inputs; };
};
}

8
lib/examples/default.nix Normal file
View File

@@ -0,0 +1,8 @@
{ inputs, ... }:
{
# Import all examples
sops = import ./sops.nix;
homeSops = import ./home-sops.nix;
fileUtils = import ./file-utils.nix;
systemUtils = import ./system-utils.nix;
}

View File

@@ -0,0 +1,58 @@
{ lib, ... }:
let
inherit (lib.mjallen.file)
readFile
pathExists
safeImport
scanDir
getFile
importModulesRecursive
scanSystems
filterNixOSSystems
filterDarwinSystems
scanHomes;
in
{
# Example of reading a file
myFileContent = readFile ./example.txt;
# Example of checking if a file exists
fileExists = pathExists ./example.txt;
# Example of safely importing a file
myConfig = safeImport ./my-config.nix {};
# Example of scanning a directory
directoryContents = scanDir ./modules;
# Example of getting a file path relative to the flake root
flakeFile = getFile "flake.nix";
# Example of importing modules recursively
modules = importModulesRecursive ./modules;
# Example of scanning systems
allSystems = scanSystems ./systems;
# Example of filtering systems
nixosSystems = filterNixOSSystems allSystems;
darwinSystems = filterDarwinSystems allSystems;
# Example of scanning homes
allHomes = scanHomes ./homes;
# Example of using these functions together
nixosConfigurations = lib.mapAttrs' (
name:
{ system, hostname, ... }:
{
name = hostname;
value = lib.nixosSystem {
inherit system;
modules = [
{ networking.hostName = hostname; }
] ++ importModulesRecursive ./modules/nixos;
};
}
) nixosSystems;
}

View File

@@ -0,0 +1,31 @@
{ config, lib, pkgs, ... }:
let
inherit (lib.mjallen.module) mkModule mkOpt mkBoolOpt;
in
mkModule {
name = "sops";
description = "SOPS secret management for home-manager";
options = {
defaultSopsFile = mkOpt lib.types.path null "Default sops file.";
sshKeyPaths = mkOpt (lib.types.listOf lib.types.str) [] "SSH Key paths to use.";
};
config = {
home.packages = with pkgs; [
age
sops
ssh-to-age
];
sops = {
inherit (config.mjallen.sops) defaultSopsFile;
defaultSopsFormat = "yaml";
age = {
generateKey = true;
keyFile = "${config.home.homeDirectory}/.config/sops/age/keys.txt";
sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ] ++ config.mjallen.sops.sshKeyPaths;
};
};
};
}

36
lib/examples/sops.nix Normal file
View File

@@ -0,0 +1,36 @@
{ config, lib, ... }:
let
inherit (lib.mjallen.module) mkModule mkOpt mkBoolOpt;
in
mkModule {
name = "sops";
description = "SOPS secret management";
options = {
defaultSopsFile = mkOpt lib.types.path null "Default sops file.";
generateAgeKey = mkBoolOpt true "Whether to automatically generate an age key if one doesn't exist.";
ageKeyPath = mkOpt (lib.types.nullOr lib.types.str) null "Custom path to the age key file. If null, will use the default path.";
sshKeyPaths = mkOpt (lib.types.listOf lib.types.str) [
"/etc/ssh/ssh_host_ed25519_key"
] "SSH Key paths to use.";
validateSopsFiles = mkBoolOpt false "Whether to validate that sops files exist.";
};
config = {
sops = {
inherit (config.mjallen.sops) defaultSopsFile validateSopsFiles;
age = {
inherit (config.mjallen.sops) generateAgeKey;
keyFile = if config.mjallen.sops.ageKeyPath != null
then config.mjallen.sops.ageKeyPath
else "${config.users.users.${config.mjallen.user.name}.home}/.config/sops/age/keys.txt";
sshKeyPaths = config.mjallen.sops.sshKeyPaths;
};
};
};
}

View File

@@ -0,0 +1,111 @@
{ inputs, ... }:
let
inherit (inputs.self.mjallen-lib.system.common)
mkExtendedLib
mkNixpkgsConfig
mkHomeConfigs
mkHomeManagerConfig
mkSpecialArgs;
in
{
# Example of creating NixOS configurations
nixosConfigurations =
let
# Get all systems
allSystems = inputs.self.mjallen-lib.file.scanSystems ../systems;
# Filter for NixOS systems
nixosSystems = inputs.self.mjallen-lib.file.filterNixOSSystems allSystems;
in
inputs.nixpkgs.lib.mapAttrs' (
name:
{ system, hostname, ... }:
let
# Create extended lib with mjallen-lib
extendedLib = mkExtendedLib inputs.self inputs.nixpkgs;
# Find matching home configurations for this system
matchingHomes = mkHomeConfigs {
flake = inputs.self;
inherit system hostname;
};
# Create home-manager configuration
homeManagerConfig = mkHomeManagerConfig {
inherit extendedLib inputs system matchingHomes;
isNixOS = true;
};
in
{
name = hostname;
value = inputs.nixpkgs.lib.nixosSystem {
inherit system;
# Pass special arguments to modules
specialArgs = mkSpecialArgs {
inherit inputs hostname extendedLib;
username = "mjallen";
};
modules = [
# Set lib to extended lib
{ _module.args.lib = extendedLib; }
# Configure nixpkgs
{
nixpkgs = {
inherit system;
} // mkNixpkgsConfig inputs.self;
}
# Import home-manager module
inputs.home-manager.nixosModules.home-manager
# Auto-inject home configurations
homeManagerConfig
# Import all nixos modules recursively
../${system}/${hostname}
] ++ (extendedLib.mjallen.file.importModulesRecursive ../modules/nixos);
};
}
) nixosSystems;
# Example of creating home-manager configurations
homeConfigurations =
let
# Get all homes
allHomes = inputs.self.mjallen-lib.file.scanHomes ../homes;
in
inputs.nixpkgs.lib.mapAttrs' (
name:
{ system, username, hostname, userAtHost, path, ... }:
let
# Create extended lib with mjallen-lib
extendedLib = mkExtendedLib inputs.self inputs.nixpkgs;
in
{
name = userAtHost;
value = inputs.home-manager.lib.homeManagerConfiguration {
pkgs = import inputs.nixpkgs {
inherit system;
inherit ((mkNixpkgsConfig inputs.self)) config overlays;
};
extraSpecialArgs = {
inherit inputs hostname username system;
inherit (inputs) self;
lib = extendedLib;
};
modules = [
# Set lib to extended lib
{ _module.args.lib = extendedLib; }
# Import the home configuration
path
] ++ (extendedLib.mjallen.file.importModulesRecursive ../modules/home);
};
}
) allHomes;
}

127
lib/file/default.nix Normal file
View File

@@ -0,0 +1,127 @@
{ inputs, ... }@args:
let
# Get self from args or default to ../.. (the flake root)
self = if args ? self then args.self else ../..;
inherit (inputs.nixpkgs.lib)
genAttrs
filterAttrs
hasPrefix
foldl'
;
in
{
# Read a file and return its contents
readFile = path: builtins.readFile path;
# Check if a file exists
pathExists = path: builtins.pathExists path;
# Import a nix file with error handling
safeImport = path: default: if builtins.pathExists path then import path else default;
# Scan a directory and return directory names
scanDir = path: builtins.attrNames (builtins.readDir path);
# Get a file path relative to the flake root (similar to Snowfall's get-file)
getFile = relativePath: self + "/${relativePath}";
# Recursively discover and import all Nix modules in a directory tree
importModulesRecursive =
path:
let
# Helper function to recursively walk directories
walkDir =
currentPath:
let
currentEntries = builtins.readDir currentPath;
entryNames = builtins.attrNames currentEntries;
# Get all directories that contain default.nix
directoriesWithDefault = builtins.filter (
name:
currentEntries.${name} == "directory" && builtins.pathExists (currentPath + "/${name}/default.nix")
) entryNames;
# Get ALL directories (to recurse into)
allDirectories = builtins.filter (name: currentEntries.${name} == "directory") entryNames;
# Import directories that have default.nix
directoryImports = map (name: currentPath + "/${name}") directoriesWithDefault;
# Recursively walk ALL subdirectories
subDirImports = builtins.concatLists (map (dir: walkDir (currentPath + "/${dir}")) allDirectories);
in
directoryImports ++ subDirImports;
in
walkDir path;
# Recursively scan systems directory structure
scanSystems =
systemsPath:
let
systemArchs = builtins.attrNames (builtins.readDir systemsPath);
generateSystemConfigs =
system:
let
systemPath = systemsPath + "/${system}";
hosts = builtins.attrNames (builtins.readDir systemPath);
in
genAttrs hosts (hostname: {
inherit system hostname;
path = systemPath + "/${hostname}";
});
in
foldl' (acc: system: acc // generateSystemConfigs system) { } systemArchs;
# Filter systems for NixOS (Linux)
filterNixOSSystems =
systems:
filterAttrs (
_name: { system, ... }: hasPrefix "x86_64-linux" system || hasPrefix "aarch64-linux" system
) systems;
# Filter systems for Darwin (macOS)
filterDarwinSystems =
systems:
filterAttrs (
_name: { system, ... }: hasPrefix "aarch64-darwin" system || hasPrefix "x86_64-darwin" system
) systems;
# Scan homes directory structure for home configurations
scanHomes =
homesPath:
let
systemArchs = builtins.attrNames (builtins.readDir homesPath);
generateHomeConfigs =
system:
let
systemPath = homesPath + "/${system}";
userAtHosts = builtins.attrNames (builtins.readDir systemPath);
parseUserAtHost =
userAtHost:
let
# Split "username@hostname" into parts
parts = builtins.split "@" userAtHost;
username = builtins.head parts;
hostname = builtins.elemAt parts 2; # After split: [username, "@", hostname]
in
{
inherit
system
username
hostname
userAtHost
;
path = systemPath + "/${userAtHost}";
};
in
genAttrs userAtHosts parseUserAtHost;
in
foldl' (acc: system: acc // generateHomeConfigs system) { } systemArchs;
}

98
lib/system/common.nix Normal file
View File

@@ -0,0 +1,98 @@
{ inputs }:
let
inherit (inputs.nixpkgs.lib) filterAttrs mapAttrs';
in
{
mkExtendedLib = flake: nixpkgs: nixpkgs.lib.extend (final: prev: {
mjallen = flake.mjallen-lib;
});
mkNixpkgsConfig = flake: {
overlays = builtins.attrValues flake.overlays;
config = {
allowAliases = false;
allowUnfree = true;
permittedInsecurePackages = [
# Add any permitted insecure packages here
];
};
};
mkHomeConfigs =
{
flake,
system,
hostname,
}:
let
inherit (flake.mjallen-lib.file) scanHomes;
homesPath = ../../homes;
allHomes = scanHomes homesPath;
in
filterAttrs (
_name: homeConfig: homeConfig.system == system && homeConfig.hostname == hostname
) allHomes;
mkHomeManagerConfig =
{
extendedLib,
inputs,
system,
matchingHomes,
isNixOS ? true,
}:
if matchingHomes != { } then
{
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = {
inherit inputs system;
inherit (inputs) self;
lib = extendedLib;
};
sharedModules = [
{ _module.args.lib = extendedLib; }
]
++ (extendedLib.mjallen.file.importModulesRecursive ../../modules/home);
users = mapAttrs' (_name: homeConfig: {
name = homeConfig.username;
value = {
imports = [ homeConfig.path ];
home = {
inherit (homeConfig) username;
homeDirectory = inputs.nixpkgs.lib.mkDefault (
if isNixOS then "/home/${homeConfig.username}" else "/Users/${homeConfig.username}"
);
};
}
// (
if isNixOS then
{
_module.args.username = homeConfig.username;
}
else
{ }
);
}) matchingHomes;
};
}
else
{ };
mkSpecialArgs =
{
inputs,
hostname,
username,
extendedLib,
}:
{
inherit inputs hostname username;
inherit (inputs) self;
lib = extendedLib;
namespace = "mjallen";
format = "system";
host = hostname;
};
}

5
lib/system/default.nix Normal file
View File

@@ -0,0 +1,5 @@
{ inputs }:
{
# Common utilities used by system builders
common = import ./common.nix { inherit inputs; };
}