#!/usr/bin/env bash # Fixture tests for tools/site-deploy.sh using a local bare remote and a fake # SSH transport. Covers missing-branch initial publish, identical-tree no-op, # changed-tree publish, and ls-remote transport failure classification. set -euo pipefail cd "$(dirname "$0")/.." || exit 1 fail=0 tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT setup_fixture_repo() { local root=$1 mkdir -p "$root/tools" "$root/site/dist" cp tools/site-deploy.sh "$root/tools/site-deploy.sh" cat > "$root/tools/site-build.sh" <<'EOF' #!/usr/bin/env bash set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" DIST="$ROOT/site/dist" VARIANT="${SITE_DEPLOY_FIXTURE_VARIANT:-base}" rm -rf "$DIST" mkdir -p "$DIST" printf '%s\n' "$VARIANT" > "$DIST/content.txt" printf '%s\n' \ "$VARIANT" "$SITE_SOURCE_REVISION" > "$DIST/index.html" printf '' > "$DIST/.nojekyll" EOF chmod +x "$root/tools/site-build.sh" cat > "$root/fake-ssh.sh" <<'EOF' #!/usr/bin/env bash set -euo pipefail ROOT="$(cd "$(dirname "$0")" && pwd)" REMOTE="$ROOT/remote.git" LOG="$ROOT/ssh.log" SERVICES="$ROOT/ssh-services.log" joined="$*" printf '%s\n' "$joined" >> "$LOG" case "$joined" in *git-upload-pack*) service="git-upload-pack" ;; *git-receive-pack*) service="git-receive-pack" ;; *) echo "fake-ssh: unsupported invocation: $joined" >&2 exit 97 ;; esac printf '%s\n' "$service" >> "$SERVICES" if [ "${FAKE_SSH_FAIL_ON:-}" = "$service" ]; then echo "fake-ssh: forced failure for $service" >&2 exit 66 fi case "$service" in git-upload-pack) exec git-upload-pack "$REMOTE" ;; git-receive-pack) exec git-receive-pack "$REMOTE" ;; esac EOF chmod +x "$root/fake-ssh.sh" git init --bare -q "$root/remote.git" git -C "$root" init -q git -C "$root" config user.name "Fixture User" git -C "$root" config user.email "fixture@example.com" git -C "$root" config core.sshCommand "$root/fake-ssh.sh" git -C "$root" remote add origin "ssh://fixture/placeholder.git" cat > "$root/.gitignore" <<'EOF' remote.git/ site/dist/ ssh.log ssh-services.log EOF printf 'fixture source\n' > "$root/source.txt" git -C "$root" add .gitignore source.txt fake-ssh.sh tools/site-build.sh tools/site-deploy.sh git -C "$root" commit -q -m "fixture source" } run_deploy_ok() { local root=$1 local variant=$2 ( cd "$root" GIT_SSH_VARIANT=simple SITE_DEPLOY_FIXTURE_VARIANT="$variant" bash tools/site-deploy.sh ) 2>&1 } run_deploy_fail() { local root=$1 local variant=$2 local fail_on=$3 ( cd "$root" GIT_SSH_VARIANT=simple SITE_DEPLOY_FIXTURE_VARIANT="$variant" FAKE_SSH_FAIL_ON="$fail_on" \ bash tools/site-deploy.sh ) 2>&1 } remote_head() { git -C "$1/remote.git" rev-parse "refs/heads/$2" } remote_tree() { git -C "$1/remote.git" rev-parse "refs/heads/$2^{tree}" } parent_count() { git -C "$1/remote.git" rev-list --parents -n 1 "refs/heads/$2" | awk '{ print NF - 1 }' } count_service() { local root=$1 local service=$2 if [ ! -f "$root/ssh-services.log" ]; then echo 0 return fi awk -v service="$service" '$0 == service { count++ } END { print count + 0 }' "$root/ssh-services.log" } reset_ssh_logs() { : > "$1/ssh.log" : > "$1/ssh-services.log" } echo "=== fixture: missing branch initial publish ===" root="$tmp/missing-branch" setup_fixture_repo "$root" output="$(run_deploy_ok "$root" base)" || { echo "FAIL fixture missing-branch: expected success" echo "$output" fail=1 } echo "$output" | grep -q "missing remote pages branch origin/pages, initial publish" || { echo "FAIL fixture missing-branch: did not report initial publish" echo "$output" fail=1 } head_before="$(remote_head "$root" pages)" tree_before="$(remote_tree "$root" pages)" [ "$(parent_count "$root" pages)" = "0" ] || { echo "FAIL fixture missing-branch: initial publish commit is not orphaned" fail=1 } [ "$(count_service "$root" git-upload-pack)" = "1" ] || { echo "FAIL fixture missing-branch: ls-remote did not use fake SSH exactly once" fail=1 } [ "$(count_service "$root" git-receive-pack)" = "1" ] || { echo "FAIL fixture missing-branch: push did not use fake SSH exactly once" fail=1 } echo "=== fixture: identical-tree no-op ===" reset_ssh_logs "$root" output="$(run_deploy_ok "$root" base)" || { echo "FAIL fixture identical-tree: expected success" echo "$output" fail=1 } echo "$output" | grep -q "public-site content is current on origin/pages" || { echo "FAIL fixture identical-tree: missing current-content message" echo "$output" fail=1 } echo "$output" | grep -q "nothing to publish" || { echo "FAIL fixture identical-tree: missing no-op message" echo "$output" fail=1 } [ "$head_before" = "$(remote_head "$root" pages)" ] || { echo "FAIL fixture identical-tree: remote head changed on no-op" fail=1 } [ "$(count_service "$root" git-upload-pack)" = "2" ] || { echo "FAIL fixture identical-tree: expected ls-remote + fetch through fake SSH" fail=1 } [ "$(count_service "$root" git-receive-pack)" = "0" ] || { echo "FAIL fixture identical-tree: no-op should not push" fail=1 } echo "=== fixture: changed-tree publish ===" reset_ssh_logs "$root" output="$(run_deploy_ok "$root" changed)" || { echo "FAIL fixture changed-tree: expected success" echo "$output" fail=1 } head_after="$(remote_head "$root" pages)" tree_after="$(remote_tree "$root" pages)" [ "$head_before" != "$head_after" ] || { echo "FAIL fixture changed-tree: remote head did not change" fail=1 } [ "$tree_before" != "$tree_after" ] || { echo "FAIL fixture changed-tree: remote tree did not change" fail=1 } [ "$(parent_count "$root" pages)" = "0" ] || { echo "FAIL fixture changed-tree: publish commit is not orphaned" fail=1 } [ "$(count_service "$root" git-upload-pack)" = "2" ] || { echo "FAIL fixture changed-tree: expected ls-remote + fetch through fake SSH" fail=1 } [ "$(count_service "$root" git-receive-pack)" = "1" ] || { echo "FAIL fixture changed-tree: changed tree did not push exactly once" fail=1 } echo "=== fixture: transport failure is not treated as missing branch ===" root="$tmp/transport-failure" setup_fixture_repo "$root" if output="$(run_deploy_fail "$root" base git-upload-pack)"; then echo "FAIL fixture transport-failure: expected non-zero exit" fail=1 else echo "$output" | grep -q "fake-ssh: forced failure for git-upload-pack" || { echo "FAIL fixture transport-failure: missing forced-failure output" echo "$output" fail=1 } if echo "$output" | grep -q "missing remote pages branch"; then echo "FAIL fixture transport-failure: transport failure was misclassified as missing branch" echo "$output" fail=1 fi fi echo "=== fixture: dirty source tree is not published ===" root="$tmp/dirty-source" setup_fixture_repo "$root" printf 'uncommitted\n' >> "$root/source.txt" if output="$(run_deploy_ok "$root" base)"; then echo "FAIL fixture dirty-source: expected non-zero exit" fail=1 else echo "$output" | grep -q "refusing to publish a dirty source tree" || { echo "FAIL fixture dirty-source: missing dirty-tree refusal" echo "$output" fail=1 } [ ! -e "$root/remote.git/refs/heads/pages" ] || { echo "FAIL fixture dirty-source: pages branch was created" fail=1 } fi if [ "$fail" -ne 0 ]; then echo "site deploy fixtures: FAILED" exit 1 fi echo "site deploy fixtures: OK"