Files
config/justfile
2025-07-15 22:43:24 +02:00

360 lines
15 KiB
Makefile

set positional-arguments # makes variables accesible as $1 $2 $@
export invokedir := invocation_directory()
export GUM_CHOOSE_HEIGHT := "15"
export GUM_FILTER_HEIGHT := "15"
export FZF_DEFAULT_OPTS := "--height 15 --cycle --bind 'ctrl-a:toggle-all' " + env('FZF_DEFAULT_OPTS', "")
#export hosts_nixos := `2>/dev/null nix eval .\#nixosConfigurations --apply builtins.attrNames --json`
#export hosts_fhs := `2>/dev/null nix eval .\#systemConfigs --apply builtins.attrNames --json`
@_default:
just "$(gum filter --placeholder "Pick a recipie..." $(just --summary --unsorted))"
eval $hostname=`just _a_host` $attrpath="system.build.toplevel.outPath" *args:
nix eval --log-format raw ".#nixosConfigurations.\"$hostname\".config.$attrpath" --show-trace "${@:3}"
eval-vm $hostname=`just _a_host` $attrpath="system.build.toplevel.outPath" *args:
# nix eval --log-format raw ".#nixosConfigurations.\"$hostname\".config.system.build.vm.outPath" --show-trace "${@:3}"
nix eval --log-format raw ".#nixosConfigurations.\"$hostname\".config.virtualisation.vmVariant.$attrpath" --show-trace "${@:3}"
repl $hostname=`just _a_host`:
NIX_NO_NOM=1 nixos-rebuild-ng --flake .#"$hostname" repl
report hostname=`just _a_host`:
nix eval .#nixosReports."{{hostname}}" --json --show-trace | yq . --yaml-output --width=999999 | bat --language yaml --style plain --paging never
reports:
nix eval .#nixosReports --json --show-trace | yq . --yaml-output --width=999999 | bat --language yaml --style plain --paging never
update:
git reset flake.lock
git restore flake.lock
git pull --rebase --autostash
nix flake update --commit-lock-file
@mkdir -p .direnv/
@printf "%s\n" "$(date +%Y-%m-%d)" > .direnv/pull-date
@update-select:
git reset flake.lock
git restore flake.lock
nix eval --file flake.nix --apply 'x: builtins.attrNames x.inputs' --json \
| (printf "%s\n" --commit-lock-file; jq '.[]' -r | tr '0123456789' '9876543210' | sort | tr '0123456789' '9876543210' | grep -v "^self$") \
| gum choose --no-limit \
| xargs nix flake update
git add flake.lock
pull-dconf:
./users/pbsds/home/profiles/desktop/gnome/dconf-pull.sh
git diff ./users/pbsds/home/profiles/desktop/gnome/
-gum confirm "git add --patch?" --default=no \
&& git add --patch ./users/pbsds/home/profiles/desktop/gnome/
-gum confirm "git checkout --path?" --default=no \
&& git checkout --patch ./users/pbsds/home/profiles/desktop/gnome/
# todo: support system-manager
# todo: support home-manager?
build hostname="" *_:
nixos-rebuild-ng build --accept-flake-config --show-trace --flake .#"{{hostname}}" "${@:2}"
build-vm hostname=`just _a_host` *_:
nixos-rebuild-ng build-vm --accept-flake-config --show-trace --flake .#"{{hostname}}" "${@:2}"
build-vm-and-run $hostname=`just _a_host` *_:
@# TODO: setup ports?
@# TODO: attach serial instead of gui?
@# TODO: headless and ssh?
@just build-vm "$hostname" "${@:2}"
./result/bin/run-"$hostname"-vm
# build-home $user=`whoami`:
# #!/usr/bin/env -S bash -euo pipefail
test *_:
sudo nixos-rebuild-ng test --accept-flake-config --show-trace --flake . "$@"
switch *_:
sudo nixos-rebuild-ng switch --accept-flake-config --show-trace --flake . "$@"
boot *_:
sudo nixos-rebuild-ng boot --accept-flake-config --show-trace --flake . "$@"
boot-and-reboot *_:
sudo nixos-rebuild-ng boot --accept-flake-config --show-trace --flake . "$@"
sudo reboot
# TODO: nixos-rebuild-ng
push $hostname=`just _a_host` cmd=`gum choose test switch boot --header "Select mode..."`:
#!/usr/bin/env -S bash -euo pipefail
remote_sudo=1
gum confirm "Use '--use-remote-sudo'?" || remote_sudo=0
# build once first for nom output
(set -x; nixos-rebuild build --flake .#"$hostname") || exit $?
# target_host="root@$(nix eval --log-format raw ".#nixosConfigurations.\"$hostname\".config.networking.fqdn" --json | jq . -r)"
target_host="$hostname"
printf "%s\n" pushing...
# nixos-rebuild will perform a build still, meaning another full eval...
# TODO: support a simple copy-closure with no activation on remote
if [[ "$remote_sudo" -eq 1 ]]; then
# set -x; NIX_NO_NOM=1 NIX_SSHOPTS="-t" nixos-rebuild {{cmd}} --flake .#"$hostname" --target-host "$target_host" --use-substitutes --use-remote-sudo
set -x; NIX_NO_NOM=1 IX_SSHOPTS="-o RequestTTY=force" nixos-rebuild {{cmd}} --flake .#"$hostname" --target-host "$target_host" --use-substitutes --use-remote-sudo
else
set -x; NIX_NO_NOM=1 nixos-rebuild {{cmd}} --flake .#"$hostname" --target-host "root@$target_host" --use-substitutes
fi
# test-files $hostname=`just _a_host` *filenames:
# #!/usr/bin/env -S bash -euo pipefail
# # TODO: remember previous choices
# [[ -n "$hostname" ]] || { printf >&2 "%s\n" "ERROR: no hostname"; false; }
# declare -a filenames=("${@:2}")
# if [[ "${#filenames[@]}" -eq 0 ]]; then
# etcfiles=$(
# nix eval ".#nixosConfigurations.\"$hostname\".config.environment.etc" --json \
# --apply 'builtins.mapAttrs (k: v: { inherit (v) enable target mode; })'
# )
# homekeys=$(
# nix eval ".#nixosConfigurations.\"$hostname\".config.home-manager.users" --json \
# --apply 'builtins.mapAttrs (user: config: { inherit (config.home) homeDirectory; files = builtins.mapAttrs (k: v: { inherit (v) enable target recursive; }) config.home.file; } )'
# # | jq 'to_entries[]| .key as $user | .value.homeDirectory as $homeDirectory | .value.files | to_entries[] | select(.value) | [ "config.home-manager.users.\"\($user)\".home.file.\"\(.key)\"", $homeDirectory + "/" + .value.target, .value.target, .key, $user, $homeDirectory] | @tsv' -r
# )
# filepathindex=$(
# jq --null-input -c \
# --arg hostname "$hostname" \
# --argjson etcfiles "$etcfiles" \
# --argjson homekeys "$homekeys" \
# '{
# "nixosConfigurations.\"\($hostname)\".config.environment.etc": (
# $etcfiles | with_entries( select(.value.enable) | { key, value: (.value + { target : ("/etc/" + .value.target) }) } )
# )
# } + (
# $homekeys | with_entries(
# .key as $user |
# .value.homeDirectory as $homeDirectory |
# {
# key: ("nixosConfigurations.\"\($hostname)\".config.home-manager.users.\"\($user)\".home.file"),
# value: (
# .value.files | with_entries(
# select(.value.enable) | {
# key,
# value: (.value + {
# target: ($homeDirectory + "/" + .value.target),
# }),
# }
# )
# ),
# }
# )
# )'
# )
# # cache
# if [[ "$UID" -ne 0 ]]; then
# mkdir -p .direnv/
# cat <<<"$filepathindex" >.direnv/just-cache-test-files-"$hostname".json
# fi
# else
# [[ -s .direnv/just-cache-test-files-"$hostname".json ]] || { printf >&2 "%s\n" "ERROR: no cache found"; false; }
# filepathindex=$(cat .direnv/just-cache-test-files-"$hostname".json)
# fi
# if [[ "${#filenames[@]}" -eq 0 ]]; then
# filenames=$(
# set +e
# jq <<<"$filepathindex" 'to_entries[] | .value | to_entries[] | .value' -r \
# | fzf --height=50% --reverse --multi -d$'\t' --with-nth 2
# )
# [[ -n "$selection" ]]
# fi
# # jq <<<"$filepathindex" . ; false
# filepathindex_inverse=$(
# jq <<<"$filepathindex" 'with_entries( .key as $attr | .value | to_entries[] | {key: .value.target, value: (.value + {attr: $attr, key})} )' -c
# )
# # jq <<<"$filepathindex_inverse" . ; false
# # jq <<<"$filepathindex" 'to_entries[] | .key as $attr | .value | to_entries[] | @sh "\(.value) \($attr).\("\""+.key+"\"")"' -r
# nix_expr=$(
# printf "%s\n" 'pkgs.writeScript "do" ('
# printf '"%s\\n" +\n' '#!${pkgs.runtimeShell}'
# printf '"%s\\n" +\n' 'set -euo pipefail'
# printf '"%s\\n" +\n' 'sudo : && SUDO=sudo || SUDO=:'
# printf "%s\n" "${filenames[@]}" |
# jq -sR --argjson index "$filepathindex_inverse" '
# split("\n") | map(select(.==""|not) | $index[.] ) | group_by(.attr) | map(
# " (let cfg = \(.[0].attr); in '"''"'" + (map(
# if .target | startswith("/etc/") then
# if .mode == "symlink" and false then "
# $SUDO ln -sfn ${cfg.\"\(.key)\".source} \(.target | @sh )
# " else "
# $SUDO rm -rf \(.target | @sh )
# $SUDO cp -a ${cfg.\"\(.key)\".source} \(.target | @sh )
# $SUDO chown -R ${toString cfg.\"\(.key)\".uid}:${toString cfg.\"\(.key)\".gid} \(.target | @sh )
# $SUDO chmod -R ${toString cfg.\"\(.key)\".uid}:${toString cfg.\"\(.key)\".gid} \(.target | @sh )
# # TODO: cfg.\"\(.key)\".user
# # TODO: cfg.\"\(.key)\".group
# " end
# else
# if .recursive then
# "${lib.getExe pkgs.xorg.lndir} -silent ${cfg.\"\(.key)\".source} \(.target | @sh )\n"
# else
# "ln -sfn ${cfg.\"\(.key)\".source} \(.target | @sh )\n"
# end
# end
# ) | join("")) + "'"''"')"
# ) | join(" + \n")
# ' -r
# printf "%s\n" ')'
# )
# # printf "%s\n" "$nix_expr" ; false
# flake=$(nix flake archive . --json | jq .path -r)
# nix build --impure --expr "with (builtins.getFlake ''$flake''); let inherit (nixosConfigurations.\"$hostname\") pkgs; in $nix_expr" --show-trace
# false
# # TODO: run the result
inspect:
nix run -- nixpkgs#nix-inspect -p .
inspect-config host=`just _a_host` prefix="":
nix run -- nixpkgs#nix-inspect -e '(builtins.getFlake "'"$PWD"'").nixosConfigurations."{{host}}".config{{ if prefix == "" { "" } else { "." + prefix } }}'
_nixos_attrnames:
#!/usr/bin/env -S bash -euo pipefail
hostname=$(hostname)
cachefile=.direnv/just-cache-nixos-attrnames.txt
if [[ flake.nix -nt "$cachefile" ]]; then
hostnames=$(nix eval .#nixosConfigurations --apply builtins.attrNames --json 2>/dev/null | jq '.[]' -r)
if [[ "$(grep <<<"$hostnames" -Fx "$hostname" | wc -l)" -eq 1 ]]; then
hostnames="$(printf "%s\n" "$hostname"; grep <<<"$hostnames" -v "^$hostname$")"
fi
if [[ "$UID" -ne 0 ]]; then
mkdir -p .direnv/
cat <<<"$hostnames" >"$cachefile"
fi
else
hostnames="$(cat $cachefile)"
fi
current_remote=$(just remote-current)
if [[ -n "$current_remote" && "$current_remote" != "$hostname" && "$(grep <<<"$hostnames" -Fx "$current_remote" | wc -l)" -eq 1 ]]; then
head -n1 <<<"$hostnames"
printf "%s\n" "$current_remote"
tail -n+2 <<<"$hostnames" | grep -vFx "$current_remote"
else
printf "%s\n" "$hostnames"
fi
@_a_host:
# just _nixos_attrnames | gum filter --placeholder "Pick a host..."
# just _nixos_attrnames | fzf --reverse
just _nixos_attrnames | fzf
@_some_hosts:
# just _nixos_attrnames | gum filter --placeholder "Pick a host..." --no-limit
# just _nixos_attrnames | fzf --reverse --multi
just _nixos_attrnames | fzf --multi
_nixos_fqdns:
#!/usr/bin/env -S bash -euo pipefail
cachefile=.direnv/just-cache-nixos-fqdns.json
if [[ flake.nix -nt "$cachefile" ]]; then
mkdir -p .direnv/
fqdns=$(nix eval .#nixosConfigurations --apply 'builtins.mapAttrs (_: x: x.config.networking.fqdn)' --json 2>/dev/null)
if [[ "$UID" -ne 0 ]]; then
cat <<<"$fqdns" >"$cachefile"
fi
else
fqdns=$( cat "$cachefile" )
fi
just _nixos_attrnames | jq -R --argjson fqdns "$fqdns" '$fqdns[.]' -r
@_a_fqdn:
# just _nixos_fqdns | gum filter --placeholder "Pick a host..."
# just _nixos_fqdns | fzf --reverse
just _nixos_fqdns | fzf
@_some_fqdns:
# just _nixos_fqdns | gum filter --placeholder "Pick a host..." --no-limit
# just _nixos_fqdns | fzf --reverse --multi
just _nixos_fqdns | fzf --multi
@remote-current:
# remote-host # slow
test ! -s .remote.toml || \
printf "%s\n" "$(tomlq <.remote.toml '.hosts | to_entries[] | select(.value.default==true) | .value.host' -r)"
@remote-list:
tomlq <.remote.toml '.hosts | to_entries[] | "remote-set \(.key+1) # -> \(.value.host)"' -r
@remote-set remote=`just remote-current >&2 && just _a_remote_label`:
remote-set "{{remote}}"
motd:
#!/usr/bin/env -S bash -euo pipefail
printf "%s\n" "Current remote: $(just remote-current)"
just remote-list
printf "\n"
todos=$(rg 'TODO' | wc -l)
if [[ "$todos" -gt 10 ]]; then
printf "%s\n" "There are $todos 'TODO's"
fi
@_a_remote_label:
just remote-list | gum filter --placeholder "Pick a remote..." | cut -d' ' -f2
@_some_remote_labels:
just remote-list | gum filter --placeholder "Pick remotes..." --no-limit | cut -d' ' -f2
@_some_remote_label_names:
just remote-list | gum filter --placeholder "Pick remotes..." --no-limit | tr ' ' '\t' | cut -f2,5
@_a_remote_fqdn:
just remote-list | gum filter --placeholder "Pick a remote..." | cut -d' ' -f5-
@_some_remote_fqdns:
just remote-list | gum filter --placeholder "Pick remote..." --no-limit | cut -d' ' -f5-
_remote_ensure $hostname=`just _a_fqdn`:
#!/usr/bin/env -S bash -euo pipefail
if test ! -s .remote.toml; then
remote-init "$hostname"
elif tomlq <.remote.toml '.hosts | to_entries[] |.value.host' -r | grep -q "$hostname"; then
true
else
remote-add "$hostname"
fi
@_remote_label_from_fqdn hostname=`just _a_fqdn`:
tomlq <.remote.toml '.hosts | to_entries[] | select(.value.host == "{{ hostname }}") | .key+1' -r
remote-mprocs *cmd:
#!/usr/bin/env -S bash -euo pipefail
declare -a cmd=("$@")
if [[ "${#cmd[@]}" -eq 0 ]]; then
cmd=($(just --summary --unsorted | xargs printf "just %s\n" | gum filter --placeholder "Pick a recipie..."))
[[ "${#cmd[@]}" -gt 0 ]]
fi
escaped_cmd="$(printf "%q " "${cmd[@]}")"
declare -a labels=()
declare -a names=()
label_names=$(just _some_remote_label_names | grep .)
readarray -td $'\n' labels < <( cut -f1 <<<"$label_names" )
readarray -td $'\n' names < <( cut -f2 <<<"$label_names" )
# TODO: when tmux exits the output is cleared
# printf "remote --label=%q \"env NIX_NO_NOM=1 ${escaped_cmd//%/%%}\"\n" "${labels[@]}" | xargs -d'\n' mprocs --names "$(IFS=','; printf "%s" "${labels[*]}")"
printf "tmux new \"remote --label=%q ${escaped_cmd//%/%%} ; read\" \; set-option destroy-unattached\n" "${labels[@]}" | xargs -d'\n' mprocs --names "$(IFS=','; printf "%s" "${names[*]}")"
@remote-mprocs-init +$cmd=`printf "just %s\n" $(just --summary --unsorted) | gum filter --placeholder "Pick a recipie..."`:
just _some_fqdns | xe -s 'just _remote_ensure "$1"; just _remote_label_from_fqdn "$1"' | sed -E 's/(.*)/remote --label="\1" "env NIX_NO_NOM=1 $cmd"/g' | xargs -d'\n' mprocs
# TODO: support multiple labels, run in tmux
#remote label=`just _a_remote_label` cmd=`printf "just %s\n" $(just --summary --unsorted) | gum filter --placeholder "Pick a recipie..."`:
# remote --label={{label}} {{cmd}}
# TODO: packaged as nix-web
@gorgon:
# https://codeberg.org/gorgon/gorgon/src/branch/main/nix-web
# https://discourse.nixos.org/t/a-web-ui-for-the-nix-store-early-beta/35762
nix run 'git+https://codeberg.org/gorgon/gorgon#nix-web'