| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- #!/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 <<EOF
- 1. Gogs: create a 'deploy' user, add this key to it, and make it a Write
- collaborator on each repo the pipeline releases:
- EOF
- if $dry; then echo " (deploy's public key: $home/.ssh/id_ed25519.pub)"; else sudo cat "$home/.ssh/id_ed25519.pub" | sed 's/^/ /'; fi
- cat <<EOF
- 2. Step 4 of README.md: allow Gogs to call 127.0.0.1 (app.ini), restart gogs.
- 3. For each site, follow "Moving a site onto the pipeline" in README.md.
- The webhook secret is in /etc/deployd/secret.env:
- sudo cat /etc/deployd/secret.env
- EOF
|