#!/usr/bin/env bash
# test.sh (e.g. ~/src/posts)
#
# Runs release end to end in a throwaway sandbox: a local bare remote
# instead of Gogs, and stand-in sudo/systemctl that record what they were
# asked to do. Nothing on the real server is touched.
set -euo pipefail
here=$(cd "$(dirname "$0")" && pwd)
src=$(cd "${1:?usage: test.sh }" && pwd)
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
pass=0; fail=0
ok() { echo "ok $*"; pass=$((pass+1)); }
bad() { echo "FAIL $*"; fail=$((fail+1)); }
check() { local what=$1; shift; if "$@"; then ok "$what"; else bad "$what"; fi; }
# Remote: bajdev/posts (go app with content) and bajdev/site (static).
mkdir -p "$T/remote/bajdev" "$T/apps/posts/content" "$T/vhosts/site.example" "$T/bin"
git clone --quiet --bare "$src" "$T/remote/bajdev/posts.git"
git init --quiet -b main "$T/site" && mkdir "$T/site/public" && echo hi > "$T/site/public/index.html"
git -C "$T/site" add . && git -C "$T/site" -c user.name=t -c user.email=t@t commit --quiet -m init
git clone --quiet --bare "$T/site" "$T/remote/bajdev/site.git"
echo stale > "$T/apps/posts/content/removed-post.md"
cat > "$T/sites.json" < "$T/bin/sudo"
printf '#!/bin/sh\necho "$*" >> "%s/systemctl.log"\n[ "$1" = is-active ] && [ -n "$SYSTEMCTL_FAIL" ] && exit 3\nexit 0\n' "$T" > "$T/bin/systemctl"
chmod +x "$T/bin/sudo" "$T/bin/systemctl"
export PATH="$T/bin:$PATH" RELEASE_CONF="$T/sites.json" RELEASE_GIT_BASE="$T/remote/" \
RELEASE_WORK="$T/work" RELEASE_APPS="$T/apps" RELEASE_VHOSTS="$T/vhosts" RELEASE_LOG="$T/deploys.log" \
GIT_AUTHOR_NAME=deploy GIT_AUTHOR_EMAIL=deploy@test GIT_COMMITTER_NAME=deploy GIT_COMMITTER_EMAIL=deploy@test
R="$here/release"
head=$(git -C "$src" rev-parse main)
# 1. A normal release of the Go app.
out=$(DEPLOY_BY="PR merged by bajdev" "$R" bajdev/posts "$head" 2>&1) || { bad "release posts: $out"; }
check "binary built" test -x "$T/apps/posts/posts"
check "no .new left behind" test ! -e "$T/apps/posts/posts.new"
check "content synced" test -f "$T/apps/posts/content/2026-10-24-hello-world.md"
check "deleted posts removed" test ! -e "$T/apps/posts/content/removed-post.md"
check "service restarted" grep -qx "restart goapp@posts" "$T/systemctl.log"
tag1=$(git -C "$T/remote/bajdev/posts.git" tag -l 'deploy/posts/*' | tail -1)
check "deploy tag pushed" test -n "$tag1"
check "tag is on the commit" test "$(git -C "$T/remote/bajdev/posts.git" rev-parse "$tag1^{commit}")" = "$head"
check "log line written" grep -q "posts $head $tag1 PR merged by bajdev" "$T/deploys.log"
# 2. Rollback to that tag (a second later, so the new tag name differs).
sleep 1
out=$(DEPLOY_BY="rollback by bajdev" "$R" bajdev/posts "$tag1" 2>&1) || bad "rollback: $out"
check "rollback logged" grep -q "rollback by bajdev" "$T/deploys.log"
check "rollback tagged" test "$(git -C "$T/remote/bajdev/posts.git" tag -l 'deploy/posts/*' | wc -l)" -eq 2
# 3. Static site.
out=$("$R" bajdev/site main 2>&1) || bad "release site: $out"
check "static files synced" test -f "$T/vhosts/site.example/index.html"
check "static tag pushed" test -n "$(git -C "$T/remote/bajdev/site.git" tag -l 'deploy/site.example/*')"
# 4. Refusals.
refused() { #
local want=$1; shift; local out
if out=$("$R" "$@" 2>&1); then return 1; fi
[[ $out == *"$want"* ]]
}
check "unknown repo refused" refused "unknown repo" bajdev/nope main
check "../ repo name refused" refused "bad repo name" ../x main
check "dot-leading name refused" refused "bad repo name" bajdev/.git main
check "option-like ref refused" refused "bad ref" bajdev/posts --upload-pack=x
check "wrong arg count refused" refused "usage:" bajdev/posts
# 5. A service that fails to start: no tag, no log line.
tags_before=$(git -C "$T/remote/bajdev/posts.git" tag -l 'deploy/posts/*' | wc -l)
lines_before=$(wc -l < "$T/deploys.log")
sleep 1
check "failed start exits non-zero" bash -c '! SYSTEMCTL_FAIL=1 "$0" bajdev/posts main >/dev/null 2>&1' "$R"
check "failed start not tagged" test "$(git -C "$T/remote/bajdev/posts.git" tag -l 'deploy/posts/*' | wc -l)" -eq "$tags_before"
check "failed start not logged" test "$(wc -l < "$T/deploys.log")" -eq "$lines_before"
echo "$pass passed, $fail failed"
[[ $fail -eq 0 ]]