#!/usr/bin/env bash set -euo pipefail cd "$(dirname "${BASH_SOURCE[0]}")/.." root=/opt/autolibrary service=autolibrary-webserver.service current=$root/current sha=${RELEASE_SHA:?Missing RELEASE_SHA} port=${AUTOLIBRARY_PORT:-3003} [[ $sha =~ ^[0-9a-f]{40}$ && $(git rev-parse HEAD) == "$sha" ]] [[ $port =~ ^[1-9][0-9]{0,4}$ && $port -le 65535 ]] base_url=http://127.0.0.1:$port healthy() { local path for path in / /api/health; do [[ $(curl --silent --show-error --noproxy '*' --max-time 2 \ --output /dev/null --write-out '%{http_code}' "$base_url$path") == 200 ]] || return 1 done } wait_for_service() { local attempt for ((attempt = 0; attempt < 20; attempt++)); do if systemctl is-active --quiet "$service" && healthy; then return 0 fi sleep 1 done return 1 } # Serialize releases, including manual reruns of the workflow. exec 9>"$root/releases/.deploy.lock" flock -x 9 remote_head=$(git ls-remote --exit-code origin refs/heads/main) if [[ ${remote_head%%$'\t'*} != "$sha" ]]; then echo "Skipping $sha because main has moved." exit 0 fi previous= if [[ -L $current ]]; then previous=$(readlink "$current") [[ -d $current ]] || { echo "Broken release link: $current" >&2; exit 1; } elif [[ -e $current ]]; then echo "Expected a release symlink at $current" >&2 exit 1 fi test -f "$root/shared/config.json" sudo -n -l /usr/bin/systemctl restart "$service" >/dev/null sudo -n -l /usr/bin/systemctl stop "$service" >/dev/null release=$(mktemp -d "$root/releases/$sha.XXXXXXXX") switch_dir= finished=false switch_to() { ln -s -- "$1" "$switch_dir/current" && mv -Tf -- "$switch_dir/current" "$current" } cleanup() { local status=$? trap - EXIT trap '' INT TERM set +e if ! $finished && [[ $(readlink "$current") == "$release" ]]; then echo "Deployment failed. Restoring the previous service state." >&2 if [[ -n $previous ]]; then rm -f -- "$switch_dir/current" if ! switch_to "$previous" || ! sudo -n /usr/bin/systemctl restart "$service" || ! wait_for_service; then echo "Rollback failed. Check journalctl -u $service." >&2 status=1 fi elif ! sudo -n /usr/bin/systemctl stop "$service" || ! rm -f -- "$current"; then echo "Could not clean up the first deployment. Check $service and $current." >&2 status=1 fi fi if ! $finished && [[ $(readlink "$current") != "$release" ]]; then rm -rf -- "$release" fi if [[ -n $switch_dir ]]; then rm -rf -- "$switch_dir" fi exit "$status" } trap cleanup EXIT trap 'exit 130' INT trap 'exit 143' TERM switch_dir=$(mktemp -d "$root/.switch.XXXXXXXX") install -m 0755 .deploy/webserver "$release/webserver" install -d -m 0755 "$release/frontend" "$release/backend" cp -r frontend/dist "$release/frontend/dist" cp -r backend/templates "$release/backend/templates" test -s "$release/frontend/dist/index.html" test -s "$release/backend/templates/404.html" ln -s "$root/shared/logs" "$release/logs" chmod -R a+rX "$release" switch_to "$release" sudo -n /usr/bin/systemctl restart "$service" wait_for_service finished=true echo "Deployed $sha to $release"