| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970 |
- #!/usr/bin/env bash
- # release <owner/repo> <commit-or-tag>
- #
- # Builds and deploys one exact commit of a repo listed in sites.json, then
- # tags it deploy/<target>/<UTC time> in Gogs and appends a line to the
- # deploy log. Runs as the deploy user, from deployd or by hand for a
- # rollback. Unknown repos are refused.
- #
- # The RELEASE_* variables exist only so tests can point the script at a
- # sandbox; on the server they are never set.
- set -euo pipefail
- conf=${RELEASE_CONF:-/etc/deployd/sites.json}
- git_base=${RELEASE_GIT_BASE:-git@git.monoaf.com:}
- work_root=${RELEASE_WORK:-/var/lib/deploy/src}
- apps=${RELEASE_APPS:-/opt/apps}
- vhosts=${RELEASE_VHOSTS:-/srv/www/vhosts}
- logfile=${RELEASE_LOG:-/var/log/deploys.log}
- by=${DEPLOY_BY:-manual}
- [[ $# -eq 2 ]] || { echo "usage: release <owner/repo> <commit-or-tag>" >&2; exit 2; }
- repo=$1; ref=$2
- [[ $repo =~ ^[A-Za-z0-9_-][A-Za-z0-9_.-]*/[A-Za-z0-9_-][A-Za-z0-9_.-]*$ ]] || { echo "bad repo name: $repo" >&2; exit 2; }
- [[ $ref =~ ^[A-Za-z0-9_./-]+$ && $ref != -* ]] || { echo "bad ref: $ref" >&2; exit 2; }
- site=$(jq -ce --arg r "$repo" '.[$r]' "$conf") || { echo "unknown repo $repo" >&2; exit 1; }
- kind=$(jq -r .kind <<<"$site")
- # One release at a time, including manual ones.
- mkdir -p "$work_root"
- exec 9>"$work_root/.release.lock"
- flock 9
- work=$work_root/${repo//\//_}
- [[ -d $work ]] || git clone --quiet "$git_base$repo.git" "$work"
- cd "$work"
- git fetch --quiet --tags origin
- git -c advice.detachedHead=false checkout --quiet --detach "$ref"
- sha=$(git rev-parse HEAD)
- [[ -z $(git status --porcelain) ]] || { echo "working tree not clean" >&2; exit 1; }
- case $kind in
- go)
- name=$(jq -r .name <<<"$site")
- [[ $name =~ ^[a-z0-9][a-z0-9-]*$ ]] || { echo "bad app name: $name" >&2; exit 1; }
- CGO_ENABLED=0 go build -trimpath -o "$apps/$name/$name.new" .
- content=$(jq -r '.content // empty' <<<"$site")
- if [[ -n $content ]]; then
- # -rlt, not -a: let the folder's default ACLs set group and mode, so the
- # app (group apps) can always read what lands there.
- rsync -rlt --delete "$content/" "$apps/$name/content/"
- fi
- mv "$apps/$name/$name.new" "$apps/$name/$name"
- sudo -n systemctl restart "goapp@$name"
- sleep 2
- systemctl is-active --quiet "goapp@$name" || { echo "goapp@$name is not active" >&2; exit 1; }
- target=$name ;;
- static)
- host=$(jq -r .host <<<"$site")
- dir=$(jq -r '.dir // "public"' <<<"$site")
- rsync -rlt --delete "$dir/" "$vhosts/$host/"
- target=$host ;;
- *) echo "unknown kind $kind" >&2; exit 1 ;;
- esac
- tag="deploy/$target/$(date -u +%Y%m%dT%H%M%SZ)"
- git tag -a "$tag" -m "Deployed $sha to $target ($by)"
- git push --quiet origin "$tag"
- echo "$(date -Is) $target $sha $tag $by" >> "$logfile"
- echo "released $repo $sha as $tag"
|