#!/usr/bin/env nix-shell #!nix-shell -i bash -p nettools usage() { echo "deploy: deploy a nix config to a remote machine, possibly activating it" echo "" echo "usage: deploy [options] [host]" echo "options:" echo "- --action copy|switch|test (default: 'switch')" echo "- --variant light|min|''|all (default: '')" echo "- --pre: alias for --action copy --variant all all" echo "" echo "common idioms:" echo "deploy all: deploy all hosts, sequentially" exit 1 } info() { echo "[deploy]" "$@" } action=switch host=$(hostname) variant= nixArgs=() parseArgs() { while [ "$#" -ne 0 ]; do local arg=$1 shift case "$arg" in (--action) action=$1 shift ;; (--help) usage ;; (--variant) if [ -n "$1" ]; then variant=-$1 else variant= fi shift ;; (crappy|desko|lappy|moby|servo) host="$arg" ;; (--pre) action=copy host=all variant=all ;; (*) nixArgs+=("$arg") ;; esac done } runOnTarget() { # run the command ($@) on the machine we're deploying to. # if that's a remote machine, then do it via ssh, else local shell. if [ -n "$addr" ]; then ssh "$addr" "$@" else "$@" fi } # deployOneHost $host $variant deployOneHost() { local host="$1" local variant="$2" info "building $host$variant ..." nix-build -A "hosts.$host$variant.toplevel" --out-link "./build/result-$host$variant" "${nixArgs[@]}" || return 1 storePath="$(readlink ./build/result-$host$variant)" info "build $host$variant -> $storePath" # mimic `nixos-rebuild --target-host`, in effect: # - nix-copy-closure ... # - nix-env --set ... # - switch-to-configuration # avoid the actual `nixos-rebuild` for a few reasons: # - fewer nix evals # - more introspectability and debuggability # - sandbox friendliness (especially: `git` doesn't have to be run as root) if [ -n "$host" ]; then if [ -e /run/secrets/nix_signing_key ]; then info "signing store paths ..." sudo nix store sign -r -k /run/secrets/nix_signing_key "$storePath" else info "not signing store paths: /run/secrets/nix_signing_key does not exist" fi # add more `-v` for more verbosity (up to 5). # builders-use-substitutes false: optimizes so that the remote machine doesn't try to get paths from its substituters. # we already have all paths here, and the remote substitution is slow to check and SERIOUSLY flaky on moby in particular. nix copy -vv --option builders-use-substitutes false --to "ssh-ng://$host" "$storePath" || return 1 fi if [ -n "$action" ] && [ "$action" != "copy" ]; then info "activating profile... " runOnTarget sudo nix-env -p /nix/var/nix/profiles/system --set "$storePath" || return 1 runOnTarget sudo "$storePath/bin/switch-to-configuration" "$action" || return 1 fi } parseArgs "$@" failedDeploys=() if [ "$host" = "all" ]; then for host in moby lappy crappy servo desko; do if [ "$variant" = "all" ]; then for variant in -min -light ""; do deployOneHost "$host" "$variant" || \ failedDeploys+=("$host$variant") done else deployOneHost "$host" "$variant" || \ failedDeploys+=("$host$variant") fi done else deployOneHost "$host" "$variant" || \ failedDeploys+=("$host$variant") fi if [ "${#failedDeploys[@]}" -ne 0 ]; then echo "FAILED DEPLOYMENT:" for d in "${failedDeploys[@]}"; do echo "- $d" done exit 1 else echo "SUCCESS" fi