test.sh 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. #!/usr/bin/env bash
  2. # test.sh <path to a Go app repo with content/> (e.g. ~/src/posts)
  3. #
  4. # Runs release end to end in a throwaway sandbox: a local bare remote
  5. # instead of Gogs, and stand-in sudo/systemctl that record what they were
  6. # asked to do. Nothing on the real server is touched.
  7. set -euo pipefail
  8. here=$(cd "$(dirname "$0")" && pwd)
  9. src=$(cd "${1:?usage: test.sh <go app repo>}" && pwd)
  10. T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
  11. pass=0; fail=0
  12. ok() { echo "ok $*"; pass=$((pass+1)); }
  13. bad() { echo "FAIL $*"; fail=$((fail+1)); }
  14. check() { local what=$1; shift; if "$@"; then ok "$what"; else bad "$what"; fi; }
  15. # Remote: bajdev/posts (go app with content) and bajdev/site (static).
  16. mkdir -p "$T/remote/bajdev" "$T/apps/posts/content" "$T/vhosts/site.example" "$T/bin"
  17. git clone --quiet --bare "$src" "$T/remote/bajdev/posts.git"
  18. git init --quiet -b main "$T/site" && mkdir "$T/site/public" && echo hi > "$T/site/public/index.html"
  19. git -C "$T/site" add . && git -C "$T/site" -c user.name=t -c user.email=t@t commit --quiet -m init
  20. git clone --quiet --bare "$T/site" "$T/remote/bajdev/site.git"
  21. echo stale > "$T/apps/posts/content/removed-post.md"
  22. cat > "$T/sites.json" <<EOF
  23. {
  24. "bajdev/posts": { "kind": "go", "name": "posts", "content": "content" },
  25. "bajdev/site": { "kind": "static", "host": "site.example" }
  26. }
  27. EOF
  28. # Stand-ins: sudo just runs the command; systemctl logs and reports active
  29. # unless SYSTEMCTL_FAIL is set.
  30. printf '#!/bin/sh\n[ "$1" = -n ] && shift\nexec "$@"\n' > "$T/bin/sudo"
  31. printf '#!/bin/sh\necho "$*" >> "%s/systemctl.log"\n[ "$1" = is-active ] && [ -n "$SYSTEMCTL_FAIL" ] && exit 3\nexit 0\n' "$T" > "$T/bin/systemctl"
  32. chmod +x "$T/bin/sudo" "$T/bin/systemctl"
  33. export PATH="$T/bin:$PATH" RELEASE_CONF="$T/sites.json" RELEASE_GIT_BASE="$T/remote/" \
  34. RELEASE_WORK="$T/work" RELEASE_APPS="$T/apps" RELEASE_VHOSTS="$T/vhosts" RELEASE_LOG="$T/deploys.log" \
  35. GIT_AUTHOR_NAME=deploy GIT_AUTHOR_EMAIL=deploy@test GIT_COMMITTER_NAME=deploy GIT_COMMITTER_EMAIL=deploy@test
  36. R="$here/release"
  37. head=$(git -C "$src" rev-parse main)
  38. # 1. A normal release of the Go app.
  39. out=$(DEPLOY_BY="PR merged by bajdev" "$R" bajdev/posts "$head" 2>&1) || { bad "release posts: $out"; }
  40. check "binary built" test -x "$T/apps/posts/posts"
  41. check "no .new left behind" test ! -e "$T/apps/posts/posts.new"
  42. check "content synced" test -f "$T/apps/posts/content/2026-10-24-hello-world.md"
  43. check "deleted posts removed" test ! -e "$T/apps/posts/content/removed-post.md"
  44. check "service restarted" grep -qx "restart goapp@posts" "$T/systemctl.log"
  45. tag1=$(git -C "$T/remote/bajdev/posts.git" tag -l 'deploy/posts/*' | tail -1)
  46. check "deploy tag pushed" test -n "$tag1"
  47. check "tag is on the commit" test "$(git -C "$T/remote/bajdev/posts.git" rev-parse "$tag1^{commit}")" = "$head"
  48. check "log line written" grep -q "posts $head $tag1 PR merged by bajdev" "$T/deploys.log"
  49. # 2. Rollback to that tag (a second later, so the new tag name differs).
  50. sleep 1
  51. out=$(DEPLOY_BY="rollback by bajdev" "$R" bajdev/posts "$tag1" 2>&1) || bad "rollback: $out"
  52. check "rollback logged" grep -q "rollback by bajdev" "$T/deploys.log"
  53. check "rollback tagged" test "$(git -C "$T/remote/bajdev/posts.git" tag -l 'deploy/posts/*' | wc -l)" -eq 2
  54. # 3. Static site.
  55. out=$("$R" bajdev/site main 2>&1) || bad "release site: $out"
  56. check "static files synced" test -f "$T/vhosts/site.example/index.html"
  57. check "static tag pushed" test -n "$(git -C "$T/remote/bajdev/site.git" tag -l 'deploy/site.example/*')"
  58. # 4. Refusals.
  59. refused() { # <message the right guard prints> <args...>
  60. local want=$1; shift; local out
  61. if out=$("$R" "$@" 2>&1); then return 1; fi
  62. [[ $out == *"$want"* ]]
  63. }
  64. check "unknown repo refused" refused "unknown repo" bajdev/nope main
  65. check "../ repo name refused" refused "bad repo name" ../x main
  66. check "dot-leading name refused" refused "bad repo name" bajdev/.git main
  67. check "option-like ref refused" refused "bad ref" bajdev/posts --upload-pack=x
  68. check "wrong arg count refused" refused "usage:" bajdev/posts
  69. # 5. A service that fails to start: no tag, no log line.
  70. tags_before=$(git -C "$T/remote/bajdev/posts.git" tag -l 'deploy/posts/*' | wc -l)
  71. lines_before=$(wc -l < "$T/deploys.log")
  72. sleep 1
  73. check "failed start exits non-zero" bash -c '! SYSTEMCTL_FAIL=1 "$0" bajdev/posts main >/dev/null 2>&1' "$R"
  74. check "failed start not tagged" test "$(git -C "$T/remote/bajdev/posts.git" tag -l 'deploy/posts/*' | wc -l)" -eq "$tags_before"
  75. check "failed start not logged" test "$(wc -l < "$T/deploys.log")" -eq "$lines_before"
  76. echo "$pass passed, $fail failed"
  77. [[ $fail -eq 0 ]]