scripts/deploy: port from bash to ysh
This commit is contained in:
443
scripts/deploy
443
scripts/deploy
@@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env nix-shell
|
#!/usr/bin/env nix-shell
|
||||||
#!nix-shell -i bash -p nettools
|
#!nix-shell -i ysh -p nettools -p oils-for-unix
|
||||||
|
|
||||||
SELF=$(hostname)
|
var SELF = $(hostname)
|
||||||
|
|
||||||
usage() {
|
proc usage {
|
||||||
echo "deploy: deploy a nix config to a remote machine, possibly activating it"
|
echo "deploy: deploy a nix config to a remote machine, possibly activating it"
|
||||||
echo ""
|
echo ""
|
||||||
echo "usage: deploy [options] [host] [host2 ...]"
|
echo "usage: deploy [options] [host] [host2 ...]"
|
||||||
@@ -13,7 +13,9 @@ usage() {
|
|||||||
echo "- --switch (default)"
|
echo "- --switch (default)"
|
||||||
echo "- --test: switch to the new configuration, but do not make it bootable"
|
echo "- --test: switch to the new configuration, but do not make it bootable"
|
||||||
echo "- --dry-run: show what would be done without actually doing it"
|
echo "- --dry-run: show what would be done without actually doing it"
|
||||||
echo "- --pre: alias for --action copy --variant all all"
|
echo "- --pre: alias for --action copy ..."
|
||||||
|
echo ' if otherwise unspecified, implies `--variant all`'
|
||||||
|
echo " if no host is specified, copies to all known hosts"
|
||||||
echo "- --reboot: reboot the target machine after deploying (if deployed with no errors)"
|
echo "- --reboot: reboot the target machine after deploying (if deployed with no errors)"
|
||||||
echo "- --reboot-force: reboot the target machine after deploying (even on error)"
|
echo "- --reboot-force: reboot the target machine after deploying (even on error)"
|
||||||
echo "- --variant light|min|''|all (default: '')"
|
echo "- --variant light|min|''|all (default: '')"
|
||||||
@@ -29,192 +31,195 @@ usage() {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
info() {
|
proc info (...stmts) {
|
||||||
echo "[deploy]" "$@"
|
echo "[deploy]" @stmts
|
||||||
}
|
}
|
||||||
|
|
||||||
action=switch
|
var action = "switch"
|
||||||
hosts=()
|
var hosts = []
|
||||||
ip=
|
var ip = null
|
||||||
defaultHost="$SELF"
|
var defaultHost = SELF
|
||||||
variants=()
|
var variants = []
|
||||||
defaultVariant=
|
var defaultVariant = ""
|
||||||
# by default, don't ship builds to servo. i'd guess this can be overriden by passing --builders servo
|
# by default, don't ship builds to servo. i'd guess this can be overriden by passing --builders servo
|
||||||
nixArgs=(--builders "")
|
var nixArgs = ["--builders", ""]
|
||||||
doReboot=
|
var doReboot = false
|
||||||
doRebootForce=
|
var doRebootForce = false
|
||||||
dryRun=
|
var dryRun = false
|
||||||
wireguard=opportunistic
|
var wireguard = "opportunistic"
|
||||||
storePath=
|
var storePath = null
|
||||||
addHost() {
|
proc addHost (; host) {
|
||||||
if [ "$1" = all ]; then
|
if (host === "all" ) {
|
||||||
# order matters:
|
# order matters:
|
||||||
hosts+=(moby lappy desko servo)
|
call hosts->extend(["moby", "lappy", "desko", "servo"])
|
||||||
else
|
} else {
|
||||||
hosts+=("$1")
|
call hosts->append(host)
|
||||||
fi
|
}
|
||||||
}
|
}
|
||||||
addVariant() {
|
proc addVariant (; v) {
|
||||||
if [ "$1" = all ]; then
|
if (v === "all") {
|
||||||
variants+=("-min" "-light" "" "-next-min" "-next-light" "-next")
|
call variants->extend(["-min", "-light", "", "-next-min", "-next-light", "-next"])
|
||||||
elif [ -n "$1" ]; then
|
} elif (v === "") {
|
||||||
variants+=("-$1")
|
|
||||||
else
|
|
||||||
# "full" variant
|
# "full" variant
|
||||||
variants+=("")
|
call variants->append("")
|
||||||
fi
|
} else {
|
||||||
|
call variants->append("-$v")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
parseArgs() {
|
proc parseArgs(;cur=null, next=null, ...rest) {
|
||||||
while [ "$#" -ne 0 ]; do
|
if (cur === null) {
|
||||||
local arg=$1
|
# end
|
||||||
shift
|
:
|
||||||
case "$arg" in
|
} else {
|
||||||
(--build|--copy|--switch|--test)
|
case (cur) {
|
||||||
action=${arg/--/}
|
--build | --copy | --switch | --test {
|
||||||
;;
|
setglobal action = cur
|
||||||
(--deriv)
|
parseArgs (next, ...rest)
|
||||||
storePath="$1"
|
}
|
||||||
shift
|
--deriv {
|
||||||
;;
|
setglobal storePath = next
|
||||||
(--dry-run)
|
parseArgs (...rest)
|
||||||
dryRun=1
|
}
|
||||||
;;
|
--dry-run {
|
||||||
(--help)
|
setglobal dryRun = true
|
||||||
|
parseArgs (next, ...rest)
|
||||||
|
}
|
||||||
|
--help {
|
||||||
usage
|
usage
|
||||||
;;
|
parseArgs (next, ...rest)
|
||||||
(--ip)
|
}
|
||||||
ip="$1"
|
--ip {
|
||||||
shift
|
setglobal ip = next
|
||||||
;;
|
parseArgs (...rest)
|
||||||
(--pre)
|
}
|
||||||
action=copy
|
--pre {
|
||||||
defaultVariant=all
|
setglobal action = "copy"
|
||||||
defaultHost=all
|
setglobal defaultVariant = "all"
|
||||||
;;
|
setglobal defaultHost = "all"
|
||||||
(--reboot)
|
parseArgs (next, ...rest)
|
||||||
doReboot=1
|
}
|
||||||
;;
|
--reboot {
|
||||||
(--reboot-force|--force-reboot)
|
setglobal doReboot = true
|
||||||
doReboot=1
|
parseArgs (next, ...rest)
|
||||||
doRebootForce=1
|
}
|
||||||
;;
|
--reboot-force | --force-reboot {
|
||||||
(--variant)
|
setglobal doReboot = true
|
||||||
addVariant "$1"
|
setglobal doRebootForce = true
|
||||||
shift
|
parseArgs (next, ...rest)
|
||||||
;;
|
}
|
||||||
(--wireguard)
|
--variant {
|
||||||
wireguard="$1"
|
addVariant (next)
|
||||||
shift
|
parseArgs (...rest)
|
||||||
;;
|
}
|
||||||
(all|crappy|desko|lappy|moby|servo)
|
--wireguard {
|
||||||
addHost "$arg"
|
setglobal wireguard = next
|
||||||
;;
|
parseArgs (...rest)
|
||||||
(*)
|
}
|
||||||
nixArgs+=("$arg")
|
all | crappy | desko | lappy | moby | servo {
|
||||||
;;
|
addHost (cur)
|
||||||
esac
|
parseArgs (next, ...rest)
|
||||||
done
|
}
|
||||||
|
(else) {
|
||||||
|
call nixArgs->append(cur)
|
||||||
|
parseArgs (next, ...rest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if [ "${#hosts[@]}" -eq 0 ] && [ -n "$defaultHost" ]; then
|
if (hosts === [] and defaultHost) {
|
||||||
addHost "$defaultHost"
|
addHost (defaultHost)
|
||||||
fi
|
}
|
||||||
if [ "${#variants[@]}" -eq 0 ]; then
|
if (variants === []) {
|
||||||
addVariant "$defaultVariant"
|
addVariant (defaultVariant)
|
||||||
fi
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
destructive() {
|
proc destructive (...cmd) {
|
||||||
if [ -z "$dryRun" ]; then
|
if (dryRun) {
|
||||||
if [ -n "$ECHO_CMD" ]; then
|
echo "dry-run:" @cmd
|
||||||
echo "$@"
|
} else {
|
||||||
fi
|
if (get(ENV, "ECHO_CMD")) {
|
||||||
"$@"
|
echo @cmd
|
||||||
else
|
}
|
||||||
echo "dry-run: $@"
|
@cmd
|
||||||
fi
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# return "$1" or "$1-hn", based on if wireguard was requested or not
|
# return "$1" or "$1-hn", based on if wireguard was requested or not
|
||||||
resolveHost() {
|
func resolveHost (host) {
|
||||||
if [ -n "$ip" ]; then
|
if (ip) {
|
||||||
echo "$ip"
|
return (ip)
|
||||||
else
|
} else {
|
||||||
case "$wireguard-$1" in
|
case ("$wireguard-$host") {
|
||||||
(opportunistic-moby)
|
opportunistic-moby {
|
||||||
echo "$1-hn"
|
return ("$host-hn")
|
||||||
;;
|
}
|
||||||
(opportunistic-*)
|
opportunistic-* {
|
||||||
echo "$1"
|
return (host)
|
||||||
;;
|
}
|
||||||
(never-*)
|
never-* {
|
||||||
echo "$1"
|
return (host)
|
||||||
;;
|
}
|
||||||
(always-*)
|
always-* {
|
||||||
echo "$1-hn"
|
return ("$host-hn")
|
||||||
;;
|
}
|
||||||
(*)
|
(else) {
|
||||||
echo "$1-hn"
|
return ("$host-hn")
|
||||||
;;
|
}
|
||||||
esac
|
}
|
||||||
fi
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# return the number of seconds to allot to `nix copy` when copying the given variant.
|
# return the number of seconds to allot to `nix copy` when copying the given variant.
|
||||||
# this is done to avoid stalled copies from blocking the entire process, while hopefully not breaking the copies that are actually important
|
# this is done to avoid stalled copies from blocking the entire process, while hopefully not breaking the copies that are actually important
|
||||||
timeoutFor() {
|
func timeoutFor (variant) {
|
||||||
case $1 in
|
case (variant) {
|
||||||
(-min|-light|-next)
|
-min | -light | -next {
|
||||||
echo 3600
|
return (3600)
|
||||||
;;
|
}
|
||||||
(-next-min|-next-light)
|
-next-min | -next-light {
|
||||||
echo 1800
|
return (1800)
|
||||||
;;
|
}
|
||||||
(*)
|
(else) {
|
||||||
# this catches the normal variant
|
# this catches the normal variant
|
||||||
echo 14400
|
return (14400)
|
||||||
;;
|
}
|
||||||
esac
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runOnTarget() {
|
proc runOnTarget (host, ...cmd) {
|
||||||
local host="$1"
|
# run the command on the machine we're deploying to.
|
||||||
shift
|
|
||||||
# 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 that's a remote machine, then do it via ssh, else local shell.
|
||||||
if [ -n "$host" ] && [ "$host" != "$SELF" ]; then
|
if (host !== "" and host !== SELF) {
|
||||||
info "running on remote ($host):" "$@"
|
info "running on remote ($host):" @cmd
|
||||||
ssh "$host" "$@"
|
ssh "$host" @cmd
|
||||||
else
|
} else {
|
||||||
info "running locally ($SELF):" "$@"
|
info "running locally ($SELF):" @cmd
|
||||||
"$@"
|
@cmd
|
||||||
fi
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# deployOneHost $host $variant
|
proc deployOneHost (; host, variant) {
|
||||||
deployOneHost() {
|
var timeout = timeoutFor(variant)
|
||||||
local host="$1"
|
|
||||||
local variant="$2"
|
|
||||||
|
|
||||||
local timeout=$(timeoutFor "$variant")
|
|
||||||
|
|
||||||
# storePath is allowed to be either a realized derivation,
|
# storePath is allowed to be either a realized derivation,
|
||||||
# or the path to a .drv file itself
|
# or the path to a .drv file itself
|
||||||
local myStorePath="$storePath"
|
var myStorePath = storePath
|
||||||
if [ -z "$myStorePath" ]; then
|
if (not myStorePath) {
|
||||||
# `nix-build -A foo` evals and then realizes foo, but it never unloads the memory used to eval foo.
|
# `nix-build -A foo` evals and then realizes foo, but it never unloads the memory used to eval foo.
|
||||||
# my exprs are heavyweight, we need that memory for building, so do the evals separately from the realizations:
|
# my exprs are heavyweight, we need that memory for building, so do the evals separately from the realizations:
|
||||||
info "evaluating $host$variant..."
|
info "evaluating $host$variant..."
|
||||||
myStorePath=$(nix eval --raw -f . "hosts.$host$variant.toplevel.drvPath")
|
setvar myStorePath = $(nix eval --raw -f . "hosts.$host$variant.toplevel.drvPath")
|
||||||
fi
|
}
|
||||||
|
|
||||||
if [[ "$myStorePath" == *.drv ]]; then
|
if ("$myStorePath" => endsWith(".drv")) {
|
||||||
info "building $host$variant ($drvPath)"
|
info "building $host$variant ($myStorePath)"
|
||||||
myStorePath=$(destructive nix-store --realize "$myStorePath" "${nixArgs[@]}")
|
setvar myStorePath = $(destructive nix-store --realize "$myStorePath" @nixArgs)
|
||||||
if [ -z "$myStorePath" ]; then
|
if (myStorePath === "") {
|
||||||
return 1
|
false
|
||||||
fi
|
}
|
||||||
info "built $host$variant -> $myStorePath"
|
info "built $host$variant -> $myStorePath"
|
||||||
fi
|
}
|
||||||
|
|
||||||
# mimic `nixos-rebuild --target-host`, in effect:
|
# mimic `nixos-rebuild --target-host`, in effect:
|
||||||
# - nix-copy-closure ...
|
# - nix-copy-closure ...
|
||||||
@@ -225,76 +230,88 @@ deployOneHost() {
|
|||||||
# - more introspectability and debuggability
|
# - more introspectability and debuggability
|
||||||
# - sandbox friendliness (especially: `git` doesn't have to be run as root)
|
# - sandbox friendliness (especially: `git` doesn't have to be run as root)
|
||||||
|
|
||||||
local netHost=$(resolveHost "$host")
|
var netHost = resolveHost(host)
|
||||||
|
|
||||||
case "$action" in
|
case (action) {
|
||||||
(copy|switch|test)
|
copy | switch | test {
|
||||||
if [ -n "$host" ] && [ "$host" != "$SELF" ]; then
|
if (host !== "" and host !== SELF) {
|
||||||
if [ -e /run/secrets/nix_signing_key ]; then
|
if test -e /run/secrets/nix_signing_key {
|
||||||
info "signing store paths ..."
|
info "signing store paths ..."
|
||||||
destructive sudo nix store sign -r -k /run/secrets/nix_signing_key "$myStorePath"
|
destructive sudo nix store sign -r -k /run/secrets/nix_signing_key "$myStorePath"
|
||||||
else
|
} else {
|
||||||
info "not signing store paths: /run/secrets/nix_signing_key does not exist"
|
info "not signing store paths: /run/secrets/nix_signing_key does not exist"
|
||||||
fi
|
}
|
||||||
# add more `-v` for more verbosity (up to 5).
|
# 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.
|
# 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.
|
# we already have all paths here, and the remote substitution is slow to check and SERIOUSLY flaky on moby in particular.
|
||||||
ECHO_CMD=1 destructive timeout "$timeout" nix copy -vv --option builders-use-substitutes false --to "ssh-ng://$netHost" "$myStorePath" || return 1
|
ECHO_CMD=1 destructive timeout "$timeout" nix copy -vv --option builders-use-substitutes false --to "ssh-ng://$netHost" "$myStorePath"
|
||||||
fi
|
}
|
||||||
;;
|
}
|
||||||
esac
|
}
|
||||||
|
case (action) {
|
||||||
case "$action" in
|
switch | test {
|
||||||
(switch|test)
|
|
||||||
info "activating profile... "
|
info "activating profile... "
|
||||||
destructive runOnTarget "$netHost" sudo nix-env -p /nix/var/nix/profiles/system --set "$myStorePath" || return 1
|
destructive runOnTarget "$netHost" sudo nix-env -p /nix/var/nix/profiles/system --set "$myStorePath"
|
||||||
destructive runOnTarget "$netHost" sudo "$myStorePath/bin/switch-to-configuration" "$action"
|
try {
|
||||||
local rc=$?
|
destructive runOnTarget "$netHost" sudo "$myStorePath/bin/switch-to-configuration" "$action"
|
||||||
if [[ -n "$doReboot" && ("$rc" -eq 0 || -n "$doRebootForce") ]]; then
|
}
|
||||||
|
var fail = failed
|
||||||
|
if (doReboot and (not fail or doRebootForce)) {
|
||||||
info "rebooting $host"
|
info "rebooting $host"
|
||||||
destructive runOnTarget "$netHost" sane-reboot "$host" || return 1
|
destructive runOnTarget "$netHost" sane-reboot "$host" || return 1
|
||||||
fi
|
}
|
||||||
return $rc
|
if (fail) {
|
||||||
;;
|
false
|
||||||
esac
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
failedDeploys=()
|
var failedDeploys = []
|
||||||
deployHosts() {
|
proc deployHosts(; ...hosts) {
|
||||||
local hosts=("$@")
|
for v in (variants) {
|
||||||
for v in "${variants[@]}"; do
|
for h in (hosts) {
|
||||||
for h in "${hosts[@]}"; do
|
try {
|
||||||
deployOneHost "$h" "$v" || \
|
deployOneHost (h, v)
|
||||||
failedDeploys+=("$h$v")
|
}
|
||||||
done
|
if failed {
|
||||||
done
|
call failedDeploys->append("$h$v")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseArgs "$@"
|
proc main (...args) {
|
||||||
|
parseArgs (...args)
|
||||||
|
|
||||||
# i care e.g. that full moby is deployed before crappy:
|
# i care e.g. that full moby is deployed before crappy:
|
||||||
earlyHosts=()
|
var earlyHosts = []
|
||||||
lateHosts=()
|
var lateHosts = []
|
||||||
for host in "${hosts[@]}"; do
|
for host in (hosts) {
|
||||||
case $host in
|
case (host) {
|
||||||
(crappy)
|
crappy {
|
||||||
lateHosts+=("$host")
|
call lateHosts->append(host)
|
||||||
;;
|
}
|
||||||
(*)
|
(else) {
|
||||||
earlyHosts+=("$host")
|
call earlyHosts->append(host)
|
||||||
;;
|
}
|
||||||
esac
|
}
|
||||||
done
|
}
|
||||||
|
|
||||||
deployHosts "${earlyHosts[@]}"
|
deployHosts (...earlyHosts)
|
||||||
deployHosts "${lateHosts[@]}"
|
deployHosts (...lateHosts)
|
||||||
|
|
||||||
|
if (failedDeploys !== []) {
|
||||||
|
echo "FAILED DEPLOYMENT:"
|
||||||
|
for d in (failedDeploys) {
|
||||||
|
echo "- $d"
|
||||||
|
}
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
if [ "${#failedDeploys[@]}" -ne 0 ]; then
|
|
||||||
echo "FAILED DEPLOYMENT:"
|
|
||||||
for d in "${failedDeploys[@]}"; do
|
|
||||||
echo "- $d"
|
|
||||||
done
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "SUCCESS"
|
echo "SUCCESS"
|
||||||
fi
|
}
|
||||||
|
|
||||||
|
if is-main {
|
||||||
|
main @ARGV
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user