mirror of
https://gitlab.com/TECHNOFAB/nixible.git
synced 2025-12-11 01:30:10 +01:00
chore: initial commit
This commit is contained in:
commit
7602719790
24 changed files with 1916 additions and 0 deletions
2
.envrc
Normal file
2
.envrc
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
use flake . --impure --accept-flake-config
|
||||
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.direnv
|
||||
.devenv
|
||||
result
|
||||
.pre-commit-config.yaml
|
||||
|
||||
4
.gitlab-ci.yml
Normal file
4
.gitlab-ci.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
include:
|
||||
- component: gitlab.com/TECHNOFAB/nix-gitlab-ci/nix-gitlab-ci@2.1.0
|
||||
inputs:
|
||||
version: 2.1.0
|
||||
7
LICENSE.md
Normal file
7
LICENSE.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Copyright 2025 TECHNOFAB
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
65
README.md
Normal file
65
README.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Nixible
|
||||
|
||||
[](https://builtwithnix.org)
|
||||
[](https://gitlab.com/TECHNOFAB/nixible/-/commits/main)
|
||||

|
||||
[](https://gitlab.com/TECHNOFAB/nixible/-/releases)
|
||||
[](https://tec.tf/#support)
|
||||
[](https://nixible.projects.tf)
|
||||
|
||||
A Nix-based tool for managing Ansible playbooks with type safety and reproducibility.
|
||||
|
||||
## What is Nixible?
|
||||
|
||||
Nixible bridges the Nix and Ansible ecosystems by allowing you to define Ansible playbooks, inventories, and collections as Nix expressions. It provides:
|
||||
|
||||
- **Type-safe playbook definitions** using Nix's module system
|
||||
- **Reproducible Ansible environments** with locked dependencies
|
||||
- **Automatic collection management** from Ansible Galaxy
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Define your configuration
|
||||
|
||||
Create a `some-playbook.nix` file:
|
||||
|
||||
```nix title="some-playbook.nix"
|
||||
{pkgs, ...}: {
|
||||
collections = {
|
||||
"community-general" = {
|
||||
version = "8.0.0";
|
||||
hash = "sha256-...";
|
||||
};
|
||||
};
|
||||
|
||||
inventory = {}; # can also be omitted, we only use localhost
|
||||
|
||||
playbook = [{
|
||||
name = "Hello World";
|
||||
hosts = "localhost";
|
||||
tasks = [{
|
||||
name = "Say hello";
|
||||
debug.msg = "Hello from Nixible!";
|
||||
}];
|
||||
}];
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Run with Nix
|
||||
|
||||
```nix title="flake.nix"
|
||||
{
|
||||
inputs.nixible.url = "gitlab:TECHNOFAB/nixible?dir=lib";
|
||||
# outputs = ...
|
||||
# nixible_lib = inputs.nixible.lib { inherit pkgs lib; };
|
||||
packages.some-playbook = nixible_lib.mkNixibleCli ./some-playbook.nix;
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
nix run .#some-playbook
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Check the [docs](https://nixible.projects.tf).
|
||||
84
docs/examples.md
Normal file
84
docs/examples.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# Examples
|
||||
|
||||
See the `examples` directory in the repo.
|
||||
|
||||
## Task Examples
|
||||
|
||||
### File Operations
|
||||
|
||||
```nix
|
||||
{
|
||||
name = "Create configuration";
|
||||
template = {
|
||||
src = "nginx.conf.j2";
|
||||
dest = "/etc/nginx/nginx.conf";
|
||||
backup = true;
|
||||
};
|
||||
notify = "restart nginx";
|
||||
}
|
||||
```
|
||||
|
||||
### Service Management
|
||||
|
||||
```nix
|
||||
{
|
||||
name = "Start services";
|
||||
service = {
|
||||
name = "{{ item }}";
|
||||
state = "started";
|
||||
enabled = true;
|
||||
};
|
||||
loop = ["nginx" "postgresql"];
|
||||
}
|
||||
```
|
||||
|
||||
### Conditional Tasks
|
||||
|
||||
```nix
|
||||
{
|
||||
name = "Install SSL certificate";
|
||||
copy = {
|
||||
src = "ssl/cert.pem";
|
||||
dest = "/etc/ssl/certs/";
|
||||
};
|
||||
when = "ssl_enabled | default(false)";
|
||||
}
|
||||
```
|
||||
|
||||
### Block Tasks
|
||||
|
||||
```nix
|
||||
{
|
||||
block = [
|
||||
{
|
||||
name = "Create user";
|
||||
user = {
|
||||
name = "deploy";
|
||||
state = "present";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "Set up SSH key";
|
||||
authorized_key = {
|
||||
user = "deploy";
|
||||
key = "{{ ssh_public_key }}";
|
||||
};
|
||||
}
|
||||
];
|
||||
rescue = [
|
||||
{
|
||||
name = "Log error";
|
||||
debug.msg = "Failed to create user";
|
||||
}
|
||||
];
|
||||
always = [
|
||||
{
|
||||
name = "Cleanup";
|
||||
file = {
|
||||
path = "/tmp/setup";
|
||||
state = "absent";
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
```
|
||||
BIN
docs/images/favicon.png
Normal file
BIN
docs/images/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
docs/images/logo.png
Normal file
BIN
docs/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
60
docs/index.md
Normal file
60
docs/index.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Introduction
|
||||
|
||||
Nixible is a Nix-based tool for managing Ansible playbooks with type safety and reproducibility.
|
||||
|
||||
## What is Nixible?
|
||||
|
||||
Nixible bridges the Nix and Ansible ecosystems by allowing you to define Ansible playbooks, inventories, and collections as Nix expressions. It provides:
|
||||
|
||||
- **Type-safe playbook definitions** using Nix's module system
|
||||
- **Reproducible Ansible environments** with locked dependencies
|
||||
- **Automatic collection management** from Ansible Galaxy
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Define your configuration
|
||||
|
||||
Create a `some-playbook.nix` file:
|
||||
|
||||
```nix title="some-playbook.nix"
|
||||
{pkgs, ...}: {
|
||||
collections = {
|
||||
"community-general" = {
|
||||
version = "8.0.0";
|
||||
hash = "sha256-...";
|
||||
};
|
||||
};
|
||||
|
||||
inventory = {}; # can also be omitted, we only use localhost
|
||||
|
||||
playbook = [{
|
||||
name = "Hello World";
|
||||
hosts = "localhost";
|
||||
tasks = [{
|
||||
name = "Say hello";
|
||||
debug.msg = "Hello from Nixible!";
|
||||
}];
|
||||
}];
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Run with Nix
|
||||
|
||||
```nix title="flake.nix"
|
||||
{
|
||||
inputs.nixible.url = "gitlab:TECHNOFAB/nixible?dir=lib";
|
||||
# outputs = ...
|
||||
# nixible_lib = inputs.nixible.lib { inherit pkgs lib; };
|
||||
packages.some-playbook = nixible_lib.mkNixibleCli ./some-playbook.nix;
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
nix run .#some-playbook
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. **[Usage](usage.md)** - Learn how to build and run Nixible configurations
|
||||
1. **[Examples](examples.md)** - See real-world usage patterns
|
||||
1. **[Reference](reference.md)** - Detailed API and configuration reference
|
||||
215
docs/reference.md
Normal file
215
docs/reference.md
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
# Reference
|
||||
|
||||
## `flakeModule`
|
||||
|
||||
The `flakeModule` for [flake-parts](https://flake.parts).
|
||||
|
||||
Provides a `perSystem.nixible` option for defining Nixible configurations directly in your flake.
|
||||
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
nixible.url = "gitlab:TECHNOFAB/nixible?dir=lib";
|
||||
};
|
||||
|
||||
outputs = inputs@{ flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
imports = [ nixible.flakeModule ];
|
||||
systems = # ...
|
||||
|
||||
perSystem = { pkgs, ... }: {
|
||||
nixible = {
|
||||
"deploy" = {
|
||||
dependencies = [ pkgs.rsync ];
|
||||
playbook = [{
|
||||
name = "Deploy application";
|
||||
hosts = "servers";
|
||||
tasks = [ /* ... */ ];
|
||||
}];
|
||||
};
|
||||
"backup" = {
|
||||
dependencies = [ pkgs.borg ];
|
||||
playbook = [{
|
||||
name = "Backup data";
|
||||
hosts = "backup_servers";
|
||||
tasks = [ /* ... */ ];
|
||||
}];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Each configuration defined in `perSystem.nixible` automatically creates a corresponding package in `legacyPackages` with the name `nixible:<config-name>`. These packages contain the CLI executable for that specific configuration.
|
||||
|
||||
**Example usage:**
|
||||
|
||||
```bash
|
||||
nix run .#nixible:deploy
|
||||
nix run .#nixible:backup
|
||||
```
|
||||
|
||||
## `lib`
|
||||
|
||||
### `module`
|
||||
|
||||
The nix module for validation of Nixible configurations.
|
||||
Used internally by `mkNixible`.
|
||||
|
||||
### `mkNixible`
|
||||
|
||||
```nix
|
||||
mkNixible config
|
||||
```
|
||||
|
||||
Creates a Nixible configuration module evaluation.
|
||||
`config` can be a path to a nix file or a function/attrset.
|
||||
|
||||
**Noteworthy attributes**:
|
||||
|
||||
- `config`: The evaluated configuration with all options
|
||||
- `config.inventoryFile`: Generated JSON inventory file
|
||||
- `config.playbookFile`: Generated YAML playbook file
|
||||
- `config.installedCollections`: Directory containing installed collections
|
||||
- `config.cli`: The nixible CLI executable
|
||||
|
||||
### `mkNixibleCli`
|
||||
|
||||
```nix
|
||||
mkNixibleCli config
|
||||
```
|
||||
|
||||
Creates a CLI executable for your Nixible configuration.
|
||||
Basically `(mkNixible config).config.cli`.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### `ansiblePackage`
|
||||
|
||||
**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.
|
||||
119
docs/usage.md
Normal file
119
docs/usage.md
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
# Usage
|
||||
|
||||
Learn how to build and use Nixible configurations.
|
||||
|
||||
## Using flakeModule
|
||||
|
||||
The recommended way to use Nixible is with the flakeModule:
|
||||
|
||||
```nix title="flake.nix"
|
||||
{
|
||||
inputs = {
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
nixible.url = "gitlab:TECHNOFAB/nixible?dir=lib";
|
||||
};
|
||||
|
||||
outputs = inputs@{ flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
imports = [ nixible.flakeModule ];
|
||||
systems = # ...
|
||||
|
||||
perSystem = { pkgs, ... }: {
|
||||
nixible = {
|
||||
deploy = {
|
||||
dependencies = [ pkgs.rsync ];
|
||||
inventory = {
|
||||
webservers = {
|
||||
hosts = {
|
||||
web1 = { ansible_host = "192.168.1.10"; };
|
||||
};
|
||||
};
|
||||
};
|
||||
playbook = [{
|
||||
name = "Deploy application";
|
||||
hosts = "webservers";
|
||||
tasks = [{
|
||||
name = "Deploy files";
|
||||
copy = {
|
||||
src = "{{ pwd }}/dist/";
|
||||
dest = "/var/www/";
|
||||
};
|
||||
}];
|
||||
}];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Then run with:
|
||||
|
||||
```bash
|
||||
nix run .#nixible:deploy
|
||||
|
||||
# With ansible-playbook options
|
||||
nix run .#nixible:deploy -- --check --diff --limit web1
|
||||
```
|
||||
|
||||
## Using the CLI directly
|
||||
|
||||
You can also create CLI packages directly:
|
||||
|
||||
```nix title="flake.nix"
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
nixible.url = "gitlab:TECHNOFAB/nixible?dir=lib";
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, nixible, ... }: let
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
lib = nixpkgs.lib;
|
||||
nixible_lib = nixible.lib { inherit pkgs lib; };
|
||||
in {
|
||||
packages.x86_64-linux.deploy = nixible_lib.mkNixibleCli ./deploy.nix;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Then run with:
|
||||
|
||||
```bash
|
||||
nix run .#deploy
|
||||
|
||||
# Dry run with diff
|
||||
nix run .#deploy -- --check --diff
|
||||
|
||||
# Limit to specific hosts
|
||||
nix run .#deploy -- --limit webservers
|
||||
|
||||
# Extra variables
|
||||
nix run .#deploy -- --extra-vars "env=production debug=true"
|
||||
# etc.
|
||||
```
|
||||
|
||||
## Variables
|
||||
|
||||
Nixible automatically provides these variables to your playbooks:
|
||||
|
||||
- `pwd`: Current working directory when nixible is run
|
||||
- `git_root`: Git repository root (empty if not in a git repo)
|
||||
|
||||
Use them in your playbooks:
|
||||
|
||||
```nix
|
||||
playbook = [{
|
||||
name = "Deploy from current directory";
|
||||
hosts = "localhost";
|
||||
tasks = [{
|
||||
name = "Copy files";
|
||||
copy = {
|
||||
src = "{{ pwd }}/dist/";
|
||||
dest = "/var/www/";
|
||||
};
|
||||
}];
|
||||
}];
|
||||
```
|
||||
2
examples/.sops.yaml
Normal file
2
examples/.sops.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
keys: []
|
||||
creation_rules: []
|
||||
62
examples/sops.nix
Normal file
62
examples/sops.nix
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
{pkgs, ...}: {
|
||||
#
|
||||
# NOTE: needs a .sops.yaml file in the directory to work
|
||||
#
|
||||
dependencies = [pkgs.sops];
|
||||
|
||||
collections = {
|
||||
"community-crypto" = {
|
||||
version = "3.0.0";
|
||||
hash = "sha256-sRuv2qateLgZRWlTtHO1f2hb4vb7Oc/2DHTuLmexuiI=";
|
||||
};
|
||||
"community-sops" = {
|
||||
version = "2.1.0";
|
||||
hash = "sha256-5VGVBV+z4bUe6XdKu5P8+HbABCvgeR8hvDmL5s1BfUM=";
|
||||
};
|
||||
};
|
||||
|
||||
playbook = [
|
||||
{
|
||||
name = "Create SOPS-encrypted private key";
|
||||
hosts = "localhost";
|
||||
tasks = [
|
||||
{
|
||||
block = [
|
||||
{
|
||||
name = "Create private key";
|
||||
"community.crypto.openssl_privatekey_pipe" = {
|
||||
size = 2048;
|
||||
content =
|
||||
# jinja
|
||||
''
|
||||
{{ lookup(
|
||||
'community.sops.sops',
|
||||
"{{ pwd }}/keys/private_key.pem.sops",
|
||||
config_path='${./.sops.yaml}',
|
||||
empty_on_not_exist=true) }}
|
||||
'';
|
||||
};
|
||||
no_log = true;
|
||||
register = "private_key";
|
||||
}
|
||||
{
|
||||
name = "Write encrypted key to disk";
|
||||
when = "private_key is changed";
|
||||
"community.sops.sops_encrypt" = {
|
||||
path = "{{ pwd }}/keys/private_key.pem.sops";
|
||||
content_text = "{{ private_key.privatekey }}";
|
||||
config_path = ./.sops.yaml;
|
||||
};
|
||||
}
|
||||
];
|
||||
always = [
|
||||
{
|
||||
name = "Wipe private key from Ansible's facts";
|
||||
set_fact.private_key = "";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
376
flake.lock
generated
Normal file
376
flake.lock
generated
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
{
|
||||
"nodes": {
|
||||
"cachix": {
|
||||
"inputs": {
|
||||
"devenv": [
|
||||
"devenv"
|
||||
],
|
||||
"flake-compat": [
|
||||
"devenv"
|
||||
],
|
||||
"git-hooks": [
|
||||
"devenv",
|
||||
"git-hooks"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1748883665,
|
||||
"narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "f707778d902af4d62d8dd92c269f8e70de09acbe",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "latest",
|
||||
"repo": "cachix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"devenv": {
|
||||
"inputs": {
|
||||
"cachix": "cachix",
|
||||
"flake-compat": "flake-compat",
|
||||
"git-hooks": "git-hooks",
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1752600380,
|
||||
"narHash": "sha256-3ZLDE0Taf9fqcTK6+fhDvq06WgzudK/E70zdddSc5vA=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "7441c97330233543ee28d5c4612173f108250536",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"devenv",
|
||||
"nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733312601,
|
||||
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts_2": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1751413152,
|
||||
"narHash": "sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "77826244401ea9de6e3bac47c2db46005e1f30b5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1750779888,
|
||||
"narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"git-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"mkdocs-material-umami": {
|
||||
"locked": {
|
||||
"lastModified": 1745840856,
|
||||
"narHash": "sha256-1Ad1JTMQMP6YsoIKAA+SBCE15qWrYkGue9/lXOLnu9I=",
|
||||
"owner": "technofab",
|
||||
"repo": "mkdocs-material-umami",
|
||||
"rev": "3ac9b194450f6b779c37b8d16fec640198e5cd0a",
|
||||
"type": "gitlab"
|
||||
},
|
||||
"original": {
|
||||
"owner": "technofab",
|
||||
"repo": "mkdocs-material-umami",
|
||||
"type": "gitlab"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"flake-parts": "flake-parts",
|
||||
"git-hooks-nix": [
|
||||
"devenv",
|
||||
"git-hooks"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-23-11": [
|
||||
"devenv"
|
||||
],
|
||||
"nixpkgs-regression": [
|
||||
"devenv"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1752251701,
|
||||
"narHash": "sha256-fkkkwB7jz+14ZdIHAYCCNypO9EZDCKpj7LEQZhV6QJs=",
|
||||
"owner": "cachix",
|
||||
"repo": "nix",
|
||||
"rev": "54df04f09cb084b9e58529c0ae6f53f0e50f1a19",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "devenv-2.30",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-gitlab-ci": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1752052838,
|
||||
"narHash": "sha256-EqP4xB8YTVXWPCCchnVtQbuq0bKa79TUEcPF3hjuX/k=",
|
||||
"owner": "technofab",
|
||||
"repo": "nix-gitlab-ci",
|
||||
"rev": "0c6949f585a2c1ea2cf85fc01445496f7c75faae",
|
||||
"type": "gitlab"
|
||||
},
|
||||
"original": {
|
||||
"dir": "lib",
|
||||
"owner": "technofab",
|
||||
"repo": "nix-gitlab-ci",
|
||||
"type": "gitlab"
|
||||
}
|
||||
},
|
||||
"nix-mkdocs": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1745841841,
|
||||
"narHash": "sha256-297zPQbUlc7ZAYDoaD6mCmQxCC3Tr4YOKekRF1ArZ7g=",
|
||||
"owner": "technofab",
|
||||
"repo": "nixmkdocs",
|
||||
"rev": "c7e3c3b13ded25818e9789938387bba6f2cde690",
|
||||
"type": "gitlab"
|
||||
},
|
||||
"original": {
|
||||
"dir": "lib",
|
||||
"owner": "technofab",
|
||||
"repo": "nixmkdocs",
|
||||
"type": "gitlab"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1750441195,
|
||||
"narHash": "sha256-yke+pm+MdgRb6c0dPt8MgDhv7fcBbdjmv1ZceNTyzKg=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"rev": "0ceffe312871b443929ff3006960d29b120dc627",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "rolling",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1751159883,
|
||||
"narHash": "sha256-urW/Ylk9FIfvXfliA1ywh75yszAbiTEVgpPeinFyVZo=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "14a40a1d7fb9afa4739275ac642ed7301a9ba1ab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1752446735,
|
||||
"narHash": "sha256-Nz2vtUEaRB/UjvPfuhHpez060P/4mvGpXW4JCDIboA4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a421ac6595024edcfbb1ef950a3712b89161c359",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1747958103,
|
||||
"narHash": "sha256-qmmFCrfBwSHoWw7cVK4Aj+fns+c54EBP8cGqp/yK410=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "fe51d34885f7b5e3e7b59572796e1bcb427eccb1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixtest": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1749915293,
|
||||
"narHash": "sha256-SeDdPVcvtgkBK1fb8lLKf+1iOY8UyDTVN6A6H19aw2M=",
|
||||
"owner": "technofab",
|
||||
"repo": "nixtest",
|
||||
"rev": "c2a1208534fbdd8ab28ff3e45262b527f81a1755",
|
||||
"type": "gitlab"
|
||||
},
|
||||
"original": {
|
||||
"dir": "lib",
|
||||
"owner": "technofab",
|
||||
"repo": "nixtest",
|
||||
"type": "gitlab"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"flake-parts": "flake-parts_2",
|
||||
"mkdocs-material-umami": "mkdocs-material-umami",
|
||||
"nix-gitlab-ci": "nix-gitlab-ci",
|
||||
"nix-mkdocs": "nix-mkdocs",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixtest": "nixtest",
|
||||
"systems": "systems",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1689347949,
|
||||
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-linux",
|
||||
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-linux",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1752055615,
|
||||
"narHash": "sha256-19m7P4O/Aw/6+CzncWMAJu89JaKeMh3aMle1CNQSIwM=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "c9d477b5d5bd7f26adddd3f96cfd6a904768d4f9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
215
flake.nix
Normal file
215
flake.nix
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
{
|
||||
outputs = {
|
||||
flake-parts,
|
||||
systems,
|
||||
...
|
||||
} @ inputs:
|
||||
flake-parts.lib.mkFlake {inherit inputs;} {
|
||||
imports = [
|
||||
inputs.devenv.flakeModule
|
||||
inputs.treefmt-nix.flakeModule
|
||||
inputs.nix-gitlab-ci.flakeModule
|
||||
inputs.nix-mkdocs.flakeModule
|
||||
./lib/flakeModule.nix
|
||||
];
|
||||
systems = import systems;
|
||||
flake = {};
|
||||
perSystem = {
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
treefmt = {
|
||||
projectRootFile = "flake.nix";
|
||||
programs = {
|
||||
alejandra.enable = true;
|
||||
mdformat.enable = true;
|
||||
};
|
||||
};
|
||||
devenv.shells.default = {
|
||||
containers = pkgs.lib.mkForce {};
|
||||
|
||||
git-hooks.hooks = {
|
||||
treefmt = {
|
||||
enable = true;
|
||||
packageOverrides.treefmt = config.treefmt.build.wrapper;
|
||||
};
|
||||
convco.enable = true;
|
||||
};
|
||||
};
|
||||
doc = {
|
||||
path = ./docs;
|
||||
deps = pp: [
|
||||
pp.mkdocs-material
|
||||
(pp.callPackage inputs.mkdocs-material-umami {})
|
||||
];
|
||||
config = {
|
||||
site_name = "Nixible";
|
||||
repo_name = "TECHNOFAB/nixible";
|
||||
repo_url = "https://gitlab.com/TECHNOFAB/nixible";
|
||||
edit_uri = "edit/main/docs/";
|
||||
theme = {
|
||||
name = "material";
|
||||
features = ["content.code.copy" "content.action.edit"];
|
||||
icon.repo = "simple/gitlab";
|
||||
logo = "images/logo.png";
|
||||
favicon = "images/favicon.png";
|
||||
palette = [
|
||||
{
|
||||
scheme = "default";
|
||||
media = "(prefers-color-scheme: light)";
|
||||
primary = "black";
|
||||
accent = "blue";
|
||||
toggle = {
|
||||
icon = "material/brightness-7";
|
||||
name = "Switch to dark mode";
|
||||
};
|
||||
}
|
||||
{
|
||||
scheme = "slate";
|
||||
media = "(prefers-color-scheme: dark)";
|
||||
primary = "black";
|
||||
accent = "blue";
|
||||
toggle = {
|
||||
icon = "material/brightness-4";
|
||||
name = "Switch to light mode";
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
plugins = ["search" "material-umami"];
|
||||
nav = [
|
||||
{"Introduction" = "index.md";}
|
||||
{"Usage" = "usage.md";}
|
||||
{"Examples" = "examples.md";}
|
||||
{"Reference" = "reference.md";}
|
||||
];
|
||||
markdown_extensions = [
|
||||
"pymdownx.superfences"
|
||||
"admonition"
|
||||
];
|
||||
extra.analytics = {
|
||||
provider = "umami";
|
||||
site_id = "d8354dfa-2ad2-4089-90d2-899b981aef22";
|
||||
src = "https://analytics.tf/umami";
|
||||
domains = "nixible.projects.tf";
|
||||
feedback = {
|
||||
title = "Was this page helpful?";
|
||||
ratings = [
|
||||
{
|
||||
icon = "material/thumb-up-outline";
|
||||
name = "This page is helpful";
|
||||
data = "good";
|
||||
note = "Thanks for your feedback!";
|
||||
}
|
||||
{
|
||||
icon = "material/thumb-down-outline";
|
||||
name = "This page could be improved";
|
||||
data = "bad";
|
||||
note = "Thanks for your feedback! Please leave feedback by creating an issue :)";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
ci = {
|
||||
stages = ["test" "build" "deploy"];
|
||||
jobs = {
|
||||
"test:lib" = {
|
||||
stage = "test";
|
||||
script = [
|
||||
"nix run .#tests -- --junit=junit.xml"
|
||||
];
|
||||
allow_failure = true;
|
||||
artifacts = {
|
||||
when = "always";
|
||||
reports.junit = "junit.xml";
|
||||
};
|
||||
};
|
||||
"docs" = {
|
||||
stage = "build";
|
||||
script = [
|
||||
# sh
|
||||
''
|
||||
nix build .#docs:default
|
||||
mkdir -p public
|
||||
cp -r result/. public/
|
||||
''
|
||||
];
|
||||
artifacts.paths = ["public"];
|
||||
};
|
||||
"pages" = {
|
||||
nix.enable = false;
|
||||
image = "alpine:latest";
|
||||
stage = "deploy";
|
||||
script = ["true"];
|
||||
artifacts.paths = ["public"];
|
||||
rules = [
|
||||
{
|
||||
"if" = "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nixible = {
|
||||
"hello".playbook = [
|
||||
{
|
||||
name = "Hello World";
|
||||
hosts = "localhost";
|
||||
tasks = [
|
||||
{
|
||||
name = "Say hello";
|
||||
debug.msg = "Hello from Nixible!";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
"another".playbook = [];
|
||||
};
|
||||
|
||||
packages = let
|
||||
nblib = import ./lib {inherit pkgs lib;};
|
||||
ntlib = inputs.nixtest.lib {inherit pkgs lib;};
|
||||
in {
|
||||
tests = ntlib.mkNixtest {
|
||||
modules = ntlib.autodiscover {dir = ./tests;};
|
||||
args = {
|
||||
inherit pkgs nblib ntlib;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
|
||||
# flake & devenv related
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
systems.url = "github:nix-systems/default-linux";
|
||||
devenv.url = "github:cachix/devenv";
|
||||
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||
nix-gitlab-ci.url = "gitlab:technofab/nix-gitlab-ci?dir=lib";
|
||||
nixtest.url = "gitlab:technofab/nixtest?dir=lib";
|
||||
nix-mkdocs.url = "gitlab:technofab/nixmkdocs?dir=lib";
|
||||
mkdocs-material-umami.url = "gitlab:technofab/mkdocs-material-umami";
|
||||
};
|
||||
|
||||
nixConfig = {
|
||||
extra-substituters = [
|
||||
"https://cache.nixos.org/"
|
||||
"https://nix-community.cachix.org"
|
||||
"https://devenv.cachix.org"
|
||||
];
|
||||
|
||||
extra-trusted-public-keys = [
|
||||
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
|
||||
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
|
||||
"devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="
|
||||
];
|
||||
};
|
||||
}
|
||||
45
lib/ansible-collections.nix
Normal file
45
lib/ansible-collections.nix
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
stdenv,
|
||||
lib,
|
||||
pkgs,
|
||||
}: ansible: collections: let
|
||||
inherit (lib) concatStringsSep mapAttrsToList;
|
||||
|
||||
mkCollection = {
|
||||
name,
|
||||
version,
|
||||
hash,
|
||||
}:
|
||||
stdenv.mkDerivation {
|
||||
pname = name;
|
||||
inherit version;
|
||||
src = pkgs.fetchurl {
|
||||
inherit hash;
|
||||
url = "https://galaxy.ansible.com/download/${name}-${version}.tar.gz";
|
||||
};
|
||||
|
||||
phases = ["installPhase"];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp $src $out/collection.tar.gz
|
||||
'';
|
||||
};
|
||||
|
||||
installCollection = collection: "${ansible}/bin/ansible-galaxy collection install ${collection}/collection.tar.gz";
|
||||
installCollections = concatStringsSep "\n" (
|
||||
mapAttrsToList (
|
||||
name: coll:
|
||||
installCollection (
|
||||
mkCollection ({inherit name;} // coll)
|
||||
)
|
||||
)
|
||||
collections
|
||||
);
|
||||
in
|
||||
pkgs.runCommand "ansible-collections" {} ''
|
||||
mkdir -p $out
|
||||
export HOME=./
|
||||
export ANSIBLE_COLLECTIONS_PATH=$out
|
||||
${installCollections}
|
||||
''
|
||||
91
lib/ansible-core.nix
Normal file
91
lib/ansible-core.nix
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
lib,
|
||||
buildPythonPackage,
|
||||
fetchPypi,
|
||||
installShellFiles,
|
||||
docutils,
|
||||
setuptools,
|
||||
cryptography,
|
||||
jinja2,
|
||||
junit-xml,
|
||||
lxml,
|
||||
ncclient,
|
||||
packaging,
|
||||
paramiko,
|
||||
ansible-pylibssh,
|
||||
pexpect,
|
||||
psutil,
|
||||
pycrypto,
|
||||
pyyaml,
|
||||
requests,
|
||||
resolvelib,
|
||||
scp,
|
||||
windowsSupport ? false,
|
||||
pywinrm,
|
||||
xmltodict,
|
||||
}:
|
||||
buildPythonPackage rec {
|
||||
pname = "ansible-core";
|
||||
version = "2.18.6";
|
||||
pyproject = true;
|
||||
|
||||
src = fetchPypi {
|
||||
pname = "ansible_core";
|
||||
inherit version;
|
||||
hash = "sha256-JbsgzhUWobcweDGyY872hAQ7NyBxFGa9nUFk5f1XZVc=";
|
||||
};
|
||||
|
||||
# ansible_connection is already wrapped, so don't pass it through
|
||||
# the python interpreter again, as it would break execution of
|
||||
# connection plugins.
|
||||
postPatch = ''
|
||||
substituteInPlace lib/ansible/executor/task_executor.py \
|
||||
--replace "[python," "["
|
||||
|
||||
patchShebangs --build packaging/cli-doc/build.py
|
||||
|
||||
SETUPTOOLS_PATTERN='"setuptools[0-9 <>=.,]+"'
|
||||
PYPROJECT=$(cat pyproject.toml)
|
||||
if [[ "$PYPROJECT" =~ $SETUPTOOLS_PATTERN ]]; then
|
||||
echo "setuptools replace: ''${BASH_REMATCH[0]}"
|
||||
echo "''${PYPROJECT//''${BASH_REMATCH[0]}/'"setuptools"'}" > pyproject.toml
|
||||
else
|
||||
exit 2
|
||||
fi
|
||||
'';
|
||||
|
||||
nativeBuildInputs = [
|
||||
installShellFiles
|
||||
docutils
|
||||
];
|
||||
|
||||
build-system = [setuptools];
|
||||
|
||||
dependencies =
|
||||
[
|
||||
# from requirements.txt
|
||||
cryptography
|
||||
jinja2
|
||||
packaging
|
||||
pyyaml
|
||||
resolvelib
|
||||
# optional dependencies
|
||||
junit-xml
|
||||
lxml
|
||||
ncclient
|
||||
paramiko
|
||||
ansible-pylibssh
|
||||
pexpect
|
||||
psutil
|
||||
pycrypto
|
||||
requests
|
||||
scp
|
||||
xmltodict
|
||||
]
|
||||
++ lib.optionals windowsSupport [pywinrm];
|
||||
|
||||
pythonRelaxDeps = ["resolvelib"];
|
||||
|
||||
# internal import errors, missing dependencies
|
||||
doCheck = false;
|
||||
}
|
||||
20
lib/default.nix
Normal file
20
lib/default.nix
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
pkgs,
|
||||
lib ? pkgs.lib,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) evalModules;
|
||||
in rec {
|
||||
module = ./module.nix;
|
||||
|
||||
mkNixible = config:
|
||||
evalModules {
|
||||
specialArgs = {inherit pkgs;};
|
||||
modules = [
|
||||
module
|
||||
config
|
||||
];
|
||||
};
|
||||
|
||||
mkNixibleCli = config: (mkNixible config).config.cli;
|
||||
}
|
||||
6
lib/flake.nix
Normal file
6
lib/flake.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
outputs = {...}: {
|
||||
lib = import ./.;
|
||||
flakeModule = ./flakeModule.nix;
|
||||
};
|
||||
}
|
||||
33
lib/flakeModule.nix
Normal file
33
lib/flakeModule.nix
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
flake-parts-lib,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) mkOption types;
|
||||
in {
|
||||
options.perSystem = flake-parts-lib.mkPerSystemOption (
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
nixible-lib = import ./. {inherit pkgs lib;};
|
||||
in {
|
||||
options.nixible = mkOption {
|
||||
type = types.attrsOf (types.submodule (args:
|
||||
# needed to get pkgs in there, weirdly enough
|
||||
import nixible-lib.module (args
|
||||
// {
|
||||
inherit pkgs;
|
||||
})));
|
||||
default = {};
|
||||
};
|
||||
|
||||
config.legacyPackages = lib.fold (playbook: acc: acc // playbook) {} (
|
||||
map (playbook_name: {
|
||||
"nixible:${playbook_name}" = (builtins.getAttr playbook_name config.nixible).cli;
|
||||
}) (builtins.attrNames config.nixible)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
103
lib/module.nix
Normal file
103
lib/module.nix
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
{
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) types mkOption;
|
||||
|
||||
collectionType = types.submodule {
|
||||
options = {
|
||||
version = mkOption {
|
||||
type = types.str;
|
||||
description = "Version of collection";
|
||||
};
|
||||
hash = mkOption {
|
||||
type = types.str;
|
||||
description = "Hash of the collection tarball";
|
||||
};
|
||||
};
|
||||
};
|
||||
in {
|
||||
options = {
|
||||
ansiblePackage = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.python3Packages.callPackage ./ansible-core.nix {};
|
||||
description = "Ansible package to use (default doesn't have any collections installed for size)";
|
||||
};
|
||||
collections = mkOption {
|
||||
type = types.attrsOf collectionType;
|
||||
default = {};
|
||||
description = "Collections to fetch and install";
|
||||
};
|
||||
dependencies = mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [];
|
||||
description = "List of packages to include at runtime";
|
||||
};
|
||||
playbook = mkOption {
|
||||
type = types.listOf (types.submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = "Name of the play";
|
||||
};
|
||||
hosts = mkOption {
|
||||
type = types.str;
|
||||
description = "The target hosts for this play (e.g., 'all', 'webservers')";
|
||||
};
|
||||
become = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to use privilege escalation (become: yes)";
|
||||
};
|
||||
tasks = mkOption {
|
||||
type = types.listOf types.attrs;
|
||||
default = [];
|
||||
description = "List of tasks to execute in this play";
|
||||
};
|
||||
};
|
||||
});
|
||||
description = "The actual playbook, defined as a Nix data structure";
|
||||
};
|
||||
|
||||
inventory = mkOption {
|
||||
type = types.attrs;
|
||||
default = {};
|
||||
description = "Ansible inventory, will be converted to json and passed to ansible";
|
||||
};
|
||||
|
||||
inventoryFile = mkOption {
|
||||
internal = true;
|
||||
type = types.package;
|
||||
};
|
||||
playbookFile = mkOption {
|
||||
internal = true;
|
||||
type = types.package;
|
||||
};
|
||||
installedCollections = mkOption {
|
||||
internal = true;
|
||||
type = types.package;
|
||||
};
|
||||
cli = mkOption {
|
||||
internal = true;
|
||||
type = types.package;
|
||||
};
|
||||
};
|
||||
config = {
|
||||
inventoryFile = (pkgs.formats.json {}).generate "inventory.json" config.inventory;
|
||||
playbookFile = (pkgs.formats.yaml {}).generate "playbook.yml" config.playbook;
|
||||
installedCollections = pkgs.callPackage ./ansible-collections.nix {} config.ansiblePackage config.collections;
|
||||
cli = pkgs.writeShellApplication {
|
||||
name = "nixible";
|
||||
runtimeInputs = config.dependencies;
|
||||
text = ''
|
||||
set -euo pipefail
|
||||
export ANSIBLE_COLLECTIONS_PATH=${config.installedCollections}
|
||||
|
||||
git_repo=$(git rev-parse --show-toplevel 2>/dev/null || true)
|
||||
${config.ansiblePackage}/bin/ansible-playbook -i ${config.inventoryFile} ${config.playbookFile} -e "pwd=$(pwd)" -e "git_root=$git_repo" "$@"
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
119
tests/cli_test.nix
Normal file
119
tests/cli_test.nix
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
{
|
||||
pkgs,
|
||||
nblib,
|
||||
ntlib,
|
||||
...
|
||||
}: {
|
||||
suites."CLI Tests" = {
|
||||
pos = __curPos;
|
||||
tests = [
|
||||
{
|
||||
name = "dependencies inclusion";
|
||||
type = "script";
|
||||
script = let
|
||||
config = {pkgs, ...}: {
|
||||
dependencies = [pkgs.git pkgs.curl];
|
||||
playbook = [
|
||||
{
|
||||
name = "Test dependencies";
|
||||
hosts = "localhost";
|
||||
tasks = [];
|
||||
}
|
||||
];
|
||||
};
|
||||
cli = nblib.mkNixibleCli config;
|
||||
in
|
||||
# sh
|
||||
''
|
||||
${ntlib.helpers.scriptHelpers}
|
||||
|
||||
# check that dependencies are included in runtime inputs
|
||||
assert_file_contains "${cli}/bin/nixible" "${pkgs.git}" "should include git in PATH"
|
||||
assert_file_contains "${cli}/bin/nixible" "${pkgs.curl}" "should include curl in PATH"
|
||||
'';
|
||||
}
|
||||
{
|
||||
name = "CLI executable structure";
|
||||
type = "script";
|
||||
script = let
|
||||
config = {pkgs, ...}: {
|
||||
dependencies = [pkgs.git];
|
||||
playbook = [
|
||||
{
|
||||
name = "CLI test";
|
||||
hosts = "localhost";
|
||||
tasks = [
|
||||
{
|
||||
debug.msg = "Testing CLI";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
cli = nblib.mkNixibleCli config;
|
||||
in
|
||||
# sh
|
||||
''
|
||||
${ntlib.helpers.scriptHelpers}
|
||||
|
||||
# check CLI is executable
|
||||
assert "-x ${cli}/bin/nixible" "CLI should be executable"
|
||||
|
||||
# check wrapper content
|
||||
assert_file_contains "${cli}/bin/nixible" "set -euo pipefail" "should have error handling"
|
||||
assert_file_contains "${cli}/bin/nixible" "ansible-playbook" "should call ansible-playbook"
|
||||
assert_file_contains "${cli}/bin/nixible" "git rev-parse --show-toplevel" "should detect git repo"
|
||||
'';
|
||||
}
|
||||
{
|
||||
name = "variables setup";
|
||||
type = "script";
|
||||
script = let
|
||||
config = {
|
||||
playbook = [
|
||||
{
|
||||
name = "Environment test";
|
||||
hosts = "localhost";
|
||||
tasks = [];
|
||||
}
|
||||
];
|
||||
};
|
||||
cli = nblib.mkNixibleCli config;
|
||||
in
|
||||
# sh
|
||||
''
|
||||
${ntlib.helpers.scriptHelpers}
|
||||
|
||||
assert_file_contains "${cli}/bin/nixible" 'export ANSIBLE_COLLECTIONS_PATH=' "should export collections path"
|
||||
assert_file_contains "${cli}/bin/nixible" '-e "pwd=$(pwd)"' "should pass pwd variable"
|
||||
assert_file_contains "${cli}/bin/nixible" '-e "git_root=$git_repo"' "should pass git_root variable"
|
||||
'';
|
||||
}
|
||||
{
|
||||
name = "runtime dependencies inclusion";
|
||||
type = "script";
|
||||
script = let
|
||||
config = {pkgs, ...}: {
|
||||
dependencies = [pkgs.rsync pkgs.openssh];
|
||||
playbook = [
|
||||
{
|
||||
name = "Dependencies test";
|
||||
hosts = "localhost";
|
||||
tasks = [];
|
||||
}
|
||||
];
|
||||
};
|
||||
cli = nblib.mkNixibleCli config;
|
||||
in
|
||||
# sh
|
||||
''
|
||||
${ntlib.helpers.scriptHelpers}
|
||||
|
||||
# check runtime dependencies are properly included
|
||||
assert_file_contains "${cli}/bin/nixible" "rsync" "should include rsync from runtimeInputs"
|
||||
assert_file_contains "${cli}/bin/nixible" "openssh" "should include openssh from runtimeInputs"
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
101
tests/integration_test.nix
Normal file
101
tests/integration_test.nix
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
pkgs,
|
||||
nblib,
|
||||
ntlib,
|
||||
...
|
||||
}: {
|
||||
suites."Integration Tests" = {
|
||||
pos = __curPos;
|
||||
tests = [
|
||||
{
|
||||
name = "end-to-end configuration processing";
|
||||
type = "script";
|
||||
script = let
|
||||
config = {pkgs, ...}: {
|
||||
dependencies = [pkgs.curl];
|
||||
collections = {
|
||||
"community-general" = {
|
||||
version = "8.0.0";
|
||||
hash = "sha256-dNtdCxGj72LfMqPfzOpUSXLNLj1IkaAewRmHNizh67Q=";
|
||||
};
|
||||
};
|
||||
inventory = {
|
||||
test_group = {
|
||||
hosts = {
|
||||
test1 = {ansible_host = "localhost";};
|
||||
};
|
||||
vars = {
|
||||
test_var = "test_value";
|
||||
};
|
||||
};
|
||||
};
|
||||
playbook = [
|
||||
{
|
||||
name = "End-to-end test";
|
||||
hosts = "test_group";
|
||||
become = false;
|
||||
tasks = [
|
||||
{
|
||||
name = "Test task";
|
||||
debug = {
|
||||
msg = "Hello from {{ inventory_hostname }}";
|
||||
var = "test_var";
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
result = nblib.mkNixible config;
|
||||
cli = nblib.mkNixibleCli config;
|
||||
in
|
||||
# sh
|
||||
''
|
||||
${ntlib.helpers.path [pkgs.jq pkgs.gnugrep]}
|
||||
${ntlib.helpers.scriptHelpers}
|
||||
|
||||
# test that all components are generated
|
||||
assert "-f ${result.config.inventoryFile}" "should generate inventory file"
|
||||
assert "-f ${result.config.playbookFile}" "should generate playbook file"
|
||||
assert "-d ${result.config.installedCollections}" "should create collections directory"
|
||||
assert "-x ${cli}/bin/nixible" "should create CLI executable"
|
||||
|
||||
# test inventory content
|
||||
jq -e '.test_group.hosts.test1.ansible_host' "${result.config.inventoryFile}" | grep -q "localhost"
|
||||
assert_eq $? 0 "inventory should contain test host"
|
||||
|
||||
jq -e '.test_group.vars.test_var' "${result.config.inventoryFile}" | grep -q "test_value"
|
||||
assert_eq $? 0 "inventory should contain test variable"
|
||||
|
||||
# test playbook content
|
||||
assert_file_contains "${result.config.playbookFile}" "End-to-end test" "playbook should contain play name"
|
||||
assert_file_contains "${result.config.playbookFile}" "test_group" "playbook should target test_group"
|
||||
assert_file_contains "${result.config.playbookFile}" "Hello from" "playbook should contain debug message"
|
||||
'';
|
||||
}
|
||||
{
|
||||
name = "SOPS example configuration";
|
||||
type = "script";
|
||||
script = let
|
||||
# use the actual SOPS example from the repo
|
||||
sopsConfig = ../examples/sops.nix;
|
||||
result = nblib.mkNixible sopsConfig;
|
||||
cli = nblib.mkNixibleCli sopsConfig;
|
||||
in
|
||||
# sh
|
||||
''
|
||||
${ntlib.helpers.scriptHelpers}
|
||||
|
||||
assert "-f ${result.config.inventoryFile}" "SOPS example should generate inventory"
|
||||
assert "-f ${result.config.playbookFile}" "SOPS example should generate playbook"
|
||||
assert "-x ${cli}/bin/nixible" "SOPS example should generate CLI"
|
||||
|
||||
# test SOPS-specific content
|
||||
assert_file_contains "${result.config.playbookFile}" "community.crypto.openssl_privatekey_pipe" "should use crypto collection"
|
||||
assert_file_contains "${result.config.playbookFile}" "community.sops.sops_encrypt" "should use sops collection"
|
||||
assert_file_contains "${result.config.playbookFile}" "no_log: true" "should have no_log for security"
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
182
tests/lib_test.nix
Normal file
182
tests/lib_test.nix
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
{
|
||||
pkgs,
|
||||
nblib,
|
||||
ntlib,
|
||||
...
|
||||
}: {
|
||||
suites."Lib Tests" = {
|
||||
pos = __curPos;
|
||||
tests = [
|
||||
{
|
||||
name = "mkNixibleCli generates executable";
|
||||
type = "script";
|
||||
script = let
|
||||
config = {
|
||||
playbook = [
|
||||
{
|
||||
name = "Test CLI";
|
||||
hosts = "localhost";
|
||||
tasks = [
|
||||
{
|
||||
debug.msg = "Testing CLI generation";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
cli = nblib.mkNixibleCli config;
|
||||
in
|
||||
# sh
|
||||
''
|
||||
${ntlib.helpers.scriptHelpers}
|
||||
|
||||
# Check CLI contains expected content
|
||||
assert_file_contains "${cli}/bin/nixible" "ansible-playbook" "should contain ansible-playbook command"
|
||||
assert_file_contains "${cli}/bin/nixible" "ANSIBLE_COLLECTIONS_PATH" "should set collections path"
|
||||
'';
|
||||
}
|
||||
{
|
||||
name = "inventory JSON generation";
|
||||
type = "script";
|
||||
script = let
|
||||
config = {
|
||||
inventory = {
|
||||
webservers = {
|
||||
hosts = {
|
||||
web1 = {ansible_host = "192.168.1.10";};
|
||||
web2 = {ansible_host = "192.168.1.11";};
|
||||
};
|
||||
vars = {
|
||||
http_port = 80;
|
||||
};
|
||||
};
|
||||
};
|
||||
playbook = [
|
||||
{
|
||||
name = "Test inventory";
|
||||
hosts = "webservers";
|
||||
tasks = [];
|
||||
}
|
||||
];
|
||||
};
|
||||
result = nblib.mkNixible config;
|
||||
inventoryFile = result.config.inventoryFile;
|
||||
in
|
||||
# sh
|
||||
''
|
||||
${ntlib.helpers.path [pkgs.jq pkgs.gnugrep]}
|
||||
${ntlib.helpers.scriptHelpers}
|
||||
|
||||
# Check inventory file exists
|
||||
assert "-f ${inventoryFile}" "inventory file should exist"
|
||||
|
||||
# Check JSON structure
|
||||
jq -e '.webservers.hosts.web1.ansible_host' "${inventoryFile}" | grep -q "192.168.1.10"
|
||||
assert_eq $? 0 "should contain web1 host"
|
||||
|
||||
jq -e '.webservers.vars.http_port' "${inventoryFile}" | grep -q "80"
|
||||
assert_eq $? 0 "should contain http_port variable"
|
||||
'';
|
||||
}
|
||||
|
||||
{
|
||||
name = "playbook YAML generation";
|
||||
type = "script";
|
||||
script = let
|
||||
config = {
|
||||
playbook = [
|
||||
{
|
||||
name = "Test playbook generation";
|
||||
hosts = "localhost";
|
||||
become = true;
|
||||
tasks = [
|
||||
{
|
||||
name = "Install package";
|
||||
package = {
|
||||
name = "nginx";
|
||||
state = "present";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "Start service";
|
||||
service = {
|
||||
name = "nginx";
|
||||
state = "started";
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
result = nblib.mkNixible config;
|
||||
playbookFile = result.config.playbookFile;
|
||||
in
|
||||
# sh
|
||||
''
|
||||
${ntlib.helpers.scriptHelpers}
|
||||
|
||||
# Check playbook file exists
|
||||
assert "-f ${playbookFile}" "playbook file should exist"
|
||||
|
||||
# Check YAML structure
|
||||
assert_file_contains "${playbookFile}" "Test playbook generation" "should contain play name"
|
||||
assert_file_contains "${playbookFile}" "become: true" "should have become enabled"
|
||||
assert_file_contains "${playbookFile}" "Install package" "should contain first task"
|
||||
assert_file_contains "${playbookFile}" "nginx" "should contain nginx package"
|
||||
'';
|
||||
}
|
||||
{
|
||||
name = "ansible package is configurable";
|
||||
type = "script";
|
||||
script = let
|
||||
config = {pkgs, ...}: {
|
||||
ansiblePackage = pkgs.python3Packages.ansible;
|
||||
playbook = [
|
||||
{
|
||||
name = "Test custom ansible";
|
||||
hosts = "localhost";
|
||||
tasks = [];
|
||||
}
|
||||
];
|
||||
};
|
||||
cli = nblib.mkNixibleCli config;
|
||||
in
|
||||
# sh
|
||||
''
|
||||
${ntlib.helpers.scriptHelpers}
|
||||
|
||||
# check that custom ansible package is used
|
||||
assert_file_contains "${cli}/bin/nixible" "${pkgs.python3Packages.ansible}" "should use custom ansible package"
|
||||
'';
|
||||
}
|
||||
{
|
||||
name = "installed collections directory";
|
||||
type = "script";
|
||||
script = let
|
||||
config = {
|
||||
collections = {
|
||||
"amazon-aws" = {
|
||||
version = "10.1.0";
|
||||
hash = "sha256-w1wv0lYnuHXrpNubvePwKag4oM1k1I43HreFWYeIWgU=";
|
||||
};
|
||||
"community-aws" = {
|
||||
version = "10.0.0";
|
||||
hash = "sha256-oqsfmuztf8FLalwSDvRYcuvOVzLbWx/cEsYoUt8Dbn0=";
|
||||
};
|
||||
};
|
||||
};
|
||||
result = nblib.mkNixible config;
|
||||
collections = result.config.installedCollections;
|
||||
in
|
||||
# sh
|
||||
''
|
||||
${ntlib.helpers.scriptHelpers}
|
||||
|
||||
assert "-d ${collections}" "collections directory should exist"
|
||||
assert "-d ${collections}/ansible_collections/amazon/aws" "amazon/aws directory should exist"
|
||||
assert "-d ${collections}/ansible_collections/community/aws" "community/aws directory should exist"
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue