mirror of
https://gitlab.com/TECHNOFAB/soonix.git
synced 2025-12-11 22:00:05 +01:00
chore: initial commit
This commit is contained in:
commit
25cc087b1d
17 changed files with 741 additions and 0 deletions
2
.envrc
Normal file
2
.envrc
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
source $(fetchurl https://gitlab.com/rensa-nix/direnv/-/raw/v0.3.0/direnvrc "sha256-u7+KEz684NnIZ+Vh5x5qLrt8rKdnUNexewBoeTcEVHQ=")
|
||||||
|
use ren //repo/devShells/default
|
||||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
.ren/
|
||||||
|
result
|
||||||
|
|
||||||
|
# soonix
|
||||||
|
test.yaml
|
||||||
|
# end soonix
|
||||||
61
README.md
Normal file
61
README.md
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Soonix
|
||||||
|
|
||||||
|
Soonix helps you declaratively manage configuration files, build scripts, etc. using the Nix module system.
|
||||||
|
It minimizes configuration clutter and provides shell hooks for automatic file management.
|
||||||
|
|
||||||
|
Heavily based on and inspired by [Nixago](https://github.com/nix-community/nixago), thus the name (ago <-> soon, if that wasn't obvious).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Declarative Configuration**: Uses Nix modules for type-safe, declarative file management
|
||||||
|
- **Multiple Engines**: Support for JSON/YAML/TOML, templates, scripts, and more
|
||||||
|
- **Shell Hooks**: Automatic file management with status tracking
|
||||||
|
- **Flexible File Handling**: Choose between symlinks and copies based on your needs
|
||||||
|
- **GitIgnore Integration**: Automatic management of .gitignore entries
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
soonix.url = "gitlab:TECHNOFAB/soonix?dir=lib";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { nixpkgs, soonix, ... }: let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
|
shellHook = (soonix.lib { inherit pkgs; }).mkShellHook {
|
||||||
|
hooks = {
|
||||||
|
eslintrc = {
|
||||||
|
output = ".eslintrc.json";
|
||||||
|
generator = "nix";
|
||||||
|
data = { extends = ["eslint:recommended"]; };
|
||||||
|
opts = { format = "json"; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
|
packages = [ pkgs.jq ];
|
||||||
|
inherit shellHook;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use [rensa-nix/devshell](https://devshell.rensa.projects.tf), you can also
|
||||||
|
use the `devshellModule` for easy integration, see the docs for more.
|
||||||
|
|
||||||
|
## Available Engines
|
||||||
|
|
||||||
|
- **`nix`**: Convert Nix data to JSON, YAML, TOML, INI, XML formats
|
||||||
|
- **`string`**: Output raw string content with optional executable permissions
|
||||||
|
- **`derivation`**: Use existing Nix derivations as file content
|
||||||
|
- **`gotmpl`**: Advanced Go template rendering via gomplate
|
||||||
|
- **`jinja`**: Python Jinja2 template rendering
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
[Docs](https://soonix.projects.tf)
|
||||||
63
flake.lock
generated
Normal file
63
flake.lock
generated
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1755615617,
|
||||||
|
"narHash": "sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs+StOp19xNsbqdOg=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "20075955deac2583bb12f07151c2df830ef346b4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1754184128,
|
||||||
|
"narHash": "sha256-AjhoyBL4eSyXf01Bmc6DiuaMrJRNdWopmdnMY0Pa/M0=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"rev": "02e72200e6d56494f4a7c0da8118760736e41b60",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ren": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"dir": "lib",
|
||||||
|
"lastModified": 1755264589,
|
||||||
|
"narHash": "sha256-g8KjU4D/nxpMjCLQNP90VAAWUH89yvONRfChyhhzq4c=",
|
||||||
|
"owner": "rensa-nix",
|
||||||
|
"repo": "core",
|
||||||
|
"rev": "9f20f8c94b09a1c85356f8340ebe0a339b0d32e6",
|
||||||
|
"type": "gitlab"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "lib",
|
||||||
|
"owner": "rensa-nix",
|
||||||
|
"repo": "core",
|
||||||
|
"type": "gitlab"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"ren": "ren"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
31
flake.nix
Normal file
31
flake.nix
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
ren.url = "gitlab:rensa-nix/core?dir=lib";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = {
|
||||||
|
self,
|
||||||
|
ren,
|
||||||
|
...
|
||||||
|
} @ inputs:
|
||||||
|
ren.buildWith
|
||||||
|
{
|
||||||
|
inherit inputs;
|
||||||
|
cellsFrom = ./nix;
|
||||||
|
transformInputs = system: i:
|
||||||
|
i
|
||||||
|
// {
|
||||||
|
pkgs = import i.nixpkgs {inherit system;};
|
||||||
|
};
|
||||||
|
cellBlocks = with ren.blocks; [
|
||||||
|
(simple "devShells")
|
||||||
|
(simple "tests")
|
||||||
|
];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
packages = ren.select self [
|
||||||
|
["repo" "tests"]
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
25
lib/default.nix
Normal file
25
lib/default.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib ? pkgs.lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit (lib) evalModules;
|
||||||
|
|
||||||
|
soonix_lib = import ./lib.nix {inherit pkgs lib;};
|
||||||
|
in rec {
|
||||||
|
inherit (soonix_lib) engines buildAllFiles;
|
||||||
|
|
||||||
|
module = ./module.nix;
|
||||||
|
devshellModule = ./devshellModule.nix;
|
||||||
|
|
||||||
|
make = userConfig:
|
||||||
|
evalModules {
|
||||||
|
specialArgs = {inherit pkgs;};
|
||||||
|
modules = [
|
||||||
|
module
|
||||||
|
userConfig
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
mkShellHook = userConfig: (make userConfig).config.shellHook;
|
||||||
|
}
|
||||||
23
lib/devshellModule.nix
Normal file
23
lib/devshellModule.nix
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit (lib) mkOption types;
|
||||||
|
soonixModule = ./module.nix;
|
||||||
|
in {
|
||||||
|
options.soonix = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
# propagate pkgs to the soonix module
|
||||||
|
_module.args.pkgs = pkgs;
|
||||||
|
imports = [soonixModule];
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
config.enterShellCommands.soonix = {
|
||||||
|
text = config.soonix.shellHook;
|
||||||
|
deps = ["env"];
|
||||||
|
};
|
||||||
|
}
|
||||||
6
lib/flake.nix
Normal file
6
lib/flake.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
outputs = i: {
|
||||||
|
lib = import ./.;
|
||||||
|
devshellModule = ./devshellModule.nix;
|
||||||
|
};
|
||||||
|
}
|
||||||
101
lib/lib.nix
Normal file
101
lib/lib.nix
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit (pkgs) writeText writeTextFile runCommand formats;
|
||||||
|
|
||||||
|
engines = {
|
||||||
|
nix = {
|
||||||
|
name,
|
||||||
|
opts,
|
||||||
|
data,
|
||||||
|
}: let
|
||||||
|
format = opts.format or "json";
|
||||||
|
formatOpts = builtins.removeAttrs opts ["format"];
|
||||||
|
formatter = formats.${format} formatOpts;
|
||||||
|
in
|
||||||
|
formatter.generate name data;
|
||||||
|
|
||||||
|
string = {
|
||||||
|
name,
|
||||||
|
opts,
|
||||||
|
data,
|
||||||
|
}: let
|
||||||
|
executable = opts.executable or false;
|
||||||
|
in
|
||||||
|
writeTextFile {
|
||||||
|
inherit name executable;
|
||||||
|
text = data;
|
||||||
|
};
|
||||||
|
|
||||||
|
# only a passthru
|
||||||
|
derivation = {data, ...}: data;
|
||||||
|
|
||||||
|
gotmpl = {
|
||||||
|
name,
|
||||||
|
opts,
|
||||||
|
data,
|
||||||
|
}: let
|
||||||
|
inherit (opts) template;
|
||||||
|
gomplate = opts.gomplate or pkgs.gomplate;
|
||||||
|
in
|
||||||
|
runCommand name {
|
||||||
|
buildInputs = [gomplate];
|
||||||
|
passAsFile = ["dataJson"];
|
||||||
|
dataJson = builtins.toJSON data;
|
||||||
|
} ''
|
||||||
|
gomplate \
|
||||||
|
-c ".=$dataJsonPath?type=application/json" \
|
||||||
|
-f "${template}" \
|
||||||
|
-o "$out"
|
||||||
|
'';
|
||||||
|
|
||||||
|
jinja = {
|
||||||
|
name,
|
||||||
|
opts,
|
||||||
|
data,
|
||||||
|
}: let
|
||||||
|
inherit (opts) template;
|
||||||
|
python = opts.python or pkgs.python3;
|
||||||
|
dataJson = writeText "template-data.json" (builtins.toJSON data);
|
||||||
|
renderScript =
|
||||||
|
writeText "render.py"
|
||||||
|
# py
|
||||||
|
''
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from jinja2 import Template
|
||||||
|
|
||||||
|
with open('${dataJson}', 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
with open('${template}', 'r') as f:
|
||||||
|
template_str = f.read()
|
||||||
|
|
||||||
|
template = Template(template_str)
|
||||||
|
print(template.render(**data))
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
runCommand name {
|
||||||
|
buildInputs = [python python.pkgs.jinja2];
|
||||||
|
} ''
|
||||||
|
python ${renderScript} > $out
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
buildAllFiles = files:
|
||||||
|
runCommand "soonix-files" {} ''
|
||||||
|
mkdir -p $out
|
||||||
|
${lib.concatMapStringsSep "\n" (file:
|
||||||
|
# sh
|
||||||
|
''
|
||||||
|
target_dir="$out/$(dirname "${file.path}")"
|
||||||
|
mkdir -p "$target_dir"
|
||||||
|
ln -sf "${file.src}" "$out/${file.path}"
|
||||||
|
'')
|
||||||
|
files}
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
inherit engines buildAllFiles;
|
||||||
|
}
|
||||||
256
lib/module.nix
Normal file
256
lib/module.nix
Normal file
|
|
@ -0,0 +1,256 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit (lib) types mkOption concatMapStringsSep;
|
||||||
|
soonix_lib = import ./. {inherit pkgs;};
|
||||||
|
inherit (soonix_lib) engines buildAllFiles;
|
||||||
|
in {
|
||||||
|
options = {
|
||||||
|
hooks = mkOption {
|
||||||
|
type = types.attrsOf (types.submodule ({
|
||||||
|
name,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
internal = true;
|
||||||
|
default = name;
|
||||||
|
};
|
||||||
|
|
||||||
|
output = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "The relative path where the generated file should be placed";
|
||||||
|
};
|
||||||
|
|
||||||
|
generator = mkOption {
|
||||||
|
type = types.enum ["nix" "string" "derivation" "gotmpl" "jinja" "template"];
|
||||||
|
description = "Which engine to use for content generation";
|
||||||
|
};
|
||||||
|
|
||||||
|
data = mkOption {
|
||||||
|
type = types.anything;
|
||||||
|
description = "The input data for the chosen generator";
|
||||||
|
};
|
||||||
|
|
||||||
|
opts = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
default = {};
|
||||||
|
description = "Generator-specific options";
|
||||||
|
};
|
||||||
|
|
||||||
|
hook = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
mode = mkOption {
|
||||||
|
type = types.enum ["link" "copy"];
|
||||||
|
default = "link";
|
||||||
|
description = "How the file should be managed (link or copy)";
|
||||||
|
};
|
||||||
|
|
||||||
|
gitignore = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Whether to add the output path to .gitignore";
|
||||||
|
};
|
||||||
|
|
||||||
|
extra = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
description = "Additional bash commands to execute after file operation";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
description = "Hook-specific options for file management";
|
||||||
|
};
|
||||||
|
|
||||||
|
generatedDerivation = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
internal = true;
|
||||||
|
readOnly = true;
|
||||||
|
description = "The generated derivation for this file";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
generatedDerivation =
|
||||||
|
(engines.${config.generator} or (throw "Generator ${config.generator} not found"))
|
||||||
|
{
|
||||||
|
inherit (config) opts data name;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
default = {};
|
||||||
|
description = "Configuration hooks for file generation and management";
|
||||||
|
};
|
||||||
|
|
||||||
|
shellHook = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
readOnly = true;
|
||||||
|
description = "Generated shell hook script for managing all files";
|
||||||
|
};
|
||||||
|
shellHookFile = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
readOnly = true;
|
||||||
|
description = "Generated shell hook script for managing all files";
|
||||||
|
};
|
||||||
|
|
||||||
|
finalFiles = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
readOnly = true;
|
||||||
|
description = "Aggregated derivation containing all managed files";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = let
|
||||||
|
hooks = config.hooks;
|
||||||
|
hookNames = builtins.attrNames hooks;
|
||||||
|
|
||||||
|
runHooks = concatMapStringsSep "\n" (hookName: let
|
||||||
|
hook = hooks.${hookName};
|
||||||
|
modes = {
|
||||||
|
link =
|
||||||
|
# sh
|
||||||
|
''
|
||||||
|
if [[ ! -L "${hook.output}" ]] || [[ "$(readlink "${hook.output}")" != "${hook.generatedDerivation}" ]]; then
|
||||||
|
_soonix_log "info" "${hookName}" "Creating symlink: ${hook.output} -> ${hook.generatedDerivation}"
|
||||||
|
mkdir -p "$(dirname "${hook.output}")"
|
||||||
|
ln -sf "${hook.generatedDerivation}" "${hook.output}"
|
||||||
|
_changed=true
|
||||||
|
else
|
||||||
|
_soonix_log "info" "${hookName}" "Symlink up to date: ${hook.output}"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
copy =
|
||||||
|
# sh
|
||||||
|
''
|
||||||
|
if [[ ! -f "${hook.output}" ]] || ! cmp -s "${hook.generatedDerivation}" "${hook.output}"; then
|
||||||
|
_soonix_log "info" "${hookName}" "Copying file: ${hook.generatedDerivation} -> ${hook.output}"
|
||||||
|
mkdir -p "$(dirname "${hook.output}")"
|
||||||
|
cp "${hook.generatedDerivation}" "${hook.output}"
|
||||||
|
_changed=true
|
||||||
|
else
|
||||||
|
_soonix_log "info" "${hookName}" "File up to date: ${hook.output}"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
optionalGitignore =
|
||||||
|
if hook.hook.gitignore
|
||||||
|
then ''
|
||||||
|
_soonix_add_to_gitignore "${hook.output}"
|
||||||
|
''
|
||||||
|
else "";
|
||||||
|
in
|
||||||
|
# sh
|
||||||
|
''
|
||||||
|
# Process hook: ${hookName}
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
UPDATED) _soonix_updated+=("${hookName}") ;;
|
||||||
|
UPTODATE) _soonix_uptodate+=("${hookName}") ;;
|
||||||
|
*) echo "$line" ;;
|
||||||
|
esac
|
||||||
|
done < <(
|
||||||
|
set -euo pipefail
|
||||||
|
_changed=false
|
||||||
|
|
||||||
|
${modes.${hook.hook.mode} or (throw "Mode ${hook.hook.mode} doesnt exist")}
|
||||||
|
|
||||||
|
# Add to gitignore if requested
|
||||||
|
${optionalGitignore}
|
||||||
|
|
||||||
|
# Run extra commands if file changed
|
||||||
|
if [[ "$_changed" == "true" && -n "${hook.hook.extra}" ]]; then
|
||||||
|
_soonix_log "info" "${hookName}" "Running extra command: ${hook.hook.extra}"
|
||||||
|
eval "${hook.hook.extra}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$_changed" == "true" ]]; then
|
||||||
|
echo "UPDATED"
|
||||||
|
else
|
||||||
|
echo "UPTODATE"
|
||||||
|
fi
|
||||||
|
) || {
|
||||||
|
_soonix_log "error" "${hookName}" "Failed to process hook"
|
||||||
|
_soonix_failed+=("${hookName}")
|
||||||
|
}
|
||||||
|
'')
|
||||||
|
hookNames;
|
||||||
|
|
||||||
|
generatedShellHook =
|
||||||
|
# sh
|
||||||
|
''
|
||||||
|
_soonix_log() {
|
||||||
|
local level="$1"
|
||||||
|
local hook="$2"
|
||||||
|
local message="$3"
|
||||||
|
[[ "''${SOONIX_LOG-}" == "true" ]] && echo "$level [$hook]: $message" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
_soonix_add_to_gitignore() {
|
||||||
|
local file="$1"
|
||||||
|
local gitignore=".gitignore"
|
||||||
|
|
||||||
|
if [[ ! -f "$gitignore" ]]; then
|
||||||
|
touch "$gitignore"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if file is already in gitignore
|
||||||
|
if ! grep -Fxq "$file" "$gitignore"; then
|
||||||
|
# Add sentinel comments if not present
|
||||||
|
if ! grep -q "# soonix" "$gitignore"; then
|
||||||
|
echo "" >> "$gitignore"
|
||||||
|
echo "# soonix" >> "$gitignore"
|
||||||
|
echo "# end soonix" >> "$gitignore"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Insert the file path before the end comment
|
||||||
|
${pkgs.gnused}/bin/sed -i "/# end soonix/i $file" "$gitignore"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_soonix_updated=()
|
||||||
|
_soonix_failed=()
|
||||||
|
_soonix_uptodate=()
|
||||||
|
|
||||||
|
${runHooks}
|
||||||
|
|
||||||
|
echo -n "[soonix] " >&2
|
||||||
|
if [[ ''${#_soonix_updated[@]} -gt 0 ]]; then
|
||||||
|
echo -n "[updated: ''${_soonix_updated[*]}] " >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ''${#_soonix_uptodate[@]} -gt 0 ]]; then
|
||||||
|
echo -n "[unchanged: ''${_soonix_uptodate[*]}] " >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ''${#_soonix_failed[@]} -gt 0 ]]; then
|
||||||
|
echo "[failed: ''${_soonix_failed[*]}]" >&2
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
|
allFiles =
|
||||||
|
lib.mapAttrsToList (name: hook: {
|
||||||
|
src = hook.generatedDerivation;
|
||||||
|
path = hook.output;
|
||||||
|
})
|
||||||
|
hooks;
|
||||||
|
in rec {
|
||||||
|
# nothing to do if no hooks exist
|
||||||
|
shellHook =
|
||||||
|
if (builtins.length hookNames > 0)
|
||||||
|
then generatedShellHook
|
||||||
|
else "";
|
||||||
|
shellHookFile = pkgs.writeShellScript "shellHook" shellHook;
|
||||||
|
finalFiles = buildAllFiles allFiles;
|
||||||
|
};
|
||||||
|
}
|
||||||
25
nix/repo/devShells.nix
Normal file
25
nix/repo/devShells.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{inputs, ...}: let
|
||||||
|
inherit (inputs) pkgs devshell soonix;
|
||||||
|
in {
|
||||||
|
default = devshell.mkShell {
|
||||||
|
imports = [soonix.devshellModule];
|
||||||
|
packages = [
|
||||||
|
pkgs.alejandra
|
||||||
|
pkgs.nil
|
||||||
|
];
|
||||||
|
|
||||||
|
soonix.hooks.test = {
|
||||||
|
output = "test.yaml";
|
||||||
|
generator = "nix";
|
||||||
|
data = {
|
||||||
|
name = "soonix-test";
|
||||||
|
version = "1.0.0";
|
||||||
|
};
|
||||||
|
opts.format = "yaml";
|
||||||
|
hook = {
|
||||||
|
mode = "copy";
|
||||||
|
gitignore = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
46
nix/repo/flake.lock
generated
Normal file
46
nix/repo/flake.lock
generated
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"devshell-lib": {
|
||||||
|
"locked": {
|
||||||
|
"dir": "lib",
|
||||||
|
"lastModified": 1755673398,
|
||||||
|
"narHash": "sha256-51MmR+Eo1+bKDd/Ss77wwTqi4yAR2xgmyCSEbKWSpj0=",
|
||||||
|
"owner": "rensa-nix",
|
||||||
|
"repo": "devshell",
|
||||||
|
"rev": "e76bef387e8a4574f9b6d37b1a424e706491af08",
|
||||||
|
"type": "gitlab"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "lib",
|
||||||
|
"owner": "rensa-nix",
|
||||||
|
"repo": "devshell",
|
||||||
|
"type": "gitlab"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixtest-lib": {
|
||||||
|
"locked": {
|
||||||
|
"dir": "lib",
|
||||||
|
"lastModified": 1753957623,
|
||||||
|
"narHash": "sha256-kdImwKx57N0QL8HPUUb5ADwXFgSjaNOk39b/eKlzyTo=",
|
||||||
|
"owner": "TECHNOFAB",
|
||||||
|
"repo": "nixtest",
|
||||||
|
"rev": "22b43c9fe83be73c3f0648bbb54bc3c1cf7f96df",
|
||||||
|
"type": "gitlab"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "lib",
|
||||||
|
"owner": "TECHNOFAB",
|
||||||
|
"repo": "nixtest",
|
||||||
|
"type": "gitlab"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"devshell-lib": "devshell-lib",
|
||||||
|
"nixtest-lib": "nixtest-lib"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
13
nix/repo/flake.nix
Normal file
13
nix/repo/flake.nix
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
devshell-lib.url = "gitlab:rensa-nix/devshell?dir=lib";
|
||||||
|
nixtest-lib.url = "gitlab:TECHNOFAB/nixtest?dir=lib";
|
||||||
|
};
|
||||||
|
outputs = i:
|
||||||
|
i
|
||||||
|
// {
|
||||||
|
ntlib = i.nixtest-lib.lib {inherit (i.parent) pkgs;};
|
||||||
|
devshell = i.devshell-lib.lib {inherit (i.parent) pkgs;};
|
||||||
|
soonix = import "${i.parent.self}/lib" {inherit (i.parent) pkgs;};
|
||||||
|
};
|
||||||
|
}
|
||||||
10
nix/repo/tests.nix
Normal file
10
nix/repo/tests.nix
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{inputs, ...}: let
|
||||||
|
inherit (inputs) pkgs ntlib soonix;
|
||||||
|
in {
|
||||||
|
tests = ntlib.mkNixtest {
|
||||||
|
modules = ntlib.autodiscover {dir = "${inputs.self}/tests";};
|
||||||
|
args = {
|
||||||
|
inherit ntlib soonix pkgs;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
1
tests/fixtures/gotmpl_template
vendored
Normal file
1
tests/fixtures/gotmpl_template
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Hello {{ .hello }}
|
||||||
1
tests/fixtures/jinja_template
vendored
Normal file
1
tests/fixtures/jinja_template
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Hello {{hello}}
|
||||||
71
tests/soonix_test.nix
Normal file
71
tests/soonix_test.nix
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
{
|
||||||
|
ntlib,
|
||||||
|
soonix,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
hooks = {
|
||||||
|
test = {
|
||||||
|
output = "out/test.json";
|
||||||
|
generator = "nix";
|
||||||
|
data = {
|
||||||
|
name = "soonix-test";
|
||||||
|
version = "1.0.0";
|
||||||
|
};
|
||||||
|
opts.format = "json";
|
||||||
|
hook = {
|
||||||
|
mode = "copy";
|
||||||
|
gitignore = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
gomplate = {
|
||||||
|
output = "gotmpl";
|
||||||
|
generator = "gotmpl";
|
||||||
|
data.hello = "world";
|
||||||
|
opts.template = ./fixtures/gotmpl_template;
|
||||||
|
};
|
||||||
|
jinja = {
|
||||||
|
output = "jinja";
|
||||||
|
generator = "jinja";
|
||||||
|
data.hello = "world";
|
||||||
|
opts.template = ./fixtures/jinja_template;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
suites."Soonix Tests" = {
|
||||||
|
pos = __curPos;
|
||||||
|
tests = [
|
||||||
|
{
|
||||||
|
name = "files get generated correctly";
|
||||||
|
type = "script";
|
||||||
|
script = let
|
||||||
|
finalFiles = (soonix.make {inherit hooks;}).config.finalFiles;
|
||||||
|
in
|
||||||
|
# sh
|
||||||
|
''
|
||||||
|
${ntlib.helpers.scriptHelpers}
|
||||||
|
assert "-f ${finalFiles}/out/test.json" "should exist"
|
||||||
|
assert_file_contains ${finalFiles}/out/test.json "soonix-test"
|
||||||
|
|
||||||
|
assert "-f ${finalFiles}/gotmpl" "should exist"
|
||||||
|
assert_file_contains ${finalFiles}/gotmpl "Hello world"
|
||||||
|
|
||||||
|
assert "-f ${finalFiles}/jinja" "should exist"
|
||||||
|
assert_file_contains ${finalFiles}/jinja "Hello world"
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "shell hook";
|
||||||
|
type = "script";
|
||||||
|
script = let
|
||||||
|
shellHook = ntlib.helpers.toPrettyFile (soonix.mkShellHook {inherit hooks;});
|
||||||
|
in
|
||||||
|
# sh
|
||||||
|
''
|
||||||
|
${ntlib.helpers.scriptHelpers}
|
||||||
|
assert "-f ${shellHook}" "should exist"
|
||||||
|
assert_file_contains ${shellHook} "gomplate"
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue