feat(dev-env): aiolabs dev environment — options, lib, config, presets

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-06-28 06:48:37 +02:00
commit 7960f82494
6 changed files with 1689 additions and 0 deletions

369
modules/dev-env/options.nix Normal file
View file

@ -0,0 +1,369 @@
{ config, lib, ... }:
let
inherit (lib) mkEnableOption mkOption types;
# One entry in dev-env.projects.
#
# A project is a git repo with one or more worktrees (or a single clone
# if `isClone = true`). Worktrees are derived from a bare repo at
# `${dev-env.root}/repos/${name}.git` by default; that path is managed
# by the bootstrap script.
projectType = types.submodule (
{ name, ... }:
{
options = {
forgejoRepo = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
<org>/<repo> on the configured forgejo host. Used to construct
the `origin` remote. May be null for projects whose origin
lives outside forgejo in that case set `originUrl`.
'';
example = "aiolabs/lnbits";
};
originUrl = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Override the origin remote URL. By default, origin is derived
from the forgejo host + forgejoRepo. Set this to clone from
an arbitrary URL (e.g. a GitHub repo that has no forgejo mirror).
'';
example = "https://github.com/nostr-protocol/nips";
};
upstream = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
HTTPS URL of the upstream OSS repo. Set to null for projects
with no upstream (divergent branches, original work).
When set, the `upstream` remote is added to the bare repo and
the `prb`/`prc`/rebase helpers know how to sync from it.
'';
example = "https://github.com/lnbits/lnbits";
};
githubFork = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Full SSH URL of the personal github fork used for upstream PRs
(the `github-fork` remote). If null, derived from the global
`dev-env.github.forkUser` + the basename of `forgejoRepo`.
'';
example = "git@github.com:your-github-username/lnbits.git";
};
category = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Sub-directory under `dev-env.root` that groups related
projects. e.g. "bitspire" groups the bitSpire frontend +
atm-tui. null puts the project at the top level.
'';
example = "bitspire";
};
worktreeRoot = mkOption {
type = types.str;
default = if name == null then "" else name;
defaultText = "project name";
description = ''
Directory name (relative to `category` or root) under which
worktrees live. Defaults to the project name.
'';
};
worktrees = mkOption {
type = types.attrsOf (
types.submodule {
options = {
branch = mkOption {
type = types.str;
description = "Branch to check out in this worktree.";
};
path = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Override the worktree directory name. Defaults to the
attribute key.
'';
};
remote = mkOption {
type = types.str;
default = "origin";
description = ''
Which remote the branch tracks. Usually origin (forgejo)
but could be "upstream" for read-only tracking worktrees.
'';
};
};
}
);
default = { };
description = ''
Worktrees to materialize for this project, keyed by worktree
name. Each worktree becomes a directory at
`''${dev-env.root}/''${category}/''${worktreeRoot}/<key>` (or under
root if category is null).
'';
example = lib.literalExpression ''
{
dev = { branch = "dev"; };
main = { branch = "main"; };
}
'';
};
isClone = mkEnableOption ''
Treat this project as a regular clone rather than a bare-repo
worktree set. Used for projects that won't have multiple
simultaneous branches checked out (e.g. atm-tui)
'';
deployFlakeInput = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Name of the flake input in the deploy flake that this project
corresponds to. Used by `dev-deploy --local` to know which
--override-input to pass.
'';
example = "lnbits";
};
};
}
);
# One entry in dev-env.tmux.sessions.
tmuxSessionType = types.submodule {
options = {
cwd = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Session-default cwd, relative to `dev-env.root`. Windows can
override with their own cwd.
'';
};
windows = mkOption {
type = types.listOf (
types.submodule {
options = {
name = mkOption { type = types.str; };
cwd = mkOption {
type = types.nullOr types.str;
default = null;
description = "Window cwd, relative to dev-env.root.";
};
cmd = mkOption {
type = types.nullOr types.str;
default = null;
description = "Command to run in the window on creation.";
};
};
}
);
default = [ ];
};
};
};
in
{
options.dev-env = {
enable = mkEnableOption "dev-env multi-project development environment";
user = mkOption {
type = types.str;
default = "user";
description = ''
Owner of the dev tree. Drives `root`'s default and the per-user
git-hooks / state-dir paths. Set this to your login user when
importing dev-env standalone; on omni the host wires it to
`settings.user`. Kept self-contained (a plain default, no
`config.omni.*` read) so this pack is importable without omni.
'';
};
root = mkOption {
type = types.str;
default = "/home/${config.dev-env.user}/dev";
defaultText = "/home/\${config.dev-env.user}/dev";
description = ''
Root directory for the dev environment. Bare repos live under
`''${root}/repos/`, worktrees under `''${root}/<category>/<project>/`.
'';
};
forgejo = {
host = mkOption {
type = types.str;
default = "git.atitlan.io";
description = "Forgejo server hostname.";
};
sshUser = mkOption {
type = types.str;
default = "forgejo";
description = ''
SSH user for git+ssh access to forgejo. Some installations use
"git", others use "forgejo". Defaults to "forgejo" because the
reference deployment uses that.
'';
};
org = mkOption {
type = types.str;
description = ''
Default forgejo org/user under which your forks live. Used as
the namespace when a project's `forgejoRepo` is a bare name.
'';
example = "aiolabs";
};
};
github = {
forkUser = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Personal github username used for upstream PR forks (the
`github-fork` remote). When set, projects that declare an
upstream automatically get a github-fork remote derived from
`git@github.com:<forkUser>/<basename>.git`.
'';
example = "your-github-username";
};
};
projects = mkOption {
type = types.attrsOf projectType;
default = { };
description = ''
Hand-authored project list. This is merged with projects derived
from the deploy flake (when `deploy.deriveProjectsFromInputs` is
true). Hand entries win on key collision.
'';
};
deploy = {
flakeInput = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Name of the omni flake input that points at the unified
deploy flake. When set, the bootstrap script clones it to
`''${root}/deploy/<flakeInput>` as a working copy, and
`dev-deploy` uses it as the --flake source by default.
'';
example = "deploy-flake";
};
targets = mkOption {
type = types.attrsOf types.str;
default = { };
description = ''
Map from hostname to SSH target used by `dev-deploy`. Mirrors
the TARGETS associative array in optimize-deploys/unified/
deploy.sh.
'';
example = lib.literalExpression ''
{
host1 = "root@host1.example";
host5 = "root@host5.example";
host7 = "root@host7.example";
}
'';
};
deriveProjectsFromInputs = mkOption {
type = types.bool;
default = true;
description = ''
When true and `flakeInput` is set, derive a default project
entry for every flake input of the deploy flake whose URL is
under the forgejo host. Hand-authored `projects` win on key
collision.
'';
};
};
regtest = {
enable = mkEnableOption ''
Unified Bitcoin/Lightning regtest docker environment. Enables
virtualisation.docker (via mkDefault)
'';
repoUrl = mkOption {
type = types.str;
default = "forgejo@git.atitlan.io:aiolabs/lnbits-regtest.git";
description = ''
Git URL of the regtest docker-compose repo. Cloned to
`''${root}/local/docker/regtest` by the bootstrap script.
'';
};
};
tmux = {
enable = mkEnableOption "declarative tmux session launcher (dev-tm)";
sessions = mkOption {
type = types.attrsOf tmuxSessionType;
default = { };
description = ''
Named tmux session layouts. The `dev-tm <name>` script reads
these from /etc/dev-env/tmux-sessions.json at runtime and
recreates the session.
'';
};
};
gitHooks = {
enable =
mkEnableOption ''
Shared pre-commit hook via core.hooksPath. Installs a single
hook that scans for secrets (prv keys, hardcoded passwords,
unencrypted sops files) in all repos under dev-env.root
''
// {
default = true;
};
path = mkOption {
type = types.str;
default = "${config.dev-env.root or "/tmp"}/.git-hooks";
defaultText = ''"''${root}/.git-hooks"'';
description = "Directory used for git core.hooksPath.";
};
};
writeDirenvHints = mkOption {
type = types.bool;
default = true;
description = ''
When true, the bootstrap script writes a default `.envrc`
containing `use flake` into each worktree that has a flake.nix
but no existing .envrc. Never clobbers existing files.
'';
};
legacyConfigFile = mkOption {
type = types.nullOr types.str;
default = "${config.dev-env.root or "/tmp"}/.devenv.conf";
defaultText = ''"''${root}/.devenv.conf"'';
description = ''
Path at which to also write a legacy `.devenv.conf` for any
loose bash scripts still reading it. The canonical config is at
/etc/dev-env/config.sh; this is a transitional aid. Set to null
to disable once migration is complete.
'';
};
};
}