feat: add more options and generate docs for all options

This commit is contained in:
technofab 2025-08-09 20:49:11 +02:00
parent ba08a453ea
commit 8dccb9a9a8
Signed by: technofab
SSH key fingerprint: SHA256:bV4h88OqS/AxjbPn66uUdvK9JsgIW4tv3vwJQ8tpMqQ
5 changed files with 213 additions and 166 deletions

3
docs/options.md Normal file
View file

@ -0,0 +1,3 @@
# Options
{% include 'options.md' %}

View file

@ -84,132 +84,6 @@ mkNixibleCli config
Creates a CLI executable for your Nixible configuration. Creates a CLI executable for your Nixible configuration.
Basically `(mkNixible config).config.cli`. Basically `(mkNixible config).config.cli`.
## Configuration Options ______________________________________________________________________
### `ansiblePackage` See [options](./options.md) for more.
**Type:** `package`
**Default:** Custom ansible-core package
The Ansible package to use. The default package is optimized for size, by not
including the gazillion collections that `pkgs.ansible` and `pkgs.ansible-core` include.
```nix
ansiblePackage = pkgs.ansible;
```
### `collections`
**Type:** `attrsOf collectionType`
**Default:** `{}`
Ansible collections to fetch from Galaxy.
```nix
collections = {
"community-general" = {
version = "8.0.0";
hash = "sha256-...";
};
};
```
### `dependencies`
**Type:** `listOf package`
**Default:** `[]`
Additional packages available at runtime.
```nix
dependencies = [pkgs.git pkgs.rsync];
```
### `inventory`
**Type:** `attrs`
**Default:** `{}`
Ansible inventory as Nix data structure, converted to JSON.
```nix
inventory = {
webservers = {
hosts = {
web1 = { ansible_host = "192.168.1.10"; };
};
vars = {
http_port = 80;
};
};
};
```
### `playbook`
**Type:** `listOf playbookType`
List of plays that make up the playbook.
```nix
playbook = [
{
name = "Configure servers";
hosts = "webservers";
become = true;
tasks = [
{
name = "Install nginx";
package = {
name = "nginx";
state = "present";
};
}
];
}
];
```
## Collection Type
### `version`
**Type:** `str`
Version of the collection from Ansible Galaxy.
### `hash`
**Type:** `str`
SHA256 hash of the collection tarball for verification.
## Playbook Type
### `name`
**Type:** `str`
Name of the play.
### `hosts`
**Type:** `str`
Target hosts pattern (e.g., "all", "webservers", "localhost").
### `become`
**Type:** `bool`
**Default:** `false`
Whether to use privilege escalation.
### `tasks`
**Type:** `listOf attrs`
**Default:** `[]`
List of tasks to execute. Each task corresponds to Ansible task syntax.
Standard Ansible playbook options are supported: `gather_facts`, `serial`, `vars`, `vars_files`, `tags`, `handlers`, `pre_tasks`, `post_tasks`, etc.

7
flake.lock generated
View file

@ -230,16 +230,17 @@
"nix-mkdocs": { "nix-mkdocs": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1745841841, "lastModified": 1754753501,
"narHash": "sha256-297zPQbUlc7ZAYDoaD6mCmQxCC3Tr4YOKekRF1ArZ7g=", "narHash": "sha256-k7KEGQ7qejmwrSXVSvZ9zVD1H+ZtNm0I5fmCVmZVi+4=",
"owner": "technofab", "owner": "technofab",
"repo": "nixmkdocs", "repo": "nixmkdocs",
"rev": "c7e3c3b13ded25818e9789938387bba6f2cde690", "rev": "aba4f26f320b15043101824b65e72058ecab296f",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
"dir": "lib", "dir": "lib",
"owner": "technofab", "owner": "technofab",
"ref": "v1.0.0",
"repo": "nixmkdocs", "repo": "nixmkdocs",
"type": "gitlab" "type": "gitlab"
} }

View file

@ -17,6 +17,7 @@
perSystem = { perSystem = {
lib, lib,
pkgs, pkgs,
self',
config, config,
... ...
}: { }: {
@ -42,6 +43,7 @@
path = ./docs; path = ./docs;
deps = pp: [ deps = pp: [
pp.mkdocs-material pp.mkdocs-material
pp.mkdocs-macros
(pp.callPackage inputs.mkdocs-material-umami {}) (pp.callPackage inputs.mkdocs-material-umami {})
]; ];
config = { config = {
@ -78,12 +80,21 @@
} }
]; ];
}; };
plugins = ["search" "material-umami"]; plugins = [
"search"
"material-umami"
{
macros = {
include_dir = self'.packages.optionsDocs;
};
}
];
nav = [ nav = [
{"Introduction" = "index.md";} {"Introduction" = "index.md";}
{"Usage" = "usage.md";} {"Usage" = "usage.md";}
{"Examples" = "examples.md";} {"Examples" = "examples.md";}
{"Reference" = "reference.md";} {"Reference" = "reference.md";}
{"Options" = "options.md";}
]; ];
markdown_extensions = [ markdown_extensions = [
"pymdownx.superfences" "pymdownx.superfences"
@ -174,13 +185,34 @@
packages = let packages = let
nblib = import ./lib {inherit pkgs lib;}; nblib = import ./lib {inherit pkgs lib;};
ntlib = inputs.nixtest.lib {inherit pkgs lib;}; ntlib = inputs.nixtest.lib {inherit pkgs lib;};
in { doclib = inputs.nix-mkdocs.lib {inherit lib pkgs;};
in rec {
tests = ntlib.mkNixtest { tests = ntlib.mkNixtest {
modules = ntlib.autodiscover {dir = ./tests;}; modules = ntlib.autodiscover {dir = ./tests;};
args = { args = {
inherit pkgs nblib ntlib; inherit pkgs nblib ntlib;
}; };
}; };
optionsDoc = doclib.mkOptionDocs {
module = {
imports = [
nblib.module
{
_module.args.pkgs = pkgs;
}
];
};
roots = [
{
url = "https://gitlab.com/TECHNOFAB/nixible/-/blob/main/lib";
path = toString ./lib;
}
];
};
optionsDocs = pkgs.runCommand "options-docs" {} ''
mkdir -p $out
ln -s ${optionsDoc} $out/options.md
'';
}; };
}; };
}; };
@ -195,7 +227,7 @@
treefmt-nix.url = "github:numtide/treefmt-nix"; treefmt-nix.url = "github:numtide/treefmt-nix";
nix-gitlab-ci.url = "gitlab:technofab/nix-gitlab-ci?dir=lib"; nix-gitlab-ci.url = "gitlab:technofab/nix-gitlab-ci?dir=lib";
nixtest.url = "gitlab:technofab/nixtest?dir=lib"; nixtest.url = "gitlab:technofab/nixtest?dir=lib";
nix-mkdocs.url = "gitlab:technofab/nixmkdocs?dir=lib"; nix-mkdocs.url = "gitlab:technofab/nixmkdocs/v1.0.0?dir=lib";
mkdocs-material-umami.url = "gitlab:technofab/mkdocs-material-umami"; mkdocs-material-umami.url = "gitlab:technofab/mkdocs-material-umami";
}; };

View file

@ -4,7 +4,7 @@
config, config,
... ...
}: let }: let
inherit (lib) mkOptionType isType filterAttrs types mkOption; inherit (lib) mkOptionType isType filterAttrs types mkOption literalExpression;
unsetType = mkOptionType { unsetType = mkOptionType {
name = "unset"; name = "unset";
@ -16,7 +16,11 @@
_type = "unset"; _type = "unset";
}; };
isUnset = isType "unset"; isUnset = isType "unset";
unsetOr = types.either unsetType; unsetOr = typ:
(types.either unsetType typ)
// {
description = typ.description;
};
filterUnset = value: filterUnset = value:
if builtins.isAttrs value && !builtins.hasAttr "_type" value if builtins.isAttrs value && !builtins.hasAttr "_type" value
@ -28,93 +32,226 @@
then builtins.filter (elem: !isUnset elem) (map filterUnset value) then builtins.filter (elem: !isUnset elem) (map filterUnset value)
else value; else value;
mkUnsetOption = args:
mkOption args
// {
type = unsetOr args.type;
default = unset;
defaultText = literalExpression "unset";
};
collectionType = types.submodule { collectionType = types.submodule {
options = { options = {
version = mkOption { version = mkOption {
type = types.str; type = types.str;
description = "Version of collection"; description = ''
Version of the collection.
'';
example = "1.0.0";
}; };
hash = mkOption { hash = mkOption {
type = types.str; type = types.str;
description = "Hash of the collection tarball"; description = ''
SHA256 hash of the collection tarball for verification.
'';
example = "sha256-...";
}; };
}; };
}; };
tasksType = types.submodule { tasksType = types.submodule {
freeformType = types.attrsOf (types.attrsOf types.anything); freeformType = types.attrsOf (types.attrsOf types.anything);
options = { options = {
name = mkOption { name = mkUnsetOption {
type = unsetOr types.str; type = types.str;
default = unset; description = ''
Name of the task.
'';
}; };
register = mkOption { register = mkUnsetOption {
type = unsetOr types.str; type = types.str;
default = unset; description = ''
Register the task's output to a variable.
'';
}; };
block = mkOption { block = mkUnsetOption {
type = unsetOr (types.listOf tasksType); type = types.listOf tasksType;
default = unset; description = ''
A block of tasks to execute.
'';
}; };
always = mkOption { rescue = mkUnsetOption {
type = unsetOr (types.listOf types.attrs); type = types.listOf tasksType;
default = unset; description = ''
A list of tasks to execute on failure of block tasks.
'';
};
always = mkUnsetOption {
type = types.listOf types.attrs;
description = ''
Tasks that always run, regardless of task status.
'';
};
delegate_to = mkUnsetOption {
type = types.str;
description = ''
Delegate task execution to another host.
'';
};
ignore_errors = mkUnsetOption {
type = types.bool;
description = ''
Ignore errors and continue with the playbook.
'';
};
loop = mkUnsetOption {
type = types.anything;
description = ''
Define a loop for the task.
'';
};
when = mkUnsetOption {
type = types.str;
description = ''
Condition under which the task runs.
'';
}; };
}; };
}; };
playbookType = types.listOf (types.submodule { playType = types.submodule {
freeformType = types.attrsOf (types.attrsOf types.anything);
options = { options = {
name = mkOption { name = mkOption {
type = types.str; type = types.str;
description = "Name of the play"; description = ''
Name of the play.
'';
}; };
hosts = mkOption { hosts = mkOption {
type = types.str; type = types.str;
description = "The target hosts for this play (e.g., 'all', 'webservers')"; description = ''
The target hosts for this play (e.g., 'all', 'webservers').
'';
example = "all";
}; };
become = mkOption { remote_user = mkUnsetOption {
type = unsetOr types.bool; type = types.str;
default = unset; description = ''
description = "Whether to use privilege escalation (become: yes)"; The user to execute tasks as on the remote server.
'';
}; };
gather_facts = mkOption { tags = mkUnsetOption {
type = unsetOr types.bool; type = types.listOf types.str;
default = unset; description = ''
description = ""; Tags to filter tasks to run.
'';
};
become = mkUnsetOption {
type = types.bool;
description = ''
Whether to use privilege escalation (become: yes).
'';
};
become_method = mkUnsetOption {
type = types.str;
description = ''
Privilege escalation method.
'';
};
vars = mkUnsetOption {
type = types.attrs;
description = ''
Variables for the play.
'';
};
gather_facts = mkUnsetOption {
type = types.either types.bool types.str;
description = ''
Whether to run the setup module to gather facts before executing tasks.
'';
};
when = mkUnsetOption {
type = types.str;
description = ''
Condition under which the play runs.
'';
}; };
tasks = mkOption { tasks = mkOption {
type = types.listOf tasksType; type = types.listOf tasksType;
default = []; default = [];
description = "List of tasks to execute in this play"; description = ''
List of tasks to execute in this play
'';
}; };
}; };
}); };
playbookType = types.listOf playType;
in { in {
options = { options = {
ansiblePackage = mkOption { ansiblePackage = mkOption {
type = types.package; type = types.package;
default = pkgs.python3Packages.callPackage ./ansible-core.nix {}; default = pkgs.python3Packages.callPackage ./ansible-core.nix {};
description = "Ansible package to use (default doesn't have any collections installed for size)"; description = ''
The Ansible package to use. The default package is optimized for size, by not including the gazillion collections that pkgs.ansible and pkgs.ansible-core include.
'';
example = literalExpression "pkgs.ansible";
}; };
collections = mkOption { collections = mkOption {
type = types.attrsOf collectionType; type = types.attrsOf collectionType;
default = {}; default = {};
description = "Collections to fetch and install"; description = ''
Ansible collections to fetch and install from Galaxy.
'';
example = {
"community-general" = {
version = "8.0.0";
hash = "sha256-...";
};
};
}; };
dependencies = mkOption { dependencies = mkOption {
type = types.listOf types.package; type = types.listOf types.package;
default = []; default = [];
description = "List of packages to include at runtime"; description = "List of packages to include at runtime";
example = literalExpression "[pkgs.git pkgs.rsync]";
}; };
playbook = mkOption { playbook = mkOption {
type = playbookType; type = playbookType;
apply = res: filterUnset res; apply = res: filterUnset res;
description = "The actual playbook, defined as a Nix data structure"; description = "The actual playbook, defined as a Nix data structure";
example = [
{
name = "Configure servers";
hosts = "webservers";
become = true;
tasks = [
{
name = "Install nginx";
package = {
name = "nginx";
state = "present";
};
}
];
}
];
}; };
inventory = mkOption { inventory = mkOption {
type = types.attrs; type = types.attrs;
default = {}; default = {};
description = "Ansible inventory, will be converted to json and passed to ansible"; description = ''
Ansible inventory, will be converted to JSON and passed to Ansible.
'';
example = {
webservers = {
hosts = {
web1 = {ansible_host = "192.168.1.10";};
};
vars = {
http_port = 80;
};
};
};
}; };
inventoryFile = mkOption { inventoryFile = mkOption {