1
0

install.sh 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. #!/usr/bin/env bash
  2. # install.sh [--dry-run]
  3. #
  4. # Installs the deploy pipeline from this checkout: steps 1-3 of "Install
  5. # (once, as the admin)" in README.md. Run it as the admin (a user with
  6. # sudo), from anywhere; it finds the repo from its own path.
  7. #
  8. # Safe to re-run, e.g. to upgrade after merging changes here: it never
  9. # replaces the deploy user's SSH key, the webhook secret or the deploy log,
  10. # and restarts deployd so a new binary takes effect.
  11. #
  12. # --dry-run prints every command instead of running it.
  13. set -euo pipefail
  14. dry=false
  15. case ${1:-} in
  16. --dry-run) dry=true ;;
  17. "") ;;
  18. *) echo "usage: install.sh [--dry-run]" >&2; exit 2 ;;
  19. esac
  20. repo=$(cd "$(dirname "$0")" && pwd)
  21. home=/var/lib/deploy
  22. cd "$repo"
  23. say() { printf '\n== %s\n' "$*"; }
  24. run() { # print and run a command (only print with --dry-run)
  25. printf '+ %s\n' "$*"
  26. $dry || "$@"
  27. }
  28. as_deploy() { # run a shell snippet as the deploy user
  29. printf "+ sudo -iu deploy sh -c '%s'\n" "$1"
  30. $dry || sudo -iu deploy sh -c "$1"
  31. }
  32. exists() { # does a root-only path exist? In a dry run, never prompt: assume not.
  33. if $dry; then sudo -n test -e "$1" 2>/dev/null; else sudo test -e "$1"; fi
  34. }
  35. for f in deployd/main.go release/release sites.json systemd/deployd.service sudoers/60-deploy; do
  36. [[ -f $f ]] || { echo "missing $f: run this from a checkout of bajdev/devops" >&2; exit 1; }
  37. done
  38. command -v go >/dev/null || { echo "go is not installed" >&2; exit 1; }
  39. $dry || sudo -v
  40. say "1. deploy user, log and SSH key"
  41. if getent passwd deploy >/dev/null; then
  42. echo "user deploy exists"
  43. run sudo usermod -aG webops deploy
  44. else
  45. run sudo useradd -r -m -d "$home" -s /bin/bash -G webops deploy
  46. fi
  47. if exists /var/log/deploys.log; then
  48. echo "/var/log/deploys.log exists, kept"
  49. else
  50. run sudo install -m 664 -o deploy -g webops /dev/null /var/log/deploys.log
  51. fi
  52. if exists "$home/.ssh/id_ed25519"; then
  53. echo "deploy SSH key exists, kept"
  54. else
  55. as_deploy 'mkdir -p -m 700 ~/.ssh && ssh-keygen -q -t ed25519 -N "" -f ~/.ssh/id_ed25519 -C deploy@vps'
  56. fi
  57. as_deploy 'git config --global user.name Deploy && git config --global user.email deploy@monoexp.com'
  58. # Record Gogs' host key now. Logging in only works once the Gogs deploy
  59. # account has the key, so a refusal here is expected on a first install.
  60. as_deploy 'ssh -o StrictHostKeyChecking=accept-new -o BatchMode=yes git@git.monoexp.com true 2>/dev/null || true'
  61. say "2. build, test and install"
  62. build=$(mktemp -d); trap 'rm -rf "$build"' EXIT
  63. (cd deployd && run go test ./... && run env CGO_ENABLED=0 go build -trimpath -o "$build/deployd" .)
  64. run sudo install -o root -g root -m 755 "$build/deployd" /usr/local/bin/deployd
  65. run sudo install -o root -g root -m 755 release/release /usr/local/bin/release
  66. run sudo install -d -o root -g root -m 755 /etc/deployd
  67. run sudo install -o root -g root -m 644 sites.json /etc/deployd/sites.json
  68. run sudo install -o root -g root -m 644 systemd/deployd.service /etc/systemd/system/deployd.service
  69. # sudoers: install under a name sudo ignores (it contains a dot), check it,
  70. # then move it into place, so a bad file can never break sudo.
  71. run sudo install -o root -g root -m 440 sudoers/60-deploy /etc/sudoers.d/60-deploy.new
  72. if $dry; then
  73. echo "+ sudo visudo -cf /etc/sudoers.d/60-deploy.new && sudo mv /etc/sudoers.d/60-deploy.new /etc/sudoers.d/60-deploy"
  74. elif sudo visudo -cf /etc/sudoers.d/60-deploy.new; then
  75. sudo mv /etc/sudoers.d/60-deploy.new /etc/sudoers.d/60-deploy
  76. else
  77. sudo rm -f /etc/sudoers.d/60-deploy.new
  78. echo "sudoers/60-deploy failed visudo; nothing changed" >&2
  79. exit 1
  80. fi
  81. say "3. webhook secret and service"
  82. if exists /etc/deployd/secret.env; then
  83. echo "/etc/deployd/secret.env exists, kept"
  84. else
  85. # umask 077: the file is created readable by root only, never briefly wider.
  86. echo "+ openssl rand -hex 32 | sed 's/^/WEBHOOK_SECRET=/' | sudo sh -c 'umask 077 && cat > /etc/deployd/secret.env'"
  87. $dry || openssl rand -hex 32 | sed 's/^/WEBHOOK_SECRET=/' | sudo sh -c 'umask 077 && cat > /etc/deployd/secret.env'
  88. fi
  89. run sudo systemctl daemon-reload
  90. run sudo systemctl enable deployd
  91. run sudo systemctl restart deployd
  92. if ! $dry; then
  93. sleep 1
  94. systemctl is-active --quiet deployd || { echo "deployd is not running; see: journalctl -u deployd" >&2; exit 1; }
  95. echo "deployd is active"
  96. fi
  97. say "Done. Next, by hand"
  98. cat <<EOF
  99. 1. Gogs: create a 'deploy' user, add this key to it, and make it a Write
  100. collaborator on each repo the pipeline releases:
  101. EOF
  102. if $dry; then echo " (deploy's public key: $home/.ssh/id_ed25519.pub)"; else sudo cat "$home/.ssh/id_ed25519.pub" | sed 's/^/ /'; fi
  103. cat <<EOF
  104. 2. Step 4 of README.md: allow Gogs to call 127.0.0.1 (app.ini), restart gogs.
  105. 3. For each site, follow "Moving a site onto the pipeline" in README.md.
  106. The webhook secret is in /etc/deployd/secret.env:
  107. sudo cat /etc/deployd/secret.env
  108. EOF