aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--common.sh157
-rwxr-xr-xdevices.sh125
-rwxr-xr-xlocal.sh58
4 files changed, 221 insertions, 120 deletions
diff --git a/.gitignore b/.gitignore
index fcfc4a1..fd1b4ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
result*
+.result-*
diff --git a/common.sh b/common.sh
new file mode 100644
index 0000000..995a57e
--- /dev/null
+++ b/common.sh
@@ -0,0 +1,157 @@
+# Common Bash functions for helper scripts in this repository
+set -eu
+
+## Logging #####################################################################
+_print() {
+ local color="\e[$1m"
+ local clrcolor="\e[0m"
+ shift
+ if [ ! -t 1 ]; then
+ color=""
+ clrcolor=""
+ fi
+ printf "${color}%s${clrcolor}\n" "$*" >&2
+}
+
+stage() {
+ _print '1;32' "$@"
+}
+
+info() {
+ _print '1;35' "$@"
+}
+
+error() {
+ _print '1;31' "$@"
+}
+warning() {
+ _print '1;33' "$@"
+}
+
+## SSH access helper ###########################################################
+
+# Convert hostname to the SSH destination
+sshdest() {
+ awk -F- 'NF > 1 { print $2"."$1; exit } { print $1 }' <<<"$1"
+}
+
+_ssh() {
+ local device="$1"
+ shift
+ if [ "$device" != "$(hostname)" ]; then
+ local -a args
+ [ "$1" = "sudo" ] && \
+ args+=('-t') # Crude detection for terminal need
+ ssh "${args[@]}" "$(sshdest "$device")" -- "$@"
+ else
+ "$@"
+ fi
+}
+
+## Evalutions and queries ######################################################
+
+# The path where build result is linked to
+result() {
+ echo ".result-$1"
+}
+
+# Get system of the device
+device_system() {
+ nix eval --raw ".#nixosConfigurations.$1.config.nixpkgs.system"
+}
+
+# Validates if link is valid.
+build_validate() {
+ local device="$1"
+ [ -L "$(result "$device")" ] && [ -e "$(result "$device")" ]
+}
+
+## Build NixOS system ##########################################################
+# $1: device name
+# All other arguments are passed to the nix build command
+build() {
+ local device="$1"
+ shift
+
+ local toplevel=".config.system.build.toplevel"
+ if [ "$(device_system "$device")" = "armv7l-linux" ]; then
+ toplevel=".config.system.build.cross.x86_64-linux${toplevel}"
+ fi
+
+ stage "Building system for device: $device"
+ nix build \
+ -o "$(result "${device}")" \
+ --keep-going \
+ "$@" \
+ "${0%/*}#nixosConfigurations.${device}${toplevel}"
+}
+
+## Copy NixOS system ###########################################################
+# $1: device name
+copy() {
+ local device="$1"
+ if ! build_validate "$device"; then
+ warning "System for device '$device' seems to be not build." >&2
+ return 1
+ fi
+ local store
+ store="$(readlink -f "$(result "$device")")"
+
+ local freespace required;
+ freespace="$(_ssh "$device" df -B 1 /nix | awk 'NR == 2 { print $4 }')"
+ required="$(nix path-info -S "$store" | awk '{ print $2 }')"
+ info "Free space on device: $(numfmt --to=iec "$freespace")"
+ info "Required space: $(numfmt --to=iec "$required")"
+ if [ "$required" -ge "$freespace" ]; then
+ error "There is not enough space to copy clousure to: $device" >&2
+ return 1
+ fi
+
+ stage "Copy closure to: $device"
+ nix copy -s --to "ssh://$(sshdest "$device")" "$store"
+}
+
+## Switch Nix encironment ######################################################
+# $1: switch operation to be performed
+# $2: device name
+# TODO possibly really query if switch is or is not required
+setenv() {
+ local switchop="$1"
+ local device="$2"
+ if ! build_validate "$device"; then
+ warning "System '$device' seems to be not build." >&2
+ return 1
+ fi
+ local store
+ store="$(readlink -f "$(result "$device")")"
+
+ stage "${switchop^} system: $device"
+ local cursystem
+ cursystem="$(_ssh "$device" readlink -f /nix/var/nix/profiles/system)"
+ if [ "$cursystem" != "$store" ]; then
+ info "-----------------------------------------------------------------"
+ _ssh "$device" \
+ nix store diff-closures "$cursystem" "$store"
+ info "-----------------------------------------------------------------"
+ # TODO we should call here switch-to-configuration as well to use single
+ # sudo session and remove one prompt.
+ _ssh "$device" \
+ sudo sh -c \
+ 'nix-env --profile /nix/var/nix/profiles/system --set "$store" && /nix/var/nix/profiles/system/bin/switch-to-configuration "$1"' \
+ "$switchop"
+ else
+ warning "The latest system might have been already set."
+ fi
+}
+
+boot() {
+ setenv boot "$1"
+}
+
+switch() {
+ setenv switch "$1"
+}
+
+switch_test() {
+ setenv test "$1"
+}
diff --git a/devices.sh b/devices.sh
index 25632db..3a2dce5 100755
--- a/devices.sh
+++ b/devices.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -eu
+source "${0%/*}/common.sh"
declare -a devices
################################################################################
## aarch64
@@ -24,134 +24,18 @@ valid_device() {
return 1
}
-device_system() {
- nix eval --raw ".#nixosConfigurations.$1.config.nixpkgs.system"
-}
-
-sshdev() {
- echo "$1" | awk -F- 'NF > 1 { print $2"."$1; exit } { print $1 }'
-}
-
-
-build() {
- local system="$1"
- echo "Building $system"
- local -a args
- local toplevel=".config.system.build.toplevel"
- args+=("--keep-going")
- if [ "$(device_system "$1")" = "armv7l-linux" ]; then
- toplevel=".config.system.build.cross.x86_64-linux${toplevel}"
- fi
- nix build \
- -o "result-${system}" \
- "${args[@]}" \
- "${0%/*}#nixosConfigurations.${system}${toplevel}"
-}
-
-build_validate() {
- local system="$1"
- [ -L "result-$system" ] && [ -e "result-$system" ]
-}
-
-copy() {
- local system="$1"
- if ! build_validate "$system"; then
- echo "System '$system' seems to be not build." >&2
- return 1
- fi
- local store="$(readlink -f "result-$system")"
- local host="$(sshdev "$system")"
-
- local freespace="$(ssh "$host" -- df -B 1 /nix | awk 'NR == 2 { print $4 }')"
- local required="$(nix path-info -S "$store" | awk '{ print $2 }')"
- echo "Free space on device: $(numfmt --to=iec "$freespace")"
- echo "Required space: $(numfmt --to=iec "$required")"
- if [ "$required" -ge "$freespace" ]; then
- echo "There is not enough space to copy clousure to: $system" >&2
- return 1
- fi
-
- echo "Copy closure to: $system"
- nix copy -s --to "ssh://$host" "$store"
-}
-
-setenv() {
- local system="$1"
- if ! build_validate "$system"; then
- echo "System '$system' seems to be not build." >&2
- return 1
- fi
- local store="$(readlink -f "result-$system")"
- local host="$(sshdev "$system")"
-
- echo "Update system: $system"
- if [ "$(ssh "$host" -- readlink -f /nix/var/nix/profiles/system)" != "$store" ]; then
- ssh -t "$host" -- \
- sudo nix-env --profile /nix/var/nix/profiles/system --set "$store"
- fi
-}
-
-boot() {
- local system="$1"
- setenv "$system" || return 1
-
- local store="$(readlink -f "result-$system")"
- local host="$(sshdev "$system")"
-
- echo "Setting boot system: $system"
- ssh -t "$host" -- \
- sudo /nix/var/nix/profiles/system/bin/switch-to-configuration boot
-}
-
-is_current() {
- ssh "$1" -- \
- '[ "$(readlink -f /run/current-system)" != "$(readlink -f /nix/var/nix/profiles/system)" ]'
-}
-
-switch() {
- local system="$1"
- setenv "$system" || return 1
-
- local store="$(readlink -f "result-$system")"
- local host="$(sshdev "$system")"
-
- if is_current "$host"; then
- echo "Switching: $system"
- ssh -t "$host" -- \
- sudo /nix/var/nix/profiles/system/bin/switch-to-configuration switch
- else
- echo "This system is already running: $system"
- fi
-}
-
-switch_test() {
- local system="$1"
- setenv "$system" || return 1
-
- local store="$(readlink -f "result-$system")"
- local host="$(sshdev "$system")"
-
- if is_current "$host"; then
- echo "Testing: $system"
- ssh -t "$host" -- \
- sudo /nix/var/nix/profiles/system/bin/switch-to-configuration test
- else
- echo "This system is already running: $system"
- fi
-}
-
for_devices() {
for device in "${selected_devices[@]}"; do
for op in "$@"; do
if ! "$op" "$device"; then
- echo "Operation '$op' failed for: $device" >&2
+ error "Operation '$op' failed for: $device" >&2
break
fi
done
done
}
-
+################################################################################
operation="${1:-}"
[ $# -gt 0 ] && shift
@@ -159,7 +43,7 @@ declare -a selected_devices
if [ $# -gt 0 ]; then
for device in "$@"; do
if ! valid_device "$device"; then
- echo "No such device: $device" >&2
+ error "No such device: $device" >&2
exit 2
fi
selected_devices+=("$device")
@@ -168,6 +52,7 @@ else
selected_devices=("${devices[@]}")
fi
+
case "$operation" in
help|h)
cat <<-EOF
diff --git a/local.sh b/local.sh
new file mode 100755
index 0000000..37985c3
--- /dev/null
+++ b/local.sh
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+source "${0%/*}/common.sh"
+
+operations() {
+ for op in "$@"; do
+ if ! "$op" "$(hostname)"; then
+ error "Operation '$op' failed" >&2
+ break
+ fi
+ done
+}
+
+################################################################################
+operation="${1:-}"
+if [ $# -gt 1 ]; then
+ echo "Invalid argument: $2" >&2
+ exit 2
+fi
+
+case "$operation" in
+ help|h)
+ cat <<-EOF
+ Usage $0 operation [device]...
+ Local system builder and updater for remote devices.
+
+ Operations:
+ build: build device system
+ boot: set built system to be boot default on the device
+ switch: switch to the built system on the target device
+ test: test the built system on the target device
+ EOF
+ ;;
+ build|b)
+ operations build
+ ;;
+ boot)
+ operations boot
+ ;;
+ switch|s)
+ operations switch
+ ;;
+ test|t)
+ operations switch_test
+ ;;
+ build-switch|bs|"")
+ operations build switch
+ ;;
+ build-test|bt)
+ operations build switch_test
+ ;;
+ build-boot|bb)
+ operations build boot
+ ;;
+ default)
+ echo "Unknown operation: $operation" >&2
+ exit 2
+ ;;
+esac