Merge branch 'feat/cli' into 'main'

feat!: add cli with update and list subcommands + gitignore param

See merge request TECHNOFAB/soonix!3
This commit is contained in:
TECHNOFAB 2026-01-05 22:24:28 +01:00
commit 6693b7cab8
5 changed files with 202 additions and 82 deletions

View file

@ -16,7 +16,7 @@
},
"postUpgradeTasks": {
"commands": [
"nix-portable nix run .#soonix:update"
"nix-portable nix run .#soonix -- update --skip-gitignore"
]
}
}

View file

@ -146,16 +146,24 @@ in {
Packages for updating the hooks without a devshell. (readonly)
'';
example = {
"soonix:update" = "<derivation>";
"soonix" = "<derivation>";
};
};
};
config = let
hooks = config.hooks;
hookNames = builtins.attrNames hooks;
# allow excluding gitignore since stuff like renovate can't use/commit it anyways
getHookNames = {includeGitignored ? true}:
if includeGitignored
then (builtins.attrNames hooks)
else
map
(hook: hook.name)
(builtins.filter (hook: !hook.hook.gitignore) (builtins.attrValues hooks));
runHooks = concatMapStringsSep "\n" (hookName: let
runHooks = args:
concatMapStringsSep "\n" (hookName: let
hook = hooks.${hookName};
modes = {
link =
@ -193,6 +201,7 @@ in {
''
else "";
in
builtins.addErrorContext "[soonix] while generating script for ${hookName}"
# sh
''
# Process hook: ${hookName}
@ -227,9 +236,10 @@ in {
_soonix_failed+=("${hookName}")
}
'')
hookNames;
(getHookNames args);
generatedShellHook =
generateShellHook = args:
builtins.addErrorContext "[soonix] while generating shell hook"
# sh
''
_soonix_log() {
@ -265,7 +275,7 @@ in {
_soonix_failed=()
_soonix_uptodate=()
${runHooks}
${runHooks args}
local output=$'\E[msoonix:\E[38;5;8m'
local status=0
@ -297,8 +307,8 @@ in {
in rec {
# nothing to do if no hooks exist
shellHook =
if (builtins.length hookNames > 0)
then generatedShellHook
if (builtins.length (builtins.attrNames config.hooks) > 0)
then generateShellHook {}
else "";
shellHookFile = pkgs.writeShellScript "shellHook" shellHook;
devshellModule = {
@ -307,11 +317,99 @@ in {
};
finalFiles = buildAllFiles allFiles;
# make it simpler to update the hooks without any devshell
packages."soonix:update" = pkgs.writeShellScriptBin "soonix:update" ''
packages = {
soonix =
pkgs.writeShellScriptBin "soonix"
# sh
''
set -euo pipefail
SKIP_GITIGNORE=false
COMMAND=""
show_help() {
cat << EOF
soonix - Declarative file management tool
USAGE:
soonix <COMMAND> [OPTIONS]
COMMANDS:
update Update all managed files
list List all managed file targets
help Show this help message
OPTIONS:
--skip-gitignore Skip files that would be added to .gitignore
EXAMPLES:
soonix update # Update all files
soonix update --skip-gitignore # Update only non-gitignored files
soonix list # List all targets
soonix list --skip-gitignore # List only non-gitignored targets
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
update|list|help)
COMMAND="$1"
shift
;;
--skip-gitignore)
SKIP_GITIGNORE=true
shift
;;
*)
echo "Error: Unknown argument '$1'" >&2
echo ""
show_help
exit 1
;;
esac
done
if [[ -z "$COMMAND" ]]; then
echo "Error: No command specified" >&2
echo ""
show_help
exit 1
fi
case "$COMMAND" in
help)
show_help
;;
list)
${concatMapStringsSep "\n" (hookName: let
hook = hooks.${hookName};
in ''
if [[ "$SKIP_GITIGNORE" == "true" && "${
if hook.hook.gitignore
then "true"
else "false"
}" == "true" ]]; then
: # skip
else
echo "${hook.output}"
fi
'') (builtins.attrNames hooks)}
;;
update)
if [[ "$SKIP_GITIGNORE" == "true" ]]; then
function _soonix() {
${shellHook}
${generateShellHook {includeGitignored = false;}}
}
_soonix
else
function _soonix() {
${generateShellHook {}}
}
_soonix
fi
;;
esac
'';
};
};
}

View file

@ -41,8 +41,10 @@ in {
}
{
name = "soonix";
files = "nix run .#soonix -- list --skip-gitignore";
stage_fixed = true;
run = "nix run .#soonix:update";
# {files} is needed so lefthook git add's them
run = "nix run .#soonix -- update --skip-gitignore; #{files}";
}
];
};

View file

@ -14,7 +14,7 @@ in
data = {
extends = ["config:recommended"];
postUpgradeTasks.commands = [
"nix-portable nix run .#soonix:update"
"nix-portable nix run .#soonix -- update --skip-gitignore"
];
lockFileMaintenance = {
enabled = true;

View file

@ -69,6 +69,26 @@ in {
assert_file_contains ${shellHook} "gomplate"
'';
}
{
name = "packages";
type = "script";
script = let
conf = (soonix.make {inherit hooks;}).config;
soonixBin = conf.packages.soonix + "/bin/soonix";
in
# sh
''
${ntlib.helpers.path [pkgs.gnugrep]}
${ntlib.helpers.scriptHelpers}
assert -f "${soonixBin}" "should exist"
assert_file_contains "${soonixBin}" "gotmpl"
assert_file_contains "${soonixBin}" "test.json"
assert_file_contains "${soonixBin}" "SKIP_GITIGNORE"
'';
}
];
};
}