#!/usr/bin/env bash # install.sh [--dry-run] # # Installs the deploy pipeline from this checkout: steps 1-3 of "Install # (once, as the admin)" in README.md. Run it as the admin (a user with # sudo), from anywhere; it finds the repo from its own path. # # Safe to re-run, e.g. to upgrade after merging changes here: it never # replaces the deploy user's SSH key, the webhook secret or the deploy log, # and restarts deployd so a new binary takes effect. # # --dry-run prints every command instead of running it. set -euo pipefail dry=false case ${1:-} in --dry-run) dry=true ;; "") ;; *) echo "usage: install.sh [--dry-run]" >&2; exit 2 ;; esac repo=$(cd "$(dirname "$0")" && pwd) home=/var/lib/deploy cd "$repo" say() { printf '\n== %s\n' "$*"; } run() { # print and run a command (only print with --dry-run) printf '+ %s\n' "$*" $dry || "$@" } as_deploy() { # run a shell snippet as the deploy user printf "+ sudo -iu deploy sh -c '%s'\n" "$1" $dry || sudo -iu deploy sh -c "$1" } exists() { # does a root-only path exist? In a dry run, never prompt: assume not. if $dry; then sudo -n test -e "$1" 2>/dev/null; else sudo test -e "$1"; fi } for f in deployd/main.go release/release sites.json systemd/deployd.service sudoers/60-deploy; do [[ -f $f ]] || { echo "missing $f: run this from a checkout of bajdev/devops" >&2; exit 1; } done command -v go >/dev/null || { echo "go is not installed" >&2; exit 1; } $dry || sudo -v say "1. deploy user, log and SSH key" if getent passwd deploy >/dev/null; then echo "user deploy exists" run sudo usermod -aG webops deploy else run sudo useradd -r -m -d "$home" -s /bin/bash -G webops deploy fi if exists /var/log/deploys.log; then echo "/var/log/deploys.log exists, kept" else run sudo install -m 664 -o deploy -g webops /dev/null /var/log/deploys.log fi if exists "$home/.ssh/id_ed25519"; then echo "deploy SSH key exists, kept" else as_deploy 'mkdir -p -m 700 ~/.ssh && ssh-keygen -q -t ed25519 -N "" -f ~/.ssh/id_ed25519 -C deploy@vps' fi as_deploy 'git config --global user.name Deploy && git config --global user.email deploy@monoexp.com' # Record Gogs' host key now. Logging in only works once the Gogs deploy # account has the key, so a refusal here is expected on a first install. as_deploy 'ssh -o StrictHostKeyChecking=accept-new -o BatchMode=yes git@git.monoexp.com true 2>/dev/null || true' say "2. build, test and install" build=$(mktemp -d); trap 'rm -rf "$build"' EXIT (cd deployd && run go test ./... && run env CGO_ENABLED=0 go build -trimpath -o "$build/deployd" .) run sudo install -o root -g root -m 755 "$build/deployd" /usr/local/bin/deployd run sudo install -o root -g root -m 755 release/release /usr/local/bin/release run sudo install -d -o root -g root -m 755 /etc/deployd run sudo install -o root -g root -m 644 sites.json /etc/deployd/sites.json run sudo install -o root -g root -m 644 systemd/deployd.service /etc/systemd/system/deployd.service # sudoers: install under a name sudo ignores (it contains a dot), check it, # then move it into place, so a bad file can never break sudo. run sudo install -o root -g root -m 440 sudoers/60-deploy /etc/sudoers.d/60-deploy.new if $dry; then echo "+ sudo visudo -cf /etc/sudoers.d/60-deploy.new && sudo mv /etc/sudoers.d/60-deploy.new /etc/sudoers.d/60-deploy" elif sudo visudo -cf /etc/sudoers.d/60-deploy.new; then sudo mv /etc/sudoers.d/60-deploy.new /etc/sudoers.d/60-deploy else sudo rm -f /etc/sudoers.d/60-deploy.new echo "sudoers/60-deploy failed visudo; nothing changed" >&2 exit 1 fi say "3. webhook secret and service" if exists /etc/deployd/secret.env; then echo "/etc/deployd/secret.env exists, kept" else # umask 077: the file is created readable by root only, never briefly wider. echo "+ openssl rand -hex 32 | sed 's/^/WEBHOOK_SECRET=/' | sudo sh -c 'umask 077 && cat > /etc/deployd/secret.env'" $dry || openssl rand -hex 32 | sed 's/^/WEBHOOK_SECRET=/' | sudo sh -c 'umask 077 && cat > /etc/deployd/secret.env' fi run sudo systemctl daemon-reload run sudo systemctl enable deployd run sudo systemctl restart deployd if ! $dry; then sleep 1 systemctl is-active --quiet deployd || { echo "deployd is not running; see: journalctl -u deployd" >&2; exit 1; } echo "deployd is active" fi say "Done. Next, by hand" cat <