chadtmiller.com/knotless
git that lives in your Atmosphere account
7
#!/usr/bin/env bash
# Builds knotless on the runner host from this checkout and (re)starts its
# knotless-run there.
#
# deploy/runner-host/deploy.sh <ssh destination>
# KNOTLESS_RUNNER_HOST=root@host deploy/runner-host/deploy.sh
#
# or with that in deploy/local.env. Run setup.sh on the host once first.
#
# From deploy/local.env, or the environment:
#
# - optional: KNOTLESS_RUNNER_REPOS, repositories it watches besides
# those whose records name its account, which it finds (--discover), as
# `alice.example.com/tally bob.example.com/notes`;
# - KNOTLESS_RUNNER_IDENTIFIER and KNOTLESS_RUNNER_PASSWORD, its account
# and an app password for it, needed once: they are kept on the host in
# /etc/knotless/run.env;
# - optional: KNOTLESS_RUNNER_DOMAIN and KNOTLESS_RUNNER_DID, the name
# Caddy answers the runner's bot login at (`runner.knotless.dev`, an
# A/AAAA record to this host) and the runner's account, the bot that may
# log in there: both turn on the `bots` profile, for private
# repositories (README.md, Private repositories);
# - optional: KNOTLESS_RUNNER_ARGS, more of its command line (`--queue`);
# KNOTLESS_RUNNER_JOBS (4), _CPUS (2), _MEMORY (3, GiB), _TIMEOUT
# (3600 s), _ALLOWANCE (2000) and _PRIVATE_ALLOWANCE (500), as
# docker-compose.yml passes them; KNOTLESS_LOG (info).
#
# The image is built on the host, from this checkout's HEAD as `git
# archive` gives it (so commit first): a build driven over SSH by a local
# docker client times out resolving base images, and Docker Desktop's
# builder ignores DOCKER_HOST and builds locally.
set -euo pipefail
cd "$(dirname "$0")/../.."
[ -f deploy/local.env ] && { set -a; . deploy/local.env; set +a; }
DEST="${1:-${KNOTLESS_RUNNER_HOST:-}}"
[ -n "$DEST" ] || { sed -n '5,6p' "$0" >&2; exit 2; }
REPOS="${KNOTLESS_RUNNER_REPOS:-}"
SSH="ssh -o StrictHostKeyChecking=accept-new $DEST"
COMMIT="$(git rev-parse HEAD 2>/dev/null || echo local)"
if [ -n "${KNOTLESS_RUNNER_IMAGE_FROM:-}" ]; then
# A box too small to build knotless takes the image another host built.
echo "==> copying knotless:current from $KNOTLESS_RUNNER_IMAGE_FROM to $DEST"
ssh -o StrictHostKeyChecking=accept-new "$KNOTLESS_RUNNER_IMAGE_FROM" 'docker save knotless:current | gzip -1' |
$SSH 'gunzip | docker load -q'
else
echo "==> building knotless at ${COMMIT:0:12} on $DEST"
git archive --format=tar HEAD |
$SSH "docker build -q --build-arg KNOTLESS_COMMIT=$COMMIT -t knotless:current -"
fi
if [ -n "${KNOTLESS_RUNNER_IDENTIFIER:-}" ] && [ -n "${KNOTLESS_RUNNER_PASSWORD:-}" ]; then
printf 'KNOTLESS_IDENTIFIER=%s\nKNOTLESS_PASSWORD=%s\n' "$KNOTLESS_RUNNER_IDENTIFIER" "$KNOTLESS_RUNNER_PASSWORD" |
$SSH 'umask 077; cat > /etc/knotless/run.env'
fi
$SSH '[ -f /etc/knotless/run.env ]' || {
echo "the runner needs KNOTLESS_RUNNER_IDENTIFIER and KNOTLESS_RUNNER_PASSWORD once" >&2
exit 2
}
{
echo "KNOTLESS_RUN_REPOS=$REPOS"
[ -z "${KNOTLESS_RUNNER_ARGS:-}" ] || echo "KNOTLESS_RUN_ARGS=$KNOTLESS_RUNNER_ARGS"
# KNOTLESS_RUNNER_DISCOVER set empty watches only KNOTLESS_RUNNER_REPOS.
[ -z "${KNOTLESS_RUNNER_DISCOVER+set}" ] || echo "KNOTLESS_RUN_DISCOVER=$KNOTLESS_RUNNER_DISCOVER"
for v in JOBS OWNER_JOBS CPUS MEMORY TIMEOUT ALLOWANCE PRIVATE_ALLOWANCE; do
from="KNOTLESS_RUNNER_$v"
[ -z "${!from:-}" ] || echo "KNOTLESS_RUN_$v=${!from}"
done
[ -z "${KNOTLESS_LOG:-}" ] || echo "KNOTLESS_LOG=$KNOTLESS_LOG"
if [ -n "${KNOTLESS_RUNNER_DOMAIN:-}" ]; then
echo "COMPOSE_PROFILES=bots"
echo "KNOTLESS_RUNNER_DOMAIN=$KNOTLESS_RUNNER_DOMAIN"
echo "KNOTLESS_BOT_DIDS=${KNOTLESS_RUNNER_DID:?KNOTLESS_RUNNER_DOMAIN takes KNOTLESS_RUNNER_DID, the bot that logs in there}"
fi
} | $SSH 'umask 077; cat > /etc/knotless/.env'
scp -q -o StrictHostKeyChecking=accept-new deploy/runner-host/docker-compose.yml \
deploy/runner-host/Caddyfile "$DEST:/etc/knotless/"
echo "==> starting"
$SSH '
set -e
cd /etc/knotless
mkdir -p /var/lib/knotless-run/bots/sessions /var/lib/knotless-serve /var/lib/caddy
# bots runs as uid 1000, the node user the image runs as, as the main
# host'"'"'s serve does; run, as root, reads and refreshes there too.
chown 1000:1000 /var/lib/knotless-serve /var/lib/knotless-run/bots /var/lib/knotless-run/bots/sessions
docker compose config --quiet
docker compose up -d --remove-orphans
docker compose ps
'