mirror of
https://github.com/moons-14/dotfiles.git
synced 2026-10-07 07:04:31 +09:00
Compare commits
3
Commits
19bc309b8b
...
9771a85e3f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9771a85e3f | ||
|
|
5b1bde7d90 | ||
|
|
1d82ab92b6 |
+10
-6
@@ -19,9 +19,13 @@
|
|||||||
!/flake/
|
!/flake/
|
||||||
!/overlays/
|
!/overlays/
|
||||||
|
|
||||||
!/modules/applications/
|
!/images/
|
||||||
!/modules/hardwares/
|
!/secrets/
|
||||||
!/modules/profiles/
|
|
||||||
!/modules/services/
|
!/hosts/
|
||||||
!/modules/systems/
|
|
||||||
!/modules/users/
|
!/libs/
|
||||||
|
|
||||||
|
!/modules/
|
||||||
|
|
||||||
|
!/tests/
|
||||||
|
|||||||
@@ -1,28 +1,574 @@
|
|||||||
# Repository Guidelines
|
# Repository Guidelines
|
||||||
|
|
||||||
## Project Structure & Module Organization
|
## Project Structure and Ownership
|
||||||
|
|
||||||
This repository manages NixOS and Home Manager configuration as a flake. `flake.nix` defines inputs and imports the project modules. Keep flake-level plumbing in `flake/`, reusable package overlays in `overlays/`, and development environments in `shells/`. Add configuration under the appropriate `modules/` category: `applications/`, `hardwares/`, `profiles/`, `services/`, `systems/`, or `users/`. Keep host- or user-specific choices near their owning module rather than in the root flake.
|
This repository manages NixOS, nix-darwin, and Home Manager configurations as a
|
||||||
|
flake. `flake.nix` defines inputs and delegates flake outputs through
|
||||||
|
flake-parts. Keep configuration with the component that owns it, rather than in
|
||||||
|
the root flake or an unrelated host.
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
| ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `modules/applications/` | One software component, including GUI applications, window managers, desktop environments, CLI tools, and editors |
|
||||||
|
| `modules/systems/` | OS foundations such as Nix, boot, locale, Wayland, and networking |
|
||||||
|
| `modules/services/` | Daemons, long-running services, and configuration that involves permissions or user groups |
|
||||||
|
| `modules/hardwares/` | Reusable drivers, hardware families, and VM or WSL guest configuration |
|
||||||
|
| `modules/users/` | User identity and the user's NixOS-, nix-darwin-, and Home Manager-specific definitions |
|
||||||
|
| `modules/profiles/` | Purpose- or form-factor-oriented compositions of multiple units |
|
||||||
|
| `hosts/` | Machine-specific facts and the profiles or applications selected for each machine |
|
||||||
|
| `libs/` | Registry, unit discovery, and host construction logic |
|
||||||
|
| `overlays/` | Package replacements and additions |
|
||||||
|
| `shells/` | Development shells |
|
||||||
|
| `flake/` | Supporting flake outputs such as formatters, checks, and Git hooks |
|
||||||
|
|
||||||
|
Use **unit** as the generic internal term for a Registry-managed component and
|
||||||
|
**profile** for a unit that composes multiple units. Do not introduce a
|
||||||
|
`features/` layer. Window managers and desktop environments such as niri and
|
||||||
|
GNOME belong in `modules/applications/`; do not create a separate `desktop/`
|
||||||
|
module category.
|
||||||
|
|
||||||
|
Before adding configuration, decide whether it is owned by an application,
|
||||||
|
system foundation, service, hardware family, user, profile, or individual host.
|
||||||
|
Prefer the following placements:
|
||||||
|
|
||||||
|
| Configuration | Placement |
|
||||||
|
| ---------------------------------------------------- | ------------------------------------------------ |
|
||||||
|
| Nix settings shared by every system host | `modules/systems/nix/common.nix` |
|
||||||
|
| NixOS-only boot configuration | `modules/systems/boot/.../nixos.nix` |
|
||||||
|
| Ghostty-specific configuration | `modules/applications/ghostty/` |
|
||||||
|
| niri-specific configuration | `modules/applications/niri/` |
|
||||||
|
| Applications selected for the niri environment | `modules/profiles/interface/niri/meta.nix` |
|
||||||
|
| GNOME itself | `modules/applications/gnome/` |
|
||||||
|
| Docker daemon and Docker group membership | `modules/services/docker/nixos.nix` |
|
||||||
|
| Reusable ThinkPad-family configuration | `modules/hardwares/thinkpad/` |
|
||||||
|
| The laptop unit composition | `modules/profiles/platform/laptop/meta.nix` |
|
||||||
|
| The development-environment unit composition | `modules/profiles/workload/development/meta.nix` |
|
||||||
|
| A user's OS- and Home Manager-specific configuration | `modules/users/<name>/` |
|
||||||
|
| x1g13-specific monitor layout | `hosts/x1g13/home.nix` |
|
||||||
|
| x1g13-specific disk UUID | `hosts/x1g13/nixos.nix` |
|
||||||
|
| Package replacement or addition | `overlays/` |
|
||||||
|
| Formatter, checks, or Git hooks | `flake/` |
|
||||||
|
|
||||||
|
## Unit Discovery and Identity
|
||||||
|
|
||||||
|
A directory below `modules/` is a unit if, and only if, it directly contains at
|
||||||
|
least one reserved file. Directories used only for classification, such as
|
||||||
|
`modules/applications/` or `modules/profiles/interface/`, are namespaces rather
|
||||||
|
than units when they have no reserved file of their own.
|
||||||
|
|
||||||
|
The Registry recognizes exactly these five reserved filenames:
|
||||||
|
|
||||||
|
| File | Target and responsibility |
|
||||||
|
| ------------ | -------------------------------------------------------------------------------- |
|
||||||
|
| `common.nix` | System-side configuration fragment shared by NixOS and nix-darwin |
|
||||||
|
| `nixos.nix` | NixOS-only configuration fragment |
|
||||||
|
| `darwin.nix` | nix-darwin-only configuration fragment |
|
||||||
|
| `home.nix` | Home Manager configuration fragment |
|
||||||
|
| `meta.nix` | Registry descriptor for dependencies, external modules, and descriptive metadata |
|
||||||
|
|
||||||
|
`common.nix` is never applied to Home Manager. OS-independent Home Manager
|
||||||
|
configuration still belongs in `home.nix`.
|
||||||
|
|
||||||
|
The Registry derives a unit ID from the path relative to `modules/`, joining
|
||||||
|
path components with dots. Category names remain plural. It also derives the
|
||||||
|
enable option by prefixing the same components with `my` and appending `enable`.
|
||||||
|
|
||||||
|
| Unit directory | Unit ID | Enable option |
|
||||||
|
| ---------------------------------- | ------------------------- | ----------------------------------- |
|
||||||
|
| `modules/applications/ghostty/` | `applications.ghostty` | `my.applications.ghostty.enable` |
|
||||||
|
| `modules/applications/niri/` | `applications.niri` | `my.applications.niri.enable` |
|
||||||
|
| `modules/systems/boot/uefi/` | `systems.boot.uefi` | `my.systems.boot.uefi.enable` |
|
||||||
|
| `modules/services/docker/` | `services.docker` | `my.services.docker.enable` |
|
||||||
|
| `modules/hardwares/qemu-guest/` | `hardwares.qemu-guest` | `my.hardwares.qemu-guest.enable` |
|
||||||
|
| `modules/users/moons/` | `users.moons` | `my.users.moons.enable` |
|
||||||
|
| `modules/profiles/interface/niri/` | `profiles.interface.niri` | `my.profiles.interface.niri.enable` |
|
||||||
|
|
||||||
|
Represent option paths as attribute-path lists, never as Nix source encoded in
|
||||||
|
strings or evaluated dynamically. Generate and read attributes with helpers such
|
||||||
|
as `lib.setAttrByPath` and `lib.getAttrFromPath`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
id = "applications.ghostty";
|
||||||
|
|
||||||
|
optionPath = [
|
||||||
|
"my"
|
||||||
|
"applications"
|
||||||
|
"ghostty"
|
||||||
|
"enable"
|
||||||
|
];
|
||||||
|
|
||||||
|
kind = "applications";
|
||||||
|
name = "ghostty";
|
||||||
|
|
||||||
|
relativePath = [
|
||||||
|
"applications"
|
||||||
|
"ghostty"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For `modules/profiles/interface/niri/`, path inference additionally gives
|
||||||
|
`kind = "profiles"`, `group = "interface"`, and `name = "niri"`. The path is
|
||||||
|
always authoritative for identity. `meta.nix` may provide display metadata such
|
||||||
|
as `description`, but it must not override or alias the unit ID.
|
||||||
|
|
||||||
|
## Unit Files and Fragment Contract
|
||||||
|
|
||||||
|
Only reserved files that a unit actually needs should exist. The Registry
|
||||||
|
registers present fragments and does not require empty or placeholder files. All
|
||||||
|
of the following are valid units:
|
||||||
|
|
||||||
|
```text
|
||||||
|
# Home Manager only
|
||||||
|
modules/applications/ghostty/
|
||||||
|
├── home.nix
|
||||||
|
└── settings.nix
|
||||||
|
|
||||||
|
# NixOS only
|
||||||
|
modules/applications/gnome/
|
||||||
|
└── nixos.nix
|
||||||
|
|
||||||
|
# nix-darwin only
|
||||||
|
modules/systems/macos-defaults/
|
||||||
|
└── darwin.nix
|
||||||
|
|
||||||
|
# NixOS and Home Manager, with metadata and helpers
|
||||||
|
modules/applications/niri/
|
||||||
|
├── nixos.nix
|
||||||
|
├── home.nix
|
||||||
|
├── meta.nix
|
||||||
|
├── settings.nix
|
||||||
|
└── keybindings.nix
|
||||||
|
|
||||||
|
# Metadata only, commonly a composition profile
|
||||||
|
modules/profiles/interface/niri/
|
||||||
|
└── meta.nix
|
||||||
|
|
||||||
|
# System configuration shared by NixOS and nix-darwin
|
||||||
|
modules/systems/nix/
|
||||||
|
└── common.nix
|
||||||
|
```
|
||||||
|
|
||||||
|
If a unit has no `home.nix`, do not generate or apply a Home Manager module for
|
||||||
|
it. The same rule applies independently to `common.nix`, `nixos.nix`, and
|
||||||
|
`darwin.nix`.
|
||||||
|
|
||||||
|
### Configuration fragments
|
||||||
|
|
||||||
|
`common.nix`, `nixos.nix`, `darwin.nix`, and `home.nix` are configuration
|
||||||
|
fragments to which the Registry adds the enable condition. They return the
|
||||||
|
configuration for their class directly and must not define top-level `imports`,
|
||||||
|
`options`, or `config` attributes:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# modules/services/docker/nixos.nix
|
||||||
|
{ primaryUser, ... }:
|
||||||
|
{
|
||||||
|
virtualisation.docker = {
|
||||||
|
enable = true;
|
||||||
|
autoPrune.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.${primaryUser}.extraGroups = [
|
||||||
|
"docker"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Conceptually, the Registry supplies a wrapper like this:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ config, lib, ... }@args:
|
||||||
|
{
|
||||||
|
config =
|
||||||
|
lib.mkIf
|
||||||
|
config.my.services.docker.enable
|
||||||
|
(import dockerNixosPath args);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not add hand-written `mkEnableOption`, `cfg`, or `mkIf` boilerplate to each
|
||||||
|
unit. The Registry generates the enable option from the unit path and guards the
|
||||||
|
fragment.
|
||||||
|
|
||||||
|
### Helper files and directories
|
||||||
|
|
||||||
|
Every filename other than the five reserved names is an ordinary helper,
|
||||||
|
regardless of its extension. The Registry neither discovers nor automatically
|
||||||
|
imports helper files such as `settings.nix`, `keybindings.nix`, `packages.nix`,
|
||||||
|
`colors.nix`, `rules.nix`, or `helpers.nix`. Import a helper explicitly from the
|
||||||
|
reserved fragment that uses it:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# modules/applications/niri/home.nix
|
||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
settings = import ./settings.nix;
|
||||||
|
keybindings = import ./keybindings.nix;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
programs.niri.settings = lib.recursiveUpdate settings {
|
||||||
|
binds = keybindings;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not use a leading underscore to mark a file private; `_settings.nix` has no
|
||||||
|
special meaning. Give helper files descriptive names instead. When helpers are
|
||||||
|
configuration functions, pass the module arguments explicitly and combine them
|
||||||
|
with normal Nix expressions:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# modules/applications/example/home.nix
|
||||||
|
{ lib, ... }@args:
|
||||||
|
lib.mkMerge [
|
||||||
|
(import ./packages.nix args)
|
||||||
|
(import ./settings.nix args)
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The same discovery rule applies recursively to helper directories:
|
||||||
|
|
||||||
|
```text
|
||||||
|
modules/applications/niri/
|
||||||
|
├── home.nix
|
||||||
|
└── parts/
|
||||||
|
├── appearance.nix
|
||||||
|
└── keybindings.nix
|
||||||
|
```
|
||||||
|
|
||||||
|
Here `parts/` is not a unit because it directly contains no reserved file. A
|
||||||
|
helper directory that directly contains `home.nix` or another reserved file is
|
||||||
|
itself discovered as a unit, so never use reserved filenames inside a directory
|
||||||
|
that is intended to contain helpers only.
|
||||||
|
|
||||||
|
### Registry metadata
|
||||||
|
|
||||||
|
`meta.nix` is a Registry descriptor, not a NixOS, nix-darwin, or Home Manager
|
||||||
|
module. It may declare `description`, `includes`, and class-specific external
|
||||||
|
module imports:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# modules/applications/niri/meta.nix
|
||||||
|
{ inputs, ... }:
|
||||||
|
{
|
||||||
|
description = "Niri Wayland compositor";
|
||||||
|
|
||||||
|
includes = [
|
||||||
|
"systems.wayland"
|
||||||
|
"services.xdg-portal"
|
||||||
|
];
|
||||||
|
|
||||||
|
imports.nixos = [
|
||||||
|
inputs.niri-flake.nixosModules.niri
|
||||||
|
];
|
||||||
|
|
||||||
|
imports.home = [
|
||||||
|
inputs.niri-flake.homeModules.niri
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
External modules, including modules supplied by flake inputs, define Nix module
|
||||||
|
options and therefore belong in `meta.nix` under `imports.nixos`,
|
||||||
|
`imports.darwin`, or `imports.home`. Do not place them in a configuration
|
||||||
|
fragment's top-level `imports`: the Nix module system resolves imports before a
|
||||||
|
configuration-level enable condition.
|
||||||
|
|
||||||
|
`includes` lists units to enable whenever the declaring unit is enabled. Always
|
||||||
|
use fully qualified unit IDs:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# modules/profiles/interface/niri/meta.nix
|
||||||
|
{
|
||||||
|
includes = [
|
||||||
|
"applications.niri"
|
||||||
|
"applications.ghostty"
|
||||||
|
"applications.noctalia"
|
||||||
|
"applications.vicinae"
|
||||||
|
"applications.nautilus"
|
||||||
|
"services.xdg-portal"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Never omit a prefix such as `applications.` merely because the including unit is
|
||||||
|
a profile. Fully qualified IDs make ownership explicit and allow moves, name
|
||||||
|
collisions, and missing dependencies to be detected. Do not enable another unit
|
||||||
|
by assigning to its enable option from a class fragment; declare the dependency
|
||||||
|
in `meta.includes`.
|
||||||
|
|
||||||
|
Application metadata should include only dependencies technically required for
|
||||||
|
the application to work. A profile owns the user's choice to adopt several
|
||||||
|
otherwise independent applications together. For example, niri may include the
|
||||||
|
Wayland foundation and xdg-desktop-portal as technical dependencies, while
|
||||||
|
`profiles.interface.niri` selects Ghostty, Vicinae, Noctalia, and Nautilus.
|
||||||
|
Ghostty must not depend on niri, and niri-specific keybindings remain owned by
|
||||||
|
the niri unit.
|
||||||
|
|
||||||
|
## Profiles
|
||||||
|
|
||||||
|
Profiles compose units by purpose or form factor; they do not replace clear
|
||||||
|
application, system, service, or hardware ownership. Suitable profile namespaces
|
||||||
|
include:
|
||||||
|
|
||||||
|
```text
|
||||||
|
modules/profiles/
|
||||||
|
├── base/
|
||||||
|
├── interface/
|
||||||
|
│ ├── niri/
|
||||||
|
│ ├── gnome/
|
||||||
|
│ ├── cli-minimal/
|
||||||
|
│ └── cli-interactive/
|
||||||
|
├── platform/
|
||||||
|
│ ├── laptop/
|
||||||
|
│ ├── thinkpad/
|
||||||
|
│ ├── desktop/
|
||||||
|
│ ├── vm/
|
||||||
|
│ └── wsl/
|
||||||
|
├── workload/
|
||||||
|
│ ├── development/
|
||||||
|
│ ├── personal/
|
||||||
|
│ ├── server/
|
||||||
|
│ └── remote/
|
||||||
|
└── security/
|
||||||
|
└── secure-boot/
|
||||||
|
```
|
||||||
|
|
||||||
|
The `desktop/` name above is a form-factor profile under `profiles/platform/`,
|
||||||
|
not a top-level module category.
|
||||||
|
|
||||||
|
A profile may consist only of `meta.includes`. Small settings that belong only
|
||||||
|
to the composition and have no useful independent identity may go directly in
|
||||||
|
the profile's `nixos.nix`, `darwin.nix`, or `home.nix`. Extract configuration to
|
||||||
|
an appropriate application, system, service, or hardware unit when any of these
|
||||||
|
conditions holds:
|
||||||
|
|
||||||
|
- It should be independently enableable.
|
||||||
|
- Multiple profiles reuse it.
|
||||||
|
- It owns separate configuration files.
|
||||||
|
- Its NixOS, nix-darwin, and Home Manager implementations differ.
|
||||||
|
- Other units depend on it.
|
||||||
|
- It involves a daemon, permissions, or user groups.
|
||||||
|
|
||||||
|
## Registry Responsibilities
|
||||||
|
|
||||||
|
Implement unit discovery with Nix standard functionality such as
|
||||||
|
`builtins.readDir`. Do not depend on an external indiscriminate auto-import
|
||||||
|
mechanism, and do not design the repository around `import-tree`. Registry logic
|
||||||
|
has these responsibilities:
|
||||||
|
|
||||||
|
1. Recursively visit directories below `modules/`.
|
||||||
|
2. Check only the five reserved filenames directly within each directory.
|
||||||
|
3. Register a directory as a unit when at least one reserved file exists there.
|
||||||
|
4. Derive the unit ID from the path relative to `modules/`.
|
||||||
|
5. Record only class fragments that exist.
|
||||||
|
6. Evaluate `meta.nix` as a descriptor only when it exists.
|
||||||
|
7. Exclude non-reserved files from discovery and implicit imports.
|
||||||
|
8. Generate every unit's `my.<unit path>.enable` option.
|
||||||
|
9. Enable included units from `meta.includes`.
|
||||||
|
10. Raise a clear evaluation error for a reference to a missing unit ID.
|
||||||
|
11. Apply only the fragments appropriate to the current host class.
|
||||||
|
12. Pass `home.nix` to Home Manager only for hosts that enable Home Manager.
|
||||||
|
|
||||||
|
A unit record may conceptually look like this; the implementation need not use
|
||||||
|
this exact representation:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
id = "applications.ghostty";
|
||||||
|
directory = ./applications/ghostty;
|
||||||
|
|
||||||
|
fragments = {
|
||||||
|
common = null;
|
||||||
|
nixos = null;
|
||||||
|
darwin = null;
|
||||||
|
home = ./applications/ghostty/home.nix;
|
||||||
|
};
|
||||||
|
|
||||||
|
meta = { };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep the custom Registry limited to unit discovery, enable-option generation,
|
||||||
|
`includes`, and class dispatch. Do not reimplement general Nix imports or Nix
|
||||||
|
module evaluation. In particular, never infer a unit ID from metadata or
|
||||||
|
implicitly load a non-reserved file.
|
||||||
|
|
||||||
|
## Hosts and Class Dispatch
|
||||||
|
|
||||||
|
`hosts/` is outside Registry discovery. A host contains machine-specific facts,
|
||||||
|
differences, and unit selection, not reusable shared configuration. Appropriate
|
||||||
|
host-owned data includes:
|
||||||
|
|
||||||
|
- Generated `hardware-configuration.nix`.
|
||||||
|
- Disk UUIDs and disko target devices.
|
||||||
|
- Monitor identifiers, layout, and scale.
|
||||||
|
- MAC addresses and static IP addresses.
|
||||||
|
- Kernel parameters required by one machine only.
|
||||||
|
- `system.stateVersion`.
|
||||||
|
- Host-specific secret references.
|
||||||
|
- The profiles, applications, and other units enabled on that host.
|
||||||
|
|
||||||
|
A host registry may use a specification like this:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# hosts/default.nix
|
||||||
|
{
|
||||||
|
x1g13 = {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
user = "moons";
|
||||||
|
path = ./x1g13;
|
||||||
|
|
||||||
|
profiles = [
|
||||||
|
"base"
|
||||||
|
"platform.thinkpad"
|
||||||
|
"interface.niri"
|
||||||
|
"interface.gnome"
|
||||||
|
"workload.development"
|
||||||
|
"workload.personal"
|
||||||
|
"security.secure-boot"
|
||||||
|
];
|
||||||
|
|
||||||
|
applications = [
|
||||||
|
"codex-desktop"
|
||||||
|
];
|
||||||
|
|
||||||
|
units = [
|
||||||
|
"services.tailscale"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
macbook = {
|
||||||
|
system = "aarch64-darwin";
|
||||||
|
user = "moons";
|
||||||
|
path = ./macbook;
|
||||||
|
|
||||||
|
profiles = [
|
||||||
|
"base"
|
||||||
|
"platform.laptop"
|
||||||
|
"workload.development"
|
||||||
|
"workload.personal"
|
||||||
|
];
|
||||||
|
|
||||||
|
applications = [
|
||||||
|
"ghostty"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Treat entries in `profiles` and `applications` as IDs relative to their
|
||||||
|
respective category roots. Add the category prefixes during host construction:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
selectedUnits =
|
||||||
|
[ "users.${spec.user}" ]
|
||||||
|
++ map (name: "profiles.${name}") spec.profiles
|
||||||
|
++ map (name: "applications.${name}") spec.applications
|
||||||
|
++ spec.units or [ ];
|
||||||
|
```
|
||||||
|
|
||||||
|
`units` is an escape hatch for fully qualified service, system, hardware, or
|
||||||
|
other unit IDs. Prefer profiles for the main composition; do not make hosts list
|
||||||
|
large numbers of low-level units directly.
|
||||||
|
|
||||||
|
Host modules use normal Nix module semantics and are not Registry-guarded
|
||||||
|
configuration fragments. For example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
hosts/x1g13/
|
||||||
|
├── nixos.nix
|
||||||
|
├── home.nix
|
||||||
|
├── hardware-configuration.nix
|
||||||
|
└── disko.nix
|
||||||
|
```
|
||||||
|
|
||||||
|
`hosts/x1g13/nixos.nix` may explicitly load `hardware-configuration.nix` and
|
||||||
|
`disko.nix` with the normal top-level Nix module `imports`. Do not confuse these
|
||||||
|
host imports with the prohibition on top-level `imports` in unit configuration
|
||||||
|
fragments.
|
||||||
|
|
||||||
|
Derive the system class from the host's `system`:
|
||||||
|
|
||||||
|
- A Linux NixOS host receives `common.nix` and `nixos.nix`.
|
||||||
|
- A nix-darwin host receives `common.nix` and `darwin.nix`.
|
||||||
|
- A host with integrated Home Manager additionally receives `home.nix`.
|
||||||
|
|
||||||
|
Home Manager is additive, not a system class mutually exclusive with NixOS or
|
||||||
|
nix-darwin. The supported combinations are NixOS plus Home Manager and
|
||||||
|
nix-darwin plus Home Manager. If standalone Home Manager is supported later, add
|
||||||
|
an explicit host kind because `system` alone cannot distinguish it from NixOS.
|
||||||
|
|
||||||
|
Do not duplicate reusable settings in hosts, but do not force genuinely
|
||||||
|
machine-specific values into a common unit merely to remove a host-local line.
|
||||||
|
|
||||||
|
## Coding Style and Implementation Rules
|
||||||
|
|
||||||
|
Use two-space indentation in Nix files and let `nixfmt` decide layout. Prefer
|
||||||
|
small units, explicit imports, and descriptive kebab-case names, for example
|
||||||
|
`modules/services/media-server/nixos.nix`. Use camelCase for Nix attributes
|
||||||
|
unless an upstream option dictates otherwise. Shell snippets must pass `shfmt`
|
||||||
|
and `shellcheck`; YAML, TOML, and Markdown are formatted by the configured
|
||||||
|
treefmt tools.
|
||||||
|
|
||||||
|
When implementing or modifying modules:
|
||||||
|
|
||||||
|
- Do not create `features/` or a top-level `desktop/` module category.
|
||||||
|
- Do not add per-unit `mkEnableOption`, `cfg`, or `mkIf` boilerplate; the Registry
|
||||||
|
derives and guards enable options from paths.
|
||||||
|
- Put unit dependencies in `meta.includes`, not in direct assignments to another
|
||||||
|
unit's enable option from a class fragment.
|
||||||
|
- Do not assume any non-reserved file is discovered or loaded automatically.
|
||||||
|
- Do not require an `_` prefix for helper or private files.
|
||||||
|
- Do not create unused `common.nix`, `nixos.nix`, `darwin.nix`, `home.nix`, or
|
||||||
|
`meta.nix` files.
|
||||||
|
- Do not override a path-derived unit ID from `meta.nix`.
|
||||||
|
- Keep technical application dependencies separate from the applications a
|
||||||
|
personal environment chooses to combine in a profile.
|
||||||
|
- Do not rely on module-list ordering to override values. Use Nix module
|
||||||
|
priorities such as `lib.mkDefault`, `lib.mkForce`, `lib.mkBefore`, or
|
||||||
|
`lib.mkAfter` explicitly when required.
|
||||||
|
- Keep Registry responsibilities narrow; use normal Nix imports and module
|
||||||
|
evaluation for everything outside discovery, generated enables, includes, and
|
||||||
|
class dispatch.
|
||||||
|
|
||||||
## Build, Test, and Development Commands
|
## Build, Test, and Development Commands
|
||||||
|
|
||||||
- `nix develop .#dotnix` enters the main development shell and installs the repository's pre-commit hooks.
|
- `nix develop .#dotnix` enters the main development shell and installs the
|
||||||
- `nix develop .#android` provides Android platform tools such as `adb` and `fastboot`.
|
repository's pre-commit hooks.
|
||||||
|
- `nix develop .#android` provides Android platform tools such as `adb` and
|
||||||
|
`fastboot`.
|
||||||
- `nix fmt` formats all supported files through treefmt.
|
- `nix fmt` formats all supported files through treefmt.
|
||||||
- `nix flake check` evaluates flake outputs and runs configured checks.
|
- `nix flake check` evaluates flake outputs and runs configured checks.
|
||||||
- `pre-commit run --all-files` runs formatting, dead-code and static Nix checks, shell linting, and secret scanning.
|
- `pre-commit run --all-files` runs formatting, dead-code and static Nix checks,
|
||||||
- `nix flake update` refreshes pinned inputs in `flake.lock`; review lockfile changes before committing.
|
shell linting, and secret scanning.
|
||||||
|
- `nix flake update` refreshes pinned inputs in `flake.lock`; review lockfile
|
||||||
|
changes before committing.
|
||||||
|
|
||||||
If direnv is installed, `direnv allow` activates the `dotnix` shell from `.envrc` automatically.
|
If direnv is installed, `direnv allow` activates the `dotnix` shell from `.envrc`
|
||||||
|
automatically.
|
||||||
## Coding Style & Naming Conventions
|
|
||||||
|
|
||||||
Use two-space indentation in Nix files and let `nixfmt` decide layout. Prefer small modules with explicit imports and descriptive kebab-case filenames, for example `modules/services/media-server.nix`. Use camelCase for Nix attributes unless an upstream option dictates otherwise. Shell snippets must pass `shfmt` and `shellcheck`; YAML, TOML, and Markdown are formatted by the configured treefmt tools.
|
|
||||||
|
|
||||||
## Testing Guidelines
|
## Testing Guidelines
|
||||||
|
|
||||||
There is no separate unit-test suite. Before submitting changes, run `nix flake check` and `pre-commit run --all-files`. For system-specific changes, also build or evaluate the affected NixOS/Home Manager configuration without switching the live machine. Never commit generated secrets, `.age` plaintext, or local `.direnv/` state.
|
There is no separate unit-test suite. Before submitting changes, run
|
||||||
|
`nix flake check` and `pre-commit run --all-files`. For system-specific changes,
|
||||||
|
also build or evaluate the affected NixOS, nix-darwin, or Home Manager
|
||||||
|
configuration without switching the live machine. Never commit generated
|
||||||
|
secrets, `.age` plaintext, or local `.direnv/` state.
|
||||||
|
|
||||||
## Commit & Pull Request Guidelines
|
For Registry changes, test discovery of each supported fragment combination,
|
||||||
|
dependency closure through `meta.includes`, missing-unit errors, and class
|
||||||
|
dispatch. Verify that helper files are ignored until explicitly imported and
|
||||||
|
that directories without a directly contained reserved file remain namespaces.
|
||||||
|
|
||||||
Recent history favors short, lowercase, imperative subjects such as `fix` and `update action`; automated dependency commits use `chore(deps): ...`. Prefer a specific summary that states the affected area, such as `shells: add deployment tools`. Keep commits focused. Pull requests should explain the motivation, list affected hosts or profiles, report validation commands, and note any manual migration or secret-management steps. Include screenshots only for visible desktop or application configuration changes.
|
## Commit and Pull Request Guidelines
|
||||||
|
|
||||||
|
Recent history favors short, lowercase, imperative subjects such as `fix` and
|
||||||
|
`update action`; automated dependency commits use `chore(deps): ...`. Prefer a
|
||||||
|
specific summary that states the affected area, such as
|
||||||
|
`shells: add deployment tools`. Keep commits focused. Pull requests should
|
||||||
|
explain the motivation, list affected hosts or profiles, report validation
|
||||||
|
commands, and note any manual migration or secret-management steps. Include
|
||||||
|
screenshots only for visible desktop or application configuration changes.
|
||||||
|
|||||||
Generated
+22
@@ -570,6 +570,27 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nix-darwin": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1783744694,
|
||||||
|
"narHash": "sha256-2cp6N3rrwnGYLTx9l6N+NI+kwrCWxvJUbj5WJhvB29A=",
|
||||||
|
"owner": "nix-darwin",
|
||||||
|
"repo": "nix-darwin",
|
||||||
|
"rev": "c3e90c89649b07d1a96e4b9dd6cd0d6e44b91a74",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-darwin",
|
||||||
|
"ref": "nix-darwin-26.05",
|
||||||
|
"repo": "nix-darwin",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nix-index-database": {
|
"nix-index-database": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -962,6 +983,7 @@
|
|||||||
"lanzaboote": "lanzaboote",
|
"lanzaboote": "lanzaboote",
|
||||||
"llm-agents": "llm-agents",
|
"llm-agents": "llm-agents",
|
||||||
"niri-flake": "niri-flake",
|
"niri-flake": "niri-flake",
|
||||||
|
"nix-darwin": "nix-darwin",
|
||||||
"nix-index-database": "nix-index-database",
|
"nix-index-database": "nix-index-database",
|
||||||
"nixos-hardware": "nixos-hardware",
|
"nixos-hardware": "nixos-hardware",
|
||||||
"nixos-wsl": "nixos-wsl",
|
"nixos-wsl": "nixos-wsl",
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nix-darwin = {
|
||||||
|
url = "github:nix-darwin/nix-darwin/nix-darwin-26.05";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
# Hardware / Platform
|
# Hardware / Platform
|
||||||
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
|
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
|
||||||
nixos-wsl.url = "github:nix-community/NixOS-WSL";
|
nixos-wsl.url = "github:nix-community/NixOS-WSL";
|
||||||
|
|||||||
@@ -2,5 +2,6 @@
|
|||||||
imports = [
|
imports = [
|
||||||
./formatter.nix
|
./formatter.nix
|
||||||
./git-hooks.nix
|
./git-hooks.nix
|
||||||
|
./registry.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
dotfilesLib = import ../libs {
|
||||||
|
inherit inputs lib;
|
||||||
|
root = ../.;
|
||||||
|
};
|
||||||
|
hostSpecsPath = ../hosts/default.nix;
|
||||||
|
hostSpecs =
|
||||||
|
if builtins.pathExists hostSpecsPath then
|
||||||
|
let
|
||||||
|
value = import hostSpecsPath;
|
||||||
|
in
|
||||||
|
if builtins.isFunction value then
|
||||||
|
value (
|
||||||
|
builtins.intersectAttrs (builtins.functionArgs value) {
|
||||||
|
inherit inputs lib;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else
|
||||||
|
value
|
||||||
|
else
|
||||||
|
{ };
|
||||||
|
configurations = dotfilesLib.hosts.mkConfigurations hostSpecs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
flake = {
|
||||||
|
inherit (configurations) darwinConfigurations nixosConfigurations;
|
||||||
|
lib = dotfilesLib;
|
||||||
|
};
|
||||||
|
|
||||||
|
perSystem =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
checks.registry = import ../tests/registry.nix {
|
||||||
|
inherit inputs lib pkgs;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
x1g9 = {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
user = "moons";
|
||||||
|
path = ./x1g9;
|
||||||
|
|
||||||
|
profiles = [
|
||||||
|
"base"
|
||||||
|
"interface.cli-minimal"
|
||||||
|
"platform.laptop"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{ }
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./hardware.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
system.stateVersion = "26.05";
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib ? inputs.nixpkgs.lib,
|
||||||
|
root,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
registry = import ./registry.nix {
|
||||||
|
inherit inputs lib;
|
||||||
|
modulesRoot = root + "/modules";
|
||||||
|
};
|
||||||
|
hosts = import ./hosts.nix {
|
||||||
|
inherit inputs lib registry;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit hosts registry;
|
||||||
|
}
|
||||||
+168
@@ -0,0 +1,168 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
registry,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
ensure =
|
||||||
|
condition: message: value:
|
||||||
|
if condition then value else throw "host registry: ${message}";
|
||||||
|
|
||||||
|
isLinux = system: lib.hasSuffix "-linux" system;
|
||||||
|
isDarwin = system: lib.hasSuffix "-darwin" system;
|
||||||
|
|
||||||
|
hostFile =
|
||||||
|
spec: name:
|
||||||
|
let
|
||||||
|
path = spec.path + "/${name}";
|
||||||
|
in
|
||||||
|
if builtins.pathExists path then path else null;
|
||||||
|
|
||||||
|
selectedUnits =
|
||||||
|
spec:
|
||||||
|
[ "users.${spec.user}" ]
|
||||||
|
++ map (name: "profiles.${name}") (spec.profiles or [ ])
|
||||||
|
++ map (name: "applications.${name}") (spec.applications or [ ])
|
||||||
|
++ (spec.units or [ ]);
|
||||||
|
|
||||||
|
validateSpec =
|
||||||
|
name: spec:
|
||||||
|
ensure (builtins.isAttrs spec) "${name}: host specification must be an attribute set" (
|
||||||
|
ensure (spec ? system && builtins.isString spec.system) "${name}: system is required" (
|
||||||
|
ensure (isLinux spec.system || isDarwin spec.system)
|
||||||
|
"${name}: unsupported system '${spec.system}'; expected a Linux NixOS or Darwin system"
|
||||||
|
(
|
||||||
|
ensure (spec ? user && builtins.isString spec.user && spec.user != "") "${name}: user is required" (
|
||||||
|
ensure (spec ? path && builtins.pathExists spec.path)
|
||||||
|
"${name}: path must name an existing host directory"
|
||||||
|
(
|
||||||
|
ensure
|
||||||
|
(lib.all
|
||||||
|
(field: builtins.isList (spec.${field} or [ ]) && lib.all builtins.isString (spec.${field} or [ ]))
|
||||||
|
[
|
||||||
|
"profiles"
|
||||||
|
"applications"
|
||||||
|
"units"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
"${name}: profiles, applications, and units must be lists of strings"
|
||||||
|
(ensure (builtins.isBool (spec.homeManager or true)) "${name}: homeManager must be a boolean" spec)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
mkSpecialArgs = name: spec: {
|
||||||
|
inherit inputs registry;
|
||||||
|
inherit (spec) system;
|
||||||
|
hostName = name;
|
||||||
|
primaryUser = spec.user;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkHomeManagerModule =
|
||||||
|
name: spec: selected:
|
||||||
|
let
|
||||||
|
homePath = hostFile spec "home.nix";
|
||||||
|
homeModules = [
|
||||||
|
(registry.mkModule { class = "home"; })
|
||||||
|
(registry.mkSelectionModule selected)
|
||||||
|
]
|
||||||
|
++ lib.optional (homePath != null) homePath;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ inputs.home-manager.nixosModules.home-manager ];
|
||||||
|
|
||||||
|
home-manager = {
|
||||||
|
useGlobalPkgs = true;
|
||||||
|
useUserPackages = true;
|
||||||
|
extraSpecialArgs = mkSpecialArgs name spec;
|
||||||
|
users.${spec.user}.imports = homeModules;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mkDarwinHomeManagerModule =
|
||||||
|
name: spec: selected:
|
||||||
|
let
|
||||||
|
homePath = hostFile spec "home.nix";
|
||||||
|
homeModules = [
|
||||||
|
(registry.mkModule { class = "home"; })
|
||||||
|
(registry.mkSelectionModule selected)
|
||||||
|
]
|
||||||
|
++ lib.optional (homePath != null) homePath;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ inputs.home-manager.darwinModules.home-manager ];
|
||||||
|
|
||||||
|
home-manager = {
|
||||||
|
useGlobalPkgs = true;
|
||||||
|
useUserPackages = true;
|
||||||
|
extraSpecialArgs = mkSpecialArgs name spec;
|
||||||
|
users.${spec.user}.imports = homeModules;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mkNixos =
|
||||||
|
name: rawSpec:
|
||||||
|
let
|
||||||
|
spec = validateSpec name rawSpec;
|
||||||
|
selected = registry.validateUnitIds (selectedUnits spec);
|
||||||
|
nixosPath = hostFile spec "nixos.nix";
|
||||||
|
modules = [
|
||||||
|
(registry.mkModule { class = "nixos"; })
|
||||||
|
(registry.mkSelectionModule selected)
|
||||||
|
{ networking.hostName = lib.mkDefault name; }
|
||||||
|
]
|
||||||
|
++ lib.optional (spec.homeManager or true) (mkHomeManagerModule name spec selected)
|
||||||
|
++ lib.optional (nixosPath != null) nixosPath;
|
||||||
|
in
|
||||||
|
inputs.nixpkgs.lib.nixosSystem {
|
||||||
|
inherit (spec) system;
|
||||||
|
specialArgs = mkSpecialArgs name spec;
|
||||||
|
inherit modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkDarwin =
|
||||||
|
name: rawSpec:
|
||||||
|
let
|
||||||
|
spec = validateSpec name rawSpec;
|
||||||
|
selected = registry.validateUnitIds (selectedUnits spec);
|
||||||
|
darwinPath = hostFile spec "darwin.nix";
|
||||||
|
modules = [
|
||||||
|
(registry.mkModule { class = "darwin"; })
|
||||||
|
(registry.mkSelectionModule selected)
|
||||||
|
{ networking.hostName = lib.mkDefault name; }
|
||||||
|
]
|
||||||
|
++ lib.optional (spec.homeManager or true) (mkDarwinHomeManagerModule name spec selected)
|
||||||
|
++ lib.optional (darwinPath != null) darwinPath;
|
||||||
|
in
|
||||||
|
ensure (inputs ? nix-darwin) "${name}: the nix-darwin input is required" (
|
||||||
|
inputs.nix-darwin.lib.darwinSystem {
|
||||||
|
inherit (spec) system;
|
||||||
|
specialArgs = mkSpecialArgs name spec;
|
||||||
|
inherit modules;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
mkConfigurations =
|
||||||
|
hostSpecs:
|
||||||
|
let
|
||||||
|
validated = lib.mapAttrs validateSpec hostSpecs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixosConfigurations = lib.mapAttrs mkNixos (
|
||||||
|
lib.filterAttrs (_: spec: isLinux spec.system) validated
|
||||||
|
);
|
||||||
|
darwinConfigurations = lib.mapAttrs mkDarwin (
|
||||||
|
lib.filterAttrs (_: spec: isDarwin spec.system) validated
|
||||||
|
);
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit
|
||||||
|
mkConfigurations
|
||||||
|
mkDarwin
|
||||||
|
mkNixos
|
||||||
|
selectedUnits
|
||||||
|
;
|
||||||
|
}
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
modulesRoot,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
reservedFiles = {
|
||||||
|
common = "common.nix";
|
||||||
|
nixos = "nixos.nix";
|
||||||
|
darwin = "darwin.nix";
|
||||||
|
home = "home.nix";
|
||||||
|
meta = "meta.nix";
|
||||||
|
};
|
||||||
|
|
||||||
|
isFile = kind: kind == "regular" || kind == "symlink";
|
||||||
|
|
||||||
|
ensure =
|
||||||
|
condition: message: value:
|
||||||
|
if condition then value else throw "unit registry: ${message}";
|
||||||
|
|
||||||
|
callWithAvailableArgs =
|
||||||
|
value: availableArgs:
|
||||||
|
if builtins.isFunction value then
|
||||||
|
value (builtins.intersectAttrs (builtins.functionArgs value) availableArgs)
|
||||||
|
else
|
||||||
|
value;
|
||||||
|
|
||||||
|
pathFor =
|
||||||
|
relativePath:
|
||||||
|
if relativePath == [ ] then
|
||||||
|
modulesRoot
|
||||||
|
else
|
||||||
|
modulesRoot + "/${lib.concatStringsSep "/" relativePath}";
|
||||||
|
|
||||||
|
entryIsFile = entries: name: builtins.hasAttr name entries && isFile entries.${name};
|
||||||
|
|
||||||
|
normalizeMeta =
|
||||||
|
unit:
|
||||||
|
let
|
||||||
|
metaPath = unit.fragments.meta;
|
||||||
|
importedValue =
|
||||||
|
if metaPath == null then
|
||||||
|
{ }
|
||||||
|
else
|
||||||
|
callWithAvailableArgs (import metaPath) {
|
||||||
|
inherit inputs lib unit;
|
||||||
|
};
|
||||||
|
imported =
|
||||||
|
ensure (builtins.isAttrs importedValue) "${unit.id}: meta.nix must return an attribute set"
|
||||||
|
importedValue;
|
||||||
|
allowedKeys = [
|
||||||
|
"description"
|
||||||
|
"includes"
|
||||||
|
"imports"
|
||||||
|
];
|
||||||
|
unknownKeys = lib.filter (name: !(builtins.elem name allowedKeys)) (builtins.attrNames imported);
|
||||||
|
description = imported.description or null;
|
||||||
|
includes = imported.includes or [ ];
|
||||||
|
imports = imported.imports or { };
|
||||||
|
allowedImportKeys = [
|
||||||
|
"nixos"
|
||||||
|
"darwin"
|
||||||
|
"home"
|
||||||
|
];
|
||||||
|
unknownImportKeys =
|
||||||
|
if builtins.isAttrs imports then
|
||||||
|
lib.filter (name: !(builtins.elem name allowedImportKeys)) (builtins.attrNames imports)
|
||||||
|
else
|
||||||
|
[ ];
|
||||||
|
normalized = {
|
||||||
|
inherit description includes;
|
||||||
|
imports = {
|
||||||
|
nixos = imports.nixos or [ ];
|
||||||
|
darwin = imports.darwin or [ ];
|
||||||
|
home = imports.home or [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
ensure (unknownKeys == [ ])
|
||||||
|
"${unit.id}: meta.nix has unsupported keys: ${lib.concatStringsSep ", " unknownKeys}"
|
||||||
|
(
|
||||||
|
ensure (description == null || builtins.isString description)
|
||||||
|
"${unit.id}: meta.description must be a string"
|
||||||
|
(
|
||||||
|
ensure (builtins.isList includes && lib.all builtins.isString includes)
|
||||||
|
"${unit.id}: meta.includes must be a list of fully qualified unit IDs"
|
||||||
|
(
|
||||||
|
ensure (lib.unique includes == includes) "${unit.id}: meta.includes contains duplicate unit IDs" (
|
||||||
|
ensure (builtins.isAttrs imports) "${unit.id}: meta.imports must be an attribute set" (
|
||||||
|
ensure (unknownImportKeys == [ ])
|
||||||
|
"${unit.id}: meta.imports has unsupported classes: ${lib.concatStringsSep ", " unknownImportKeys}"
|
||||||
|
(
|
||||||
|
ensure (lib.all builtins.isList [
|
||||||
|
normalized.imports.nixos
|
||||||
|
normalized.imports.darwin
|
||||||
|
normalized.imports.home
|
||||||
|
]) "${unit.id}: every meta.imports.<class> value must be a list" normalized
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
makeUnit =
|
||||||
|
relativePath: entries:
|
||||||
|
let
|
||||||
|
directory = pathFor relativePath;
|
||||||
|
id = lib.concatStringsSep "." relativePath;
|
||||||
|
fragments = lib.mapAttrs (
|
||||||
|
_class: fileName: if entryIsFile entries fileName then directory + "/${fileName}" else null
|
||||||
|
) reservedFiles;
|
||||||
|
baseUnit = {
|
||||||
|
inherit
|
||||||
|
id
|
||||||
|
directory
|
||||||
|
fragments
|
||||||
|
relativePath
|
||||||
|
;
|
||||||
|
optionPath = [ "my" ] ++ relativePath ++ [ "enable" ];
|
||||||
|
kind = builtins.head relativePath;
|
||||||
|
name = lib.last relativePath;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
lib.optionalAttrs (builtins.length relativePath > 2 && builtins.head relativePath == "profiles")
|
||||||
|
{
|
||||||
|
group = builtins.elemAt relativePath 1;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
ensure (relativePath != [ ]) "the modules root cannot itself be a unit" (
|
||||||
|
ensure (lib.all (component: component != "" && !(lib.hasInfix "." component)) relativePath)
|
||||||
|
"${id}: path components must be non-empty and must not contain dots"
|
||||||
|
(baseUnit // { meta = normalizeMeta baseUnit; })
|
||||||
|
);
|
||||||
|
|
||||||
|
walk =
|
||||||
|
relativePath:
|
||||||
|
let
|
||||||
|
directory = pathFor relativePath;
|
||||||
|
entries = builtins.readDir directory;
|
||||||
|
hasReservedFile = lib.any (fileName: entryIsFile entries fileName) (
|
||||||
|
builtins.attrValues reservedFiles
|
||||||
|
);
|
||||||
|
childDirectories = lib.filter (name: entries.${name} == "directory") (builtins.attrNames entries);
|
||||||
|
current = lib.optional hasReservedFile (makeUnit relativePath entries);
|
||||||
|
children = lib.concatMap (name: walk (relativePath ++ [ name ])) childDirectories;
|
||||||
|
in
|
||||||
|
current ++ children;
|
||||||
|
|
||||||
|
discoveredUnits =
|
||||||
|
ensure (builtins.pathExists modulesRoot) "modules root does not exist: ${toString modulesRoot}"
|
||||||
|
(walk [ ]);
|
||||||
|
unitsById = builtins.listToAttrs (map (unit: lib.nameValuePair unit.id unit) discoveredUnits);
|
||||||
|
|
||||||
|
dependencyValidation = lib.foldl' (
|
||||||
|
valid: unit:
|
||||||
|
lib.foldl' (
|
||||||
|
inner: includedId:
|
||||||
|
if builtins.hasAttr includedId unitsById then
|
||||||
|
inner
|
||||||
|
else
|
||||||
|
throw "unit registry: ${unit.id} includes missing unit '${includedId}'"
|
||||||
|
) valid unit.meta.includes
|
||||||
|
) true discoveredUnits;
|
||||||
|
|
||||||
|
units = builtins.seq dependencyValidation unitsById;
|
||||||
|
unitIds = builtins.attrNames units;
|
||||||
|
|
||||||
|
getUnit =
|
||||||
|
id: if builtins.hasAttr id units then units.${id} else throw "unit registry: unknown unit '${id}'";
|
||||||
|
|
||||||
|
validateUnitIds =
|
||||||
|
ids:
|
||||||
|
ensure (
|
||||||
|
builtins.isList ids && lib.all builtins.isString ids
|
||||||
|
) "selected units must be a list of strings" (map (id: builtins.seq (getUnit id) id) ids);
|
||||||
|
|
||||||
|
optionDefinitions = lib.foldl' lib.recursiveUpdate { } (
|
||||||
|
map (
|
||||||
|
unit:
|
||||||
|
lib.setAttrByPath unit.optionPath (
|
||||||
|
lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description =
|
||||||
|
if unit.meta.description == null then
|
||||||
|
"Whether to enable the ${unit.id} unit."
|
||||||
|
else
|
||||||
|
"Whether to enable ${unit.meta.description}.";
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) discoveredUnits
|
||||||
|
);
|
||||||
|
|
||||||
|
enabled = config: unit: lib.getAttrFromPath unit.optionPath config;
|
||||||
|
|
||||||
|
enableUnit = id: lib.setAttrByPath (getUnit id).optionPath true;
|
||||||
|
|
||||||
|
includeConfig =
|
||||||
|
config: unit: lib.mkIf (enabled config unit) (lib.mkMerge (map enableUnit unit.meta.includes));
|
||||||
|
|
||||||
|
fragmentClasses = {
|
||||||
|
nixos = [
|
||||||
|
"common"
|
||||||
|
"nixos"
|
||||||
|
];
|
||||||
|
darwin = [
|
||||||
|
"common"
|
||||||
|
"darwin"
|
||||||
|
];
|
||||||
|
home = [ "home" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
applyFragment =
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
fragmentPath,
|
||||||
|
options,
|
||||||
|
specialArgs,
|
||||||
|
unit,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
fragment = import fragmentPath;
|
||||||
|
directArgs = specialArgs // {
|
||||||
|
inherit
|
||||||
|
config
|
||||||
|
lib
|
||||||
|
options
|
||||||
|
specialArgs
|
||||||
|
unit
|
||||||
|
;
|
||||||
|
};
|
||||||
|
fragmentArgSpec = builtins.functionArgs fragment;
|
||||||
|
fragmentArgs = builtins.listToAttrs (
|
||||||
|
lib.concatMap (
|
||||||
|
name:
|
||||||
|
if builtins.hasAttr name directArgs then
|
||||||
|
[ (lib.nameValuePair name directArgs.${name}) ]
|
||||||
|
else if fragmentArgSpec.${name} then
|
||||||
|
[ ]
|
||||||
|
else
|
||||||
|
[ (lib.nameValuePair name config._module.args.${name}) ]
|
||||||
|
) (builtins.attrNames fragmentArgSpec)
|
||||||
|
);
|
||||||
|
resultValue = if builtins.isFunction fragment then fragment fragmentArgs else fragment;
|
||||||
|
result =
|
||||||
|
ensure (builtins.isAttrs resultValue)
|
||||||
|
"${unit.id}: ${builtins.baseNameOf fragmentPath} must return an attribute set"
|
||||||
|
resultValue;
|
||||||
|
forbiddenKeys = lib.filter (name: builtins.hasAttr name result) [
|
||||||
|
"imports"
|
||||||
|
"options"
|
||||||
|
"config"
|
||||||
|
];
|
||||||
|
in
|
||||||
|
ensure (forbiddenKeys == [ ])
|
||||||
|
"${unit.id}: ${builtins.baseNameOf fragmentPath} is a configuration fragment and cannot define top-level ${lib.concatStringsSep ", " forbiddenKeys}"
|
||||||
|
result;
|
||||||
|
|
||||||
|
externalImports = class: lib.concatMap (unit: unit.meta.imports.${class}) discoveredUnits;
|
||||||
|
|
||||||
|
mkModule =
|
||||||
|
{ class }:
|
||||||
|
ensure (builtins.hasAttr class fragmentClasses) "unsupported module class '${class}'" (
|
||||||
|
builtins.seq dependencyValidation (
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
options,
|
||||||
|
specialArgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
fragmentConfigs = lib.concatMap (
|
||||||
|
unit:
|
||||||
|
lib.filter (value: value != null) (
|
||||||
|
map (
|
||||||
|
fragmentClass:
|
||||||
|
let
|
||||||
|
fragmentPath = unit.fragments.${fragmentClass};
|
||||||
|
in
|
||||||
|
if fragmentPath == null then
|
||||||
|
null
|
||||||
|
else
|
||||||
|
lib.mkIf (enabled config unit) (applyFragment {
|
||||||
|
inherit
|
||||||
|
config
|
||||||
|
fragmentPath
|
||||||
|
options
|
||||||
|
specialArgs
|
||||||
|
unit
|
||||||
|
;
|
||||||
|
})
|
||||||
|
) fragmentClasses.${class}
|
||||||
|
)
|
||||||
|
) discoveredUnits;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = externalImports class;
|
||||||
|
options = optionDefinitions;
|
||||||
|
config = lib.mkMerge ((map (includeConfig config) discoveredUnits) ++ fragmentConfigs);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
mkSelectionModule =
|
||||||
|
selectedIds:
|
||||||
|
let
|
||||||
|
checkedIds = validateUnitIds (lib.unique selectedIds);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkMerge (map enableUnit checkedIds);
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit
|
||||||
|
getUnit
|
||||||
|
mkModule
|
||||||
|
mkSelectionModule
|
||||||
|
unitIds
|
||||||
|
units
|
||||||
|
validateUnitIds
|
||||||
|
;
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# Bashtop theme with Nord palette (https://www.nordtheme.com)
|
||||||
|
# by Justin Zobel <[email protected]>
|
||||||
|
|
||||||
|
# Colors should be in 6 or 2 character hexadecimal or single spaced rgb decimal: "#RRGGBB", "#BW" or "0-255 0-255 0-255"
|
||||||
|
# example for white: "#ffffff", "#ff" or "255 255 255".
|
||||||
|
|
||||||
|
# All graphs and meters can be gradients
|
||||||
|
# For single color graphs leave "mid" and "end" variable empty.
|
||||||
|
# Use "start" and "end" variables for two color gradient
|
||||||
|
# Use "start", "mid" and "end" for three color gradient
|
||||||
|
|
||||||
|
# Main background, empty for terminal default, need to be empty if you want transparent background
|
||||||
|
theme[main_bg]="#282A36"
|
||||||
|
|
||||||
|
# Main text color
|
||||||
|
theme[main_fg]="#BD93F9"
|
||||||
|
|
||||||
|
# Title color for boxes
|
||||||
|
theme[title]="#f8f8f2"
|
||||||
|
|
||||||
|
# Highlight color for keyboard shortcuts
|
||||||
|
theme[hi_fg]="#ff79c6"
|
||||||
|
|
||||||
|
# Background color of selected item in processes box
|
||||||
|
theme[selected_bg]="#44475A"
|
||||||
|
|
||||||
|
# Foreground color of selected item in processes box
|
||||||
|
theme[selected_fg]="#ECEFF4"
|
||||||
|
|
||||||
|
# Color of inactive/disabled text
|
||||||
|
theme[inactive_fg]="#6272a4"
|
||||||
|
|
||||||
|
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
|
||||||
|
theme[proc_misc]="#BD93F9"
|
||||||
|
|
||||||
|
# Cpu box outline color
|
||||||
|
theme[cpu_box]="#ff79c6"
|
||||||
|
|
||||||
|
# Memory/disks box outline color
|
||||||
|
theme[mem_box]="#ff79c6"
|
||||||
|
|
||||||
|
# Net up/down box outline color
|
||||||
|
theme[net_box]="#ff79c6"
|
||||||
|
|
||||||
|
# Processes box outline color
|
||||||
|
theme[proc_box]="#ff79c6"
|
||||||
|
|
||||||
|
# Box divider line and small boxes line color
|
||||||
|
theme[div_line]="#ff79c6"
|
||||||
|
|
||||||
|
# Temperature graph colors
|
||||||
|
theme[temp_start]="#bd93f9"
|
||||||
|
theme[temp_mid]="#bd93f9"
|
||||||
|
theme[temp_end]="#bd93f9"
|
||||||
|
|
||||||
|
# CPU graph colors
|
||||||
|
theme[cpu_start]="#bd93f9"
|
||||||
|
theme[cpu_mid]="#bd93f9"
|
||||||
|
theme[cpu_end]="#bd93f9"
|
||||||
|
|
||||||
|
# Mem/Disk free meter
|
||||||
|
theme[free_start]="#bd93f9"
|
||||||
|
theme[free_mid]="#bd93f9"
|
||||||
|
theme[free_end]="#bd93f9"
|
||||||
|
|
||||||
|
# Mem/Disk cached meter
|
||||||
|
theme[cached_start]="#bd93f9"
|
||||||
|
theme[cached_mid]="#bd93f9"
|
||||||
|
theme[cached_end]="#bd93f9"
|
||||||
|
|
||||||
|
# Mem/Disk available meter
|
||||||
|
theme[available_start]="#bd93f9"
|
||||||
|
theme[available_mid]="#bd93f9"
|
||||||
|
theme[available_end]="#bd93f9"
|
||||||
|
|
||||||
|
# Mem/Disk used meter
|
||||||
|
theme[used_start]="#bd93f9"
|
||||||
|
theme[used_mid]="#bd93f9"
|
||||||
|
theme[used_end]="#bd93f9"
|
||||||
|
|
||||||
|
# Download graph colors
|
||||||
|
theme[download_start]="#bd93f9"
|
||||||
|
theme[download_mid]="#bd93f9"
|
||||||
|
theme[download_end]="#bd93f9"
|
||||||
|
|
||||||
|
# Upload graph colors
|
||||||
|
theme[upload_start]="#bd93f9"
|
||||||
|
theme[upload_mid]="#bd93f9"
|
||||||
|
theme[upload_end]="#bd93f9"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
programs.btop = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
settings.color_theme = "dracula";
|
||||||
|
themes.dracula = builtins.readFile ./dracula.theme;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
home.packages = [
|
||||||
|
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.claude-code
|
||||||
|
];
|
||||||
|
|
||||||
|
home.file.".claude/settings.json".text = builtins.toJSON {
|
||||||
|
statusLine = {
|
||||||
|
type = "command";
|
||||||
|
command = "bun x ccusage statusline --no-offline";
|
||||||
|
padding = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
[Hotkey]
|
||||||
|
# トリガーキーを押すたびに切り替える
|
||||||
|
EnumerateWithTriggerKeys=False
|
||||||
|
# 一時的に第1入力メソッドに切り替える
|
||||||
|
AltTriggerKeys=
|
||||||
|
# 切り替え時は第1入力メソッドをスキップする
|
||||||
|
EnumerateSkipFirst=False
|
||||||
|
# Time limit in milliseconds for triggering modifier key shortcuts
|
||||||
|
ModifierOnlyKeyTimeout=250
|
||||||
|
|
||||||
|
[Hotkey/TriggerKeys]
|
||||||
|
1=Zenkaku_Hankaku
|
||||||
|
|
||||||
|
[Hotkey/ActivateKeys]
|
||||||
|
0=Henkan
|
||||||
|
|
||||||
|
[Hotkey/DeactivateKeys]
|
||||||
|
0=Muhenkan
|
||||||
|
|
||||||
|
[Hotkey/PrevPage]
|
||||||
|
0=Up
|
||||||
|
|
||||||
|
[Hotkey/NextPage]
|
||||||
|
0=Down
|
||||||
|
|
||||||
|
[Hotkey/PrevCandidate]
|
||||||
|
0=Shift+Tab
|
||||||
|
|
||||||
|
[Hotkey/NextCandidate]
|
||||||
|
0=Tab
|
||||||
|
|
||||||
|
[Hotkey/TogglePreedit]
|
||||||
|
0=Control+Alt+P
|
||||||
|
|
||||||
|
[Behavior]
|
||||||
|
# デフォルトで有効にする
|
||||||
|
ActiveByDefault=False
|
||||||
|
# フォーカス時に状態をリセット
|
||||||
|
resetStateWhenFocusIn=No
|
||||||
|
# 入力状態を共有する
|
||||||
|
ShareInputState=No
|
||||||
|
# アプリケーションにプリエディットを表示する
|
||||||
|
PreeditEnabledByDefault=True
|
||||||
|
# 入力メソッドを切り替える際に入力メソッドの情報を表示する
|
||||||
|
ShowInputMethodInformation=True
|
||||||
|
# フォーカスを変更する際に入力メソッドの情報を表示する
|
||||||
|
showInputMethodInformationWhenFocusIn=False
|
||||||
|
# 入力メソッドの情報をコンパクトに表示する
|
||||||
|
CompactInputMethodInformation=True
|
||||||
|
# 第1入力メソッドの情報を表示する
|
||||||
|
ShowFirstInputMethodInformation=True
|
||||||
|
# デフォルトのページサイズ
|
||||||
|
DefaultPageSize=5
|
||||||
|
# XKB オプションより優先する
|
||||||
|
OverrideXkbOption=False
|
||||||
|
# カスタム XKB オプション
|
||||||
|
CustomXkbOption=
|
||||||
|
# Force Enabled Addons
|
||||||
|
EnabledAddons=
|
||||||
|
# Force Disabled Addons
|
||||||
|
DisabledAddons=
|
||||||
|
# Preload input method to be used by default
|
||||||
|
PreloadInputMethod=True
|
||||||
|
# パスワード欄に入力メソッドを許可する
|
||||||
|
AllowInputMethodForPassword=False
|
||||||
|
# パスワード入力時にプリエディットテキストを表示する
|
||||||
|
ShowPreeditForPassword=False
|
||||||
|
# ユーザーデータを保存する間隔(分)
|
||||||
|
AutoSavePeriod=30
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
description = "base system configuration";
|
||||||
|
|
||||||
|
includes = [
|
||||||
|
"systems.locale"
|
||||||
|
"systems.networking"
|
||||||
|
"systems.nix"
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
description = "minimal command-line environment";
|
||||||
|
|
||||||
|
includes = [
|
||||||
|
"applications.btop"
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
description = "laptop platform configuration";
|
||||||
|
|
||||||
|
includes = [
|
||||||
|
"systems.boot.uefi"
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
boot.loader = {
|
||||||
|
systemd-boot.enable = lib.mkDefault true;
|
||||||
|
systemd-boot.configurationLimit = lib.mkDefault 8;
|
||||||
|
efi.canTouchEfiVariables = lib.mkDefault true;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
time.timeZone = "Asia/Tokyo";
|
||||||
|
|
||||||
|
i18n = {
|
||||||
|
defaultLocale = "ja_JP.UTF-8";
|
||||||
|
extraLocaleSettings = {
|
||||||
|
LC_ADDRESS = "ja_JP.UTF-8";
|
||||||
|
LC_IDENTIFICATION = "ja_JP.UTF-8";
|
||||||
|
LC_MEASUREMENT = "ja_JP.UTF-8";
|
||||||
|
LC_MONETARY = "ja_JP.UTF-8";
|
||||||
|
LC_NAME = "ja_JP.UTF-8";
|
||||||
|
LC_NUMERIC = "ja_JP.UTF-8";
|
||||||
|
LC_PAPER = "ja_JP.UTF-8";
|
||||||
|
LC_TELEPHONE = "ja_JP.UTF-8";
|
||||||
|
LC_TIME = "ja_JP.UTF-8";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
console.keyMap = "jp106";
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
networking.networkmanager.enable = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
nix.settings.experimental-features = [
|
||||||
|
"nix-command"
|
||||||
|
"flakes"
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{ primaryUser, ... }:
|
||||||
|
{
|
||||||
|
home = {
|
||||||
|
username = primaryUser;
|
||||||
|
homeDirectory = "/home/${primaryUser}";
|
||||||
|
stateVersion = "26.05";
|
||||||
|
};
|
||||||
|
|
||||||
|
programs.home-manager.enable = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{ primaryUser, ... }:
|
||||||
|
{
|
||||||
|
users.users.${primaryUser} = {
|
||||||
|
isNormalUser = true;
|
||||||
|
description = "moons";
|
||||||
|
extraGroups = [
|
||||||
|
"networkmanager"
|
||||||
|
"wheel"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
nix.settings = {
|
||||||
|
allowed-users = [ primaryUser ];
|
||||||
|
trusted-users = [
|
||||||
|
"root"
|
||||||
|
primaryUser
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
includes = [ "applications.missing" ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
imports = [ ];
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
system.stateVersion = 6;
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
fileSystems."/" = {
|
||||||
|
device = "/dev/null";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
boot.loader.grub.enable = false;
|
||||||
|
system.stateVersion = "26.05";
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
marker ? "host",
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
test.systemValues = [ "common:${marker}" ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
test.systemValues = [ "darwin" ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
test.homeValues = [ "home" ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
description = "alpha test application";
|
||||||
|
|
||||||
|
includes = [ "services.nested" ];
|
||||||
|
|
||||||
|
imports.nixos = [
|
||||||
|
{
|
||||||
|
options.test = {
|
||||||
|
external = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "external-default";
|
||||||
|
};
|
||||||
|
systemValues = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
imports.darwin = [
|
||||||
|
{
|
||||||
|
options.test.systemValues = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
imports.home = [
|
||||||
|
{
|
||||||
|
options.test.homeValues = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
test.systemValues = [ "nixos" ];
|
||||||
|
test.external = "set-by-nixos-fragment";
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
test.homeValues = [ "beta-home" ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
thisFileMustNotBeDiscovered = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
includes = [
|
||||||
|
"applications.alpha"
|
||||||
|
"applications.beta"
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
imports.nixos = [
|
||||||
|
{
|
||||||
|
options.test.nested = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
test.nested = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
home = {
|
||||||
|
username = "test";
|
||||||
|
homeDirectory = if pkgs.stdenv.hostPlatform.isDarwin then "/Users/test" else "/home/test";
|
||||||
|
stateVersion = "26.05";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
registry = import ../libs/registry.nix {
|
||||||
|
inherit inputs lib;
|
||||||
|
modulesRoot = ./fixtures/registry/modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
hostLib = import ../libs/hosts.nix {
|
||||||
|
inherit inputs lib registry;
|
||||||
|
};
|
||||||
|
|
||||||
|
baseModule = {
|
||||||
|
options = {
|
||||||
|
home = {
|
||||||
|
username = lib.mkOption { type = lib.types.str; };
|
||||||
|
homeDirectory = lib.mkOption { type = lib.types.str; };
|
||||||
|
stateVersion = lib.mkOption { type = lib.types.str; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
eval =
|
||||||
|
class: selected:
|
||||||
|
lib.evalModules {
|
||||||
|
specialArgs = {
|
||||||
|
inherit pkgs;
|
||||||
|
marker = "special-arg";
|
||||||
|
};
|
||||||
|
modules = [
|
||||||
|
baseModule
|
||||||
|
(registry.mkModule { inherit class; })
|
||||||
|
(registry.mkSelectionModule selected)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
nixos = eval "nixos" [ "profiles.interface.test" ];
|
||||||
|
darwin = eval "darwin" [ "applications.alpha" ];
|
||||||
|
home = eval "home" [ "profiles.interface.test" ];
|
||||||
|
|
||||||
|
nixosHost = hostLib.mkNixos "registry-test" {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
user = "test";
|
||||||
|
path = ./fixtures/registry/hosts;
|
||||||
|
profiles = [ "interface.test" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
darwinHost = hostLib.mkDarwin "registry-test-darwin" {
|
||||||
|
system = "aarch64-darwin";
|
||||||
|
user = "test";
|
||||||
|
path = ./fixtures/registry/hosts;
|
||||||
|
applications = [ "alpha" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
missingUnit = builtins.tryEval (
|
||||||
|
builtins.deepSeq (registry.validateUnitIds [ "applications.missing" ]) true
|
||||||
|
);
|
||||||
|
|
||||||
|
invalidDependencyRegistry = import ../libs/registry.nix {
|
||||||
|
inherit inputs lib;
|
||||||
|
modulesRoot = ./fixtures/registry-invalid-dependency/modules;
|
||||||
|
};
|
||||||
|
missingDependency = builtins.tryEval (builtins.deepSeq invalidDependencyRegistry.unitIds true);
|
||||||
|
|
||||||
|
invalidFragmentRegistry = import ../libs/registry.nix {
|
||||||
|
inherit inputs lib;
|
||||||
|
modulesRoot = ./fixtures/registry-invalid-fragment/modules;
|
||||||
|
};
|
||||||
|
invalidFragmentEvaluation = lib.evalModules {
|
||||||
|
modules = [
|
||||||
|
(invalidFragmentRegistry.mkModule { class = "home"; })
|
||||||
|
(invalidFragmentRegistry.mkSelectionModule [ "applications.broken" ])
|
||||||
|
];
|
||||||
|
};
|
||||||
|
invalidFragment = builtins.tryEval (builtins.deepSeq invalidFragmentEvaluation.config true);
|
||||||
|
|
||||||
|
assertions =
|
||||||
|
assert
|
||||||
|
registry.unitIds == [
|
||||||
|
"applications.alpha"
|
||||||
|
"applications.beta"
|
||||||
|
"profiles.interface.test"
|
||||||
|
"services.nested"
|
||||||
|
"users.test"
|
||||||
|
];
|
||||||
|
assert !(builtins.elem "namespace" registry.unitIds);
|
||||||
|
assert nixos.config.my.profiles.interface.test.enable;
|
||||||
|
assert nixos.config.my.applications.alpha.enable;
|
||||||
|
assert nixos.config.my.applications.beta.enable;
|
||||||
|
assert nixos.config.my.services.nested.enable;
|
||||||
|
assert nixos.config.test.nested;
|
||||||
|
assert
|
||||||
|
nixos.config.test.systemValues == [
|
||||||
|
"common:special-arg"
|
||||||
|
"nixos"
|
||||||
|
];
|
||||||
|
assert nixos.config.test.external == "set-by-nixos-fragment";
|
||||||
|
assert
|
||||||
|
darwin.config.test.systemValues == [
|
||||||
|
"common:special-arg"
|
||||||
|
"darwin"
|
||||||
|
];
|
||||||
|
assert
|
||||||
|
home.config.test.homeValues == [
|
||||||
|
"home"
|
||||||
|
"beta-home"
|
||||||
|
];
|
||||||
|
assert nixosHost.config.my.profiles.interface.test.enable;
|
||||||
|
assert nixosHost.config.home-manager.users.test.my.applications.alpha.enable;
|
||||||
|
assert
|
||||||
|
nixosHost.config.home-manager.users.test.test.homeValues == [
|
||||||
|
"home"
|
||||||
|
"beta-home"
|
||||||
|
];
|
||||||
|
assert darwinHost.config.my.applications.alpha.enable;
|
||||||
|
assert darwinHost.config.home-manager.users.test.my.applications.alpha.enable;
|
||||||
|
assert darwinHost.config.home-manager.users.test.test.homeValues == [ "home" ];
|
||||||
|
assert
|
||||||
|
darwinHost.config.test.systemValues == [
|
||||||
|
"common:host"
|
||||||
|
"darwin"
|
||||||
|
];
|
||||||
|
assert !missingUnit.success;
|
||||||
|
assert !missingDependency.success;
|
||||||
|
assert !invalidFragment.success;
|
||||||
|
true;
|
||||||
|
in
|
||||||
|
assert assertions;
|
||||||
|
pkgs.runCommand "unit-registry-evaluation-tests" { } ''
|
||||||
|
touch "$out"
|
||||||
|
''
|
||||||
Reference in New Issue
Block a user