aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/fu
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/by-name/fu')
-rwxr-xr-xpkgs/by-name/fu/fupdate-flake/fupdate-flake.sh194
-rw-r--r--pkgs/by-name/fu/fupdate-flake/package.nix31
-rwxr-xr-xpkgs/by-name/fu/fupdate-sys/fupdate-sys.sh168
-rw-r--r--pkgs/by-name/fu/fupdate-sys/package.nix43
-rw-r--r--pkgs/by-name/fu/fupdate/.envrc13
-rw-r--r--pkgs/by-name/fu/fupdate/.gitignore12
-rw-r--r--pkgs/by-name/fu/fupdate/Cargo.lock230
-rw-r--r--pkgs/by-name/fu/fupdate/Cargo.toml81
-rw-r--r--pkgs/by-name/fu/fupdate/flake.nix34
-rw-r--r--pkgs/by-name/fu/fupdate/fupdate.1.md70
-rwxr-xr-xpkgs/by-name/fu/fupdate/fupdate.sh197
-rw-r--r--pkgs/by-name/fu/fupdate/package.nix64
-rw-r--r--pkgs/by-name/fu/fupdate/src/cli.rs56
-rw-r--r--pkgs/by-name/fu/fupdate/src/main.rs57
-rwxr-xr-xpkgs/by-name/fu/fupdate/update.sh14
15 files changed, 971 insertions, 293 deletions
diff --git a/pkgs/by-name/fu/fupdate-flake/fupdate-flake.sh b/pkgs/by-name/fu/fupdate-flake/fupdate-flake.sh
new file mode 100755
index 00000000..00c1e443
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate-flake/fupdate-flake.sh
@@ -0,0 +1,194 @@
+#! /usr/bin/env sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+UPDATE_SCRIPT_NAME="update.sh"
+
+info() {
+ echo "Info: $1"
+}
+dbg() {
+ if [ "${DEBUG_ENABLE-unset}" != "unset" ]; then
+ echo "Debug: $1" >&2
+ fi
+}
+die() {
+ echo "Error: $1"
+ exit 1
+}
+
+# Search for a file “upwards”.
+# This will return the relative path from "$1" to the found file.
+#
+# # Type
+# upfind :: Path -> String -> Path
+#
+# # Arguments
+# $1
+# : The directory to use as start of your search.
+#
+# $2
+# : The file to search for.
+#
+# # Example
+# upfind "/home/user1" "/usr"
+# => /usr
+upfind() {
+ starting_directory="$(readlink --canonicalize "$1")"
+ search_string="$2"
+
+ current_directory="$starting_directory"
+
+ while
+ search_result=$(fd "$search_string" "$current_directory/" --max-depth 1)
+ dbg "upfind - search in $current_directory gives: $search_result"
+ [ -z "$search_result" ] && [ "$current_directory" != "/" ]
+ do current_directory=$(dirname "$current_directory"); done
+
+ realpath --relative-to="$1" "$search_result"
+}
+
+# Construct the storage path for the update script allowed hashes.
+#
+# # Type
+# get_storage_path :: Path -> Path
+#
+# # Arguments
+# $1
+# : The path to the update script
+#
+# # Returns
+# The constructed storage path.
+get_storage_path() {
+ update_script="$(realpath "$1")"
+
+ storage_path="$XDG_DATA_HOME/fupdate-flake/$update_script"
+ echo "$storage_path"
+}
+
+# Checks if a given path to the update script is allowed.
+#
+# # Type
+# is_allowed :: Path -> bool
+#
+# # Arguments
+# $1
+# : The path to the update script to check.
+#
+# # Return exit code
+# 0
+# : If the update script is allowed
+#
+# 1
+# : If it is not.
+is_allowed() {
+ update_script="$(realpath "$1")"
+
+ storage_path="$(get_storage_path "$update_script")"
+
+ # Use this invocation, to also include the path to the `$update_script`
+ update_script_hash="$(sha256sum "$update_script")"
+
+ if [ -f "$storage_path" ]; then
+ if [ "$(cat "$storage_path")" = "$update_script_hash" ]; then
+ return 0
+ else
+ return 1
+ fi
+ else
+ return 1
+ fi
+}
+
+# Asks the user if they want to allow a given script.
+#
+# # Type
+# ask_to_allow_update_script :: Path
+#
+# # Arguments
+# $1
+# : The path to the update script to ask for.
+ask_to_allow_update_script() {
+ update_script="$(realpath "$1")"
+
+ printf "\033[2J" # clear the screen
+ cat "$update_script"
+
+ printf "Do you want to allow this script?[N/y]: "
+ read -r allow
+
+ case "$allow" in
+ [yY])
+ info "Update script allowed."
+
+ storage_path="$(get_storage_path "$update_script")"
+ update_script_hash="$(sha256sum "$update_script")"
+
+ mkdir --parents "$(dirname "$storage_path")"
+ printf "%s" "$update_script_hash" >"$storage_path"
+ ;;
+ *)
+ info "Update script not allowed."
+ ;;
+ esac
+}
+
+# Performs a full update.
+# This consists of running an update script.
+# Additionally, it also checks for duplicated inputs in a `flake.lock` file, if it exists.
+#
+# # Type
+# update :: Path -> Path -> [String]
+#
+# # Arguments
+# $1
+# : The path to the update script to execute.
+#
+# $2
+# : The base directory from which to start the update.
+#
+# $3
+# : Arguments to pass to the update script.
+update() {
+ update_script="$1"
+ base_directory="$2"
+ shift 2
+
+ cd "$base_directory" || die "The provided base directory '$base_directory' cannot be accessed"
+ dbg "Changed directory to: $base_directory"
+
+ dbg "Executing update script ('$update_script') following args: '$*'"
+ "$update_script" "$@"
+
+ if [ -f "flake.lock" ] && grep '[^0-9]_[0-9]' flake.lock --quiet; then
+ grep '[^0-9]_[0-9]' flake.lock
+ die "Your flake.nix contains duplicate inputs!"
+ fi
+}
+
+main() {
+ base_directory="$(git rev-parse --show-toplevel)"
+ update_script="$(upfind "$PWD" "$UPDATE_SCRIPT_NAME")"
+ dbg "update_script is: $update_script"
+
+ if [ "$update_script" = "" ]; then
+ die "Failed to find update script."
+ elif is_allowed "$update_script"; then
+ update "$update_script" "$base_directory" "$@"
+ else
+ ask_to_allow_update_script "$update_script"
+ is_allowed "$update_script" && main "$@"
+ fi
+}
+
+main "$@"
+
+# vim: ft=sh
diff --git a/pkgs/by-name/fu/fupdate-flake/package.nix b/pkgs/by-name/fu/fupdate-flake/package.nix
new file mode 100644
index 00000000..4e21cd23
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate-flake/package.nix
@@ -0,0 +1,31 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+ writeShellApplication,
+ # Dependencies
+ coreutils,
+ fd,
+ gnugrep,
+ git,
+}:
+writeShellApplication {
+ name = "fupdate-flake";
+ text = builtins.readFile ./fupdate-flake.sh;
+
+ # The `update.sh` script might actually want to keep the path.
+ inheritPath = true;
+
+ runtimeInputs = [
+ coreutils
+ fd
+ gnugrep
+ git
+ ];
+}
diff --git a/pkgs/by-name/fu/fupdate-sys/fupdate-sys.sh b/pkgs/by-name/fu/fupdate-sys/fupdate-sys.sh
new file mode 100755
index 00000000..57ced766
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate-sys/fupdate-sys.sh
@@ -0,0 +1,168 @@
+#!/usr/bin/env dash
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+# FIXME(@bpeetz): Ideally I could replace this script with a deployment tool. Thus we
+# would have the same tool on the server as I use in my config. <2025-04-14>
+
+# Shell library {{{
+die() {
+ error "$1"
+ if [ -n "$2" ]; then
+ exit "$2"
+ else
+ exit 1
+ fi
+}
+print() {
+ # shellcheck disable=SC2059
+ printf "$*"
+}
+println() {
+ # shellcheck disable=SC2059
+ printf "$*\n"
+}
+eprint() {
+ >&2 print "$@"
+}
+eprintln() {
+ >&2 println "$@"
+}
+if [ "${NO_COLOR-unset}" != "unset" ]; then
+ error() {
+ eprintln "==> ERROR:" "$*"
+ }
+ warning() {
+ eprintln "==> WARNING:" "$*"
+ }
+ debug() {
+ [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln "==> [Debug:]" "$*"
+ }
+ debug2() {
+ [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln " -> [Debug:]" "$*"
+ }
+ msg() {
+ eprintln "==>" "$*"
+ }
+ msg2() {
+ eprintln " ->" "$*"
+ }
+ prompt() {
+ eprint "..>" "$*"
+ }
+else
+ error() {
+ eprintln "\033[1;91m==> ERROR:\033[0m" "\033[1;93m$*\033[0m"
+ }
+ warning() {
+ eprintln "\033[1;91m==> WARNING:\033[0m" "\033[1;93m$*\033[0m"
+ }
+ debug() {
+ [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln "\033[1;94m==> [Debug:]\033[0m" "\033[1;93m$*\033[0m"
+ }
+ debug2() {
+ [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln "\033[1;94m -> [Debug:]\033[0m" "\033[1;93m$*\033[0m"
+ }
+ msg() {
+ eprintln "\033[1;96m==>\033[0m" "\033[1;93m$*\033[0m"
+ }
+ msg2() {
+ eprintln "\033[1;96m ->\033[0m" "\033[1;93m$*\033[0m"
+ }
+ prompt() {
+ eprint "\033[1;96m..>\033[0m" "\033[1;93m$*\033[0m"
+ }
+fi
+# }}}
+
+NAME="update-sys"
+help() {
+ cat <<EOF
+This is a NixOS System flake update manager.
+
+USAGE:
+ $NAME [--branch <branchname>] [--help]
+
+OPTIONS:
+ --branch | -b BRANCHNAME
+ select a branch to update from.
+
+ --mode | -m MODE
+ select a mode to update with
+
+ --help | -h
+ output this help.
+ARGUMENTS:
+ BRANCHNAME := [[ git branch --list --format '%(refname:short)' ]]
+ The name of the branch to deploy the config from
+
+ MODE := switch|boot|test|build|dry-build|dry-activate|edit|repl|build-vm|build-vm-with-bootloader
+ See the 'nixos-rebuild' manpage for more information about these modes.
+EOF
+ exit "$1"
+}
+BRANCH=""
+
+while [ "$#" -gt 0 ]; do
+ case "$1" in
+ "--help" | "-h")
+ help 0
+ ;;
+ "--branch" | "-b")
+ if [ "${2-unset}" != "unset" ]; then
+ BRANCH="$2"
+ else
+ error "$1 requires an argument"
+ help 1
+ fi
+ shift 2
+ ;;
+ "--mode" | "-m")
+ if [ "${2-unset}" != "unset" ]; then
+ MODE="$2"
+ else
+ error "$1 requires an argument"
+ help 1
+ fi
+ shift 2
+ ;;
+ *)
+ error "the option $1 does not exist!"
+ help 1
+ ;;
+ esac
+done
+
+cd /etc/nixos || die "No /etc/nixos"
+msg "Starting system update..."
+git remote update origin --prune >/dev/null 2>&1
+if ! [ "$BRANCH" = "" ]; then
+ git switch "$BRANCH" >/dev/null 2>&1 && msg2 "Switched to branch '$BRANCH'"
+fi
+msg2 "Updating git repository..."
+git pull --rebase
+
+# We use a tempfile, to make this truly async.
+default_branch=$(mktemp -t fupdate_flake_XXXX)
+cleanup() {
+ rm "$default_branch"
+}
+trap cleanup EXIT
+
+git remote show origin | grep 'HEAD' | cut -d':' -f2 | sed -e 's/^ *//g' -e 's/ *$//g' >"$default_branch" &
+
+msg2 "Updating system..."
+nixos-rebuild "${MODE-switch}"
+
+git switch "$(cat "$default_branch")" >/dev/null 2>&1 && msg2 "Switched to branch '$(cat "$default_branch")'"
+msg "Finished Update!"
+
+# vim: ft=sh
diff --git a/pkgs/by-name/fu/fupdate-sys/package.nix b/pkgs/by-name/fu/fupdate-sys/package.nix
new file mode 100644
index 00000000..6672c2f2
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate-sys/package.nix
@@ -0,0 +1,43 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+ writeShellApplication,
+ # Dependencies
+ git,
+ nixos-rebuild-ng,
+ sudo,
+ openssh,
+ coreutils,
+ mktemp,
+ gnugrep,
+ gnused,
+ systemd,
+ lixPackageSets,
+}: let
+ nixos-rebuild-without-nix = nixos-rebuild-ng.override {
+ nix = lixPackageSets.latest.lix;
+ };
+in
+ writeShellApplication {
+ name = "fupdate-sys";
+ text = builtins.readFile ./fupdate-sys.sh;
+ inheritPath = false;
+ runtimeInputs = [
+ git
+ nixos-rebuild-without-nix
+ sudo
+ openssh
+ coreutils
+ mktemp
+ gnugrep
+ gnused
+ systemd
+ ];
+ }
diff --git a/pkgs/by-name/fu/fupdate/.envrc b/pkgs/by-name/fu/fupdate/.envrc
new file mode 100644
index 00000000..294de504
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/.envrc
@@ -0,0 +1,13 @@
+#!/usr/bin/env sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use flake
diff --git a/pkgs/by-name/fu/fupdate/.gitignore b/pkgs/by-name/fu/fupdate/.gitignore
new file mode 100644
index 00000000..f255eebd
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/.gitignore
@@ -0,0 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+/target
+.direnv
diff --git a/pkgs/by-name/fu/fupdate/Cargo.lock b/pkgs/by-name/fu/fupdate/Cargo.lock
new file mode 100644
index 00000000..5ea7cdd7
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/Cargo.lock
@@ -0,0 +1,230 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+version = 4
+
+[[package]]
+name = "anstream"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
+
+[[package]]
+name = "anstyle-parse"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
+
+[[package]]
+name = "clap"
+version = "4.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_complete"
+version = "4.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97bf4965940c2382204c0ded6dd3dd48c0c4e872f1e76fb1bf94f45991a2cb6a"
+dependencies = [
+ "clap",
+ "clap_lex",
+ "is_executable",
+ "shlex",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
+
+[[package]]
+name = "fupdate"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "clap_complete",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "is_executable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82cb6a9f675da968c63b6208c641b9dca58fc0133ae53375736b1767b0cab8bd"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.118"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
diff --git a/pkgs/by-name/fu/fupdate/Cargo.toml b/pkgs/by-name/fu/fupdate/Cargo.toml
new file mode 100644
index 00000000..3f4386f1
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/Cargo.toml
@@ -0,0 +1,81 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+[package]
+name = "fupdate"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1.0.103"
+clap = { version = "4.6.1", features = ["derive"] }
+clap_complete = { version = "4.6.6", features = ["unstable-dynamic"] }
+
+[profile.release]
+lto = true
+codegen-units = 1
+panic = "abort"
+split-debuginfo = "off"
+
+[lints.rust]
+# rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html
+warnings = "warn"
+future_incompatible = { level = "warn", priority = -1 }
+let_underscore = { level = "warn", priority = -1 }
+nonstandard_style = { level = "warn", priority = -1 }
+rust_2018_compatibility = { level = "warn", priority = -1 }
+rust_2018_idioms = { level = "warn", priority = -1 }
+rust_2021_compatibility = { level = "warn", priority = -1 }
+unused = { level = "warn", priority = -1 }
+# rustc allowed-by-default lints https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html
+# missing_docs = "warn"
+macro_use_extern_crate = "warn"
+meta_variable_misuse = "warn"
+missing_abi = "warn"
+missing_copy_implementations = "warn"
+missing_debug_implementations = "warn"
+non_ascii_idents = "warn"
+noop_method_call = "warn"
+single_use_lifetimes = "warn"
+trivial_casts = "warn"
+trivial_numeric_casts = "warn"
+unreachable_pub = "warn"
+unsafe_op_in_unsafe_fn = "warn"
+unused_crate_dependencies = "warn"
+unused_import_braces = "warn"
+unused_lifetimes = "warn"
+unused_qualifications = "warn"
+variant_size_differences = "warn"
+
+[lints.rustdoc]
+# rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html
+broken_intra_doc_links = "warn"
+private_intra_doc_links = "warn"
+missing_crate_level_docs = "warn"
+private_doc_tests = "warn"
+invalid_codeblock_attributes = "warn"
+invalid_rust_codeblocks = "warn"
+bare_urls = "warn"
+
+[lints.clippy]
+# clippy allowed by default
+dbg_macro = "warn"
+# clippy categories https://doc.rust-lang.org/clippy/
+all = { level = "warn", priority = -1 }
+correctness = { level = "warn", priority = -1 }
+suspicious = { level = "warn", priority = -1 }
+style = { level = "warn", priority = -1 }
+complexity = { level = "warn", priority = -1 }
+perf = { level = "warn", priority = -1 }
+pedantic = { level = "warn", priority = -1 }
+missing_panics_doc = "allow"
+missing_errors_doc = "allow"
diff --git a/pkgs/by-name/fu/fupdate/flake.nix b/pkgs/by-name/fu/fupdate/flake.nix
new file mode 100644
index 00000000..22b0957f
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/flake.nix
@@ -0,0 +1,34 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+ description = "This is a Nix flake update manager.";
+
+ inputs = {};
+
+ outputs = {...}: let
+ system = "x86_64-linux";
+ sources = import ../../../../npins/full.nix {};
+
+ pkgs = (sources.loadFlake "nixpkgs").legacyPackages."${system}";
+ in {
+ devShells."${system}".default = pkgs.mkShell {
+ packages = [
+ pkgs.cargo
+ pkgs.clippy
+ pkgs.rustc
+ pkgs.rustfmt
+
+ pkgs.cargo-edit
+ ];
+ };
+ };
+}
+# vim: ts=2
+
diff --git a/pkgs/by-name/fu/fupdate/fupdate.1.md b/pkgs/by-name/fu/fupdate/fupdate.1.md
deleted file mode 100644
index 710e8fb7..00000000
--- a/pkgs/by-name/fu/fupdate/fupdate.1.md
+++ /dev/null
@@ -1,70 +0,0 @@
-% FUPDATE(1) fupdate 1.0.0
-% Soispha
-% May 2023
-
-# NAME
-
-fupdate - updates your flake, while checking for common mistakes
-
-# SYNOPSIS
-
-**fupdate** list of \[*flake*|*\<some word>*|*--help*|*-h*\]
-
-# DESCRIPTION
-
-Argument can be stacked, this makes it possible to specify multiple targets to be updated in succession. See the Examples section for further details.
-
-No argument or *flake*
-: **fupdate**, when executed without arguments or with *flake*, will update your *flake.lock*, check for duplicate flake inputs, i.e., an input has an input declared, which you have also declared as input, and will run a script called *update.sh*, if you allow it.
-The allowance for the script is asked, when you run **fupdate** and the found script is not yet allowed. Furthermore, the allowance is based on the concrete sha256 hash of the script, so any changes will require another allowance.
-
-**\<some word>** as argument
-: If the executable **update-\<some word>** is reachable thought the PATH variable, than this is run. Otherwise, the program will exit.
-
-# OPTIONS
-
-**--help**, **-h**
-: Displays a help message and exit.
-
-**--version**, **-v**
-: Displays the software version and exit.
-
-# EXAMPLES
-
-**fupdate** or **fupdate flake**
-: Updates your *flake.lock*. See the Description section for further details.
-
-**fupdate sys**
-: Run the executable **update-sys**, if it exists. See the Description section for further details.
-
-**fupdate flake sys docs**
-: First updates your flake, then, if the command succeeded, runs **update-sys**, afterweich **update-docs** is run.
-
-# FILES
-
-*update.sh*
-: This is supposed to be a shell script located in your flake base directory, i.e., the directory which contains both a *flake.nix* and a *flake.lock* file.
-
-*~/.local/share/flake-update/*
-: **fupdate** will store the hashes to the allowed *update.sh* files here.
-
-# BUGS
-
-Report bugs to <https://codeberg.org/soispha/flake_update/issues>.
-
-# COPYRIGHT
-
-Copyright (C) 2023 Soispha
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
diff --git a/pkgs/by-name/fu/fupdate/fupdate.sh b/pkgs/by-name/fu/fupdate/fupdate.sh
deleted file mode 100755
index 4322610a..00000000
--- a/pkgs/by-name/fu/fupdate/fupdate.sh
+++ /dev/null
@@ -1,197 +0,0 @@
-#! /usr/bin/env dash
-
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
-
-UPDATE_SCRIPT_NAME="update.sh"
-CONFIG_DIRECTORY_PATH="$HOME/.local/share/flake-update"
-
-# Both are used in version()
-# shellcheck disable=SC2034
-AUTHORS="Soispha"
-# shellcheck disable=SC2034
-YEARS="2023"
-
-UPDATE_SCRIPT_NOT_WANTED=false
-
-# Searches upward for a `UPDATE_SCRIPT_NAME` script
-# Returns a path to the script if it exists, otherwise nothing is returned
-check_for_update_script() {
- dirname="$(search_upward_files "$UPDATE_SCRIPT_NAME")"
- if [ "$dirname" ]; then
- printf "%s/%s" "$dirname" "$UPDATE_SCRIPT_NAME"
- fi
-}
-
-# Checks if a given path to the update script is allowed.
-# Takes the path as input
-# Return 0, if allowed, 1 if not.
-check_for_allowed_update_script() {
- update_script="$1"
- config_path="${CONFIG_DIRECTORY_PATH}${update_script}"
- update_script_hash="$(sha256sum "$update_script")"
- if [ -f "$config_path" ]; then
- if [ "$(cat "$config_path")" = "$update_script_hash" ]; then
- dbg "Recorded hash matches"
- return 0
- else
- dbg "Recorded hash \'$(cat "$config_path")\' does not match real hash \'$update_script_hash\', assuming not allowed"
- return 1
- fi
- else
- dbg "Path \'$config_path\' does not exist, assuming not allowed"
- return 1
- fi
-}
-
-# Asks the user if they want to allow a given script.
-# Takes the path as input
-ask_to_allow_update_script() {
- update_script="$1"
- config_path="${CONFIG_DIRECTORY_PATH}${update_script}"
- update_script_hash="$(sha256sum "$update_script")"
- println "\033[2J" # clear the screen
- cat "$update_script"
- readp "Do you want to allow this script?[N/y]: " allow
- # shellcheck disable=SC2154
- dbg "allow is: $allow"
- case "$allow" in
- [yY])
- dbg "allowed script"
- dbg "storing contents in: $config_path"
- mkdir --parents "$(dirname "$config_path")"
- print "$update_script_hash" >"$config_path"
- ;;
- *)
- UPDATE_SCRIPT_NOT_ALLOWED=true
- ;;
- esac
-}
-
-# Runs the provided script and continues to update the nix flake
-# Takes the path to the script and the directory to the flake as arguments
-# If the path to the update script is empty, it will be ignored
-update() {
- update_script="$1"
- flake_base_dir="$2"
- shift 2
- dbg "Provided following args to update script: '$*'"
-
- cd "$flake_base_dir" || die "Provided dir \'$flake_base_dir\' can not be accessed"
- dbg "changed directory to: $flake_base_dir"
-
- nix flake update
-
- if ! [ "$update_script" = "" ] && ! [ "$UPDATE_SCRIPT_NOT_WANTED" = "true" ]; then
- "$update_script" "$@"
- fi
-
- if grep '[^0-9]_[0-9]' flake.lock >/dev/null; then
- batgrep '[^0-9]_[0-9]' flake.lock
- die "Your flake.nix contains duplicate inputs!"
- fi
-}
-
-help() {
- cat <<EOF
-This is a Nix flake update manager.
-
-USAGE:
- $NAME [--help | --version] [flake [--no-script] | <some other command>]
-
-OPTIONS:
- --help | -h
- Display this help and exit.
-
- --version | -v
- Display version and copyright information and exit.
-
- --no-script
- Avoid running the 'update.sh' script
-COMMANDS:
- flake
- update the flake project
-
- <some other command>
- runs a executable called "update-<some other command>", if it exists
-EOF
-}
-
-main() {
- if ! [ "$UPDATE_SCRIPT_NOT_ALLOWED" = true ]; then
- update_script="$(check_for_update_script)"
- flake_base_dir="$(search_flake_base_dir)" # Assume, that the update script is in the base dir
- dbg "update_script is: $update_script"
- dbg "flake_base_dir is: $flake_base_dir"
-
- if [ "$update_script" = "" ]; then
- update "" "$flake_base_dir" "$@"
- elif check_for_allowed_update_script "$update_script" && ! [ "$update_script" = "" ]; then
- update "$update_script" "$flake_base_dir" "$@"
- else
- ask_to_allow_update_script "$update_script"
- main "$@"
- fi
- fi
-}
-
-if [ "$#" -eq 0 ]; then
- main
- exit 0
-fi
-
-for input in "$@"; do
- case "$input" in
- "--help" | "-h")
- help
- exit 0
- ;;
- "--version" | "-v")
- version
- exit 0
- ;;
- "--no-script" | "-n")
- UPDATE_SCRIPT_NOT_WANTED=true
- ;;
- "--")
- end_of_cli_options=true
-
- # Stop processing args after that marker.
- break
- ;;
- esac
- [ "$end_of_cli_options" = "true" ] && break
-done
-
-case "$1" in
-"flake")
- shift 1
-
- # Filter out fupdate specific flags
- while [ "$1" != "--" ]; do
- # FIXME: This check allows to add a flag multiple times, but this should probably
- # not be allowed <2024-03-29>
- case "$1" in
- "--no-script" | "-n")
- shift 1
- ;;
- *)
- break
- ;;
- esac
- done
-
- [ "$1" = "--" ] && shift 1
- main "$@"
- ;;
-*)
- command="$1"
- shift 1
- [ "$1" = "--" ] && shift 1
- if which update-"$command" >/dev/null 2>&1; then
- update-"$command" "$@"
- else
- die "command \"update-$command\" is not executable, or does not exist"
- fi
- ;;
-esac
diff --git a/pkgs/by-name/fu/fupdate/package.nix b/pkgs/by-name/fu/fupdate/package.nix
index 66372add..86eccaf7 100644
--- a/pkgs/by-name/fu/fupdate/package.nix
+++ b/pkgs/by-name/fu/fupdate/package.nix
@@ -1,29 +1,41 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
{
- sysLib,
- dash,
- lix,
- gnugrep,
- fd,
- coreutils,
- bat, # used by batgrep
- bat-extras,
- gnused, # required by batgrep
- git, # needed to fetch through git
+ rustPlatform,
+ installShellFiles,
+ makeWrapper,
}:
-sysLib.writeShellScript {
- name = "fupdate";
- src = ./fupdate.sh;
- generateCompletions = true;
- keepPath = true;
- dependencies = [
- dash
- lix
- gnugrep
- fd
- coreutils
- bat # used by batgrep
- bat-extras.batgrep
- gnused # required by batgrep
- git # needed to fetch through git
+rustPlatform.buildRustPackage (finalAttrs: {
+ pname = "fupdate";
+ version = "0.1.0";
+
+ src = ./.;
+ cargoLock = {
+ lockFile = ./Cargo.lock;
+ };
+
+ buildInputs = [];
+
+ nativeBuildInputs = [
+ installShellFiles
+ makeWrapper
];
-}
+
+ postInstall = ''
+ installShellCompletion --cmd fupdate \
+ --bash <(COMPLETE=bash $out/bin/fupdate) \
+ --fish <(COMPLETE=fish $out/bin/fupdate) \
+ --zsh <(COMPLETE=zsh $out/bin/fupdate)
+ '';
+
+ meta = {
+ mainProgram = "fupdate";
+ };
+})
diff --git a/pkgs/by-name/fu/fupdate/src/cli.rs b/pkgs/by-name/fu/fupdate/src/cli.rs
new file mode 100644
index 00000000..a7168413
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/src/cli.rs
@@ -0,0 +1,56 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::{env, ffi::OsStr, fs::read_dir};
+
+use clap::Parser;
+use clap_complete::{CompletionCandidate, engine::ArgValueCompleter};
+
+/// This is a Nix flake update manager.
+#[derive(Parser, Debug)]
+#[command(author, version, about)]
+pub struct CliArgs {
+ /// The command to execute.
+ #[arg(add = ArgValueCompleter::new(get_fupdate_commands))]
+ pub command: Vec<String>,
+}
+
+fn get_fupdate_commands(current: &OsStr) -> Vec<CompletionCandidate> {
+ let mut output = vec![];
+ let path = env::var("PATH").unwrap_or_default();
+
+ let Some(current) = current.to_str() else {
+ return output;
+ };
+
+ for directory in path.split(':') {
+ if let Ok(mut read) = read_dir(directory) {
+ for value in read.by_ref().flatten() {
+ let file_name = value.file_name();
+ let name = file_name.to_string_lossy();
+ let Some(stripped) = name.strip_prefix("fupdate-") else {
+ continue;
+ };
+
+ if stripped.starts_with(current) {
+ output.push(CompletionCandidate::new(
+ value
+ .file_name()
+ .to_string_lossy()
+ .strip_prefix("fupdate-")
+ .expect("Exists"),
+ ));
+ }
+ }
+ }
+ }
+
+ output
+}
diff --git a/pkgs/by-name/fu/fupdate/src/main.rs b/pkgs/by-name/fu/fupdate/src/main.rs
new file mode 100644
index 00000000..2200ce83
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/src/main.rs
@@ -0,0 +1,57 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::process::Command;
+
+use anyhow::{Context, Result, bail};
+use clap::{CommandFactory, Parser};
+
+pub mod cli;
+
+use crate::cli::CliArgs;
+
+fn main() -> Result<(), anyhow::Error> {
+ clap_complete::CompleteEnv::with_factory(CliArgs::command).complete();
+
+ let args = CliArgs::parse();
+
+ let command = args.command.first().map_or("flake", String::as_str);
+
+ {
+ let args = if args.command.len() > 1 {
+ &args.command[1..]
+ } else {
+ &[]
+ };
+
+ // println!("Running: `fupdate-{command} {}`", args.join(" "));
+
+ let child = Command::new(format!("fupdate-{command}"))
+ .args(args)
+ .status()
+ .with_context(|| format!("Failed to spawn `fupdate-{command}`"))?;
+
+ if !child.success() {
+ bail!("Command `fupdate-{command} {}` failed!", args.join(" "));
+ }
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod test {
+ use clap::CommandFactory;
+
+ #[test]
+ fn verify_cli() {
+ super::CliArgs::command().debug_assert();
+ }
+}
diff --git a/pkgs/by-name/fu/fupdate/update.sh b/pkgs/by-name/fu/fupdate/update.sh
new file mode 100755
index 00000000..5ad524e8
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/update.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+[ "$1" = "upgrade" ] && cargo upgrade --incompatible allow --pinned allow --recursive true
+cargo update --recursive