#!/usr/bin/env bash set -euo pipefail # ============================================================================== # perf/make-shell-fixture.sh # Archives frontend from into perf/fixtures/shell-/, # computes the build tag the way server.js computeBuildTag() does, # and verifies that it matches expected-tag (or live prod tag). # ============================================================================== if [ $# -lt 1 ]; then echo "Usage: $0 [expected-tag]" >&2 exit 1 fi COMMIT="$1" EXPECTED_TAG="${2:-}" REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" PERF_DIR="$REPO_ROOT/perf" FIXTURES_DIR="$PERF_DIR/fixtures" TMP_DIR="$PERF_DIR/.tmp" mkdir -p "$FIXTURES_DIR" "$TMP_DIR" PROD_URL="https://worship.hesed.sbs/api/version" fetch_prod_tag() { curl -s -S --max-time 10 "$PROD_URL" | node -e ' let data = ""; process.stdin.on("data", chunk => { data += chunk; }); process.stdin.on("end", () => { try { const j = JSON.parse(data); if (j.buildTag) { process.stdout.write(j.buildTag); process.exit(0); } } catch {} process.exit(1); }); ' 2>/dev/null || echo "" } # Verify the commit exists in git if ! git rev-parse --verify "$COMMIT^{commit}" >/dev/null 2>&1; then echo "Error: commit '$COMMIT' not found in git repository" >&2 exit 1 fi RESOLVED_COMMIT="$(git rev-parse --short=7 "$COMMIT")" echo "Extracting frontend from commit $RESOLVED_COMMIT..." STAGING_DIR="$(mktemp -d "$TMP_DIR/fixture-staging-XXXXXX")" cleanup() { rm -rf "$STAGING_DIR" } trap cleanup EXIT # Archive frontend tree into staging dir git archive "$COMMIT" frontend | tar -x -C "$STAGING_DIR" --strip-components=1 # Compute build tag using the exact logic from server/server.js computeBuildTag() COMPUTED_TAG=$(node -e ' const { createHash } = require("crypto"); const { readdirSync, statSync, readFileSync } = require("fs"); function computeTag(dir) { const hash = createHash("sha256"); const walk = (d, prefix) => { for (const name of readdirSync(d).sort()) { const full = `${d}/${name}`; const rel = `${prefix}/${name}`; if (statSync(full).isDirectory()) { walk(full, rel); } else { hash.update(rel); hash.update(readFileSync(full)); } } }; walk(dir, "./public"); return hash.digest("hex").slice(0, 12); } const tag = computeTag(process.argv[1]); process.stdout.write(tag); ' "$STAGING_DIR") echo "Computed build tag for $RESOLVED_COMMIT: $COMPUTED_TAG" # Determine target expected tag if [ -z "$EXPECTED_TAG" ]; then echo "Querying live production tag from $PROD_URL..." EXPECTED_TAG="$(fetch_prod_tag)" if [ -z "$EXPECTED_TAG" ]; then echo "Error: failed to fetch live buildTag from $PROD_URL" >&2 exit 1 fi echo "Live production build tag: $EXPECTED_TAG" # If mismatch, deploy might be in progress: wait 5 minutes and retry once if [ "$COMPUTED_TAG" != "$EXPECTED_TAG" ]; then echo "Tag mismatch detected (computed=$COMPUTED_TAG vs prod=$EXPECTED_TAG)." echo "Deploy may be in progress. Waiting 300 seconds to retry once..." sleep 300 EXPECTED_TAG="$(fetch_prod_tag)" echo "Retried production build tag: $EXPECTED_TAG" fi fi if [ "$COMPUTED_TAG" != "$EXPECTED_TAG" ]; then echo "Error: build tag mismatch! Computed '$COMPUTED_TAG' does not match expected '$EXPECTED_TAG'" >&2 exit 1 fi TARGET_FIXTURE_DIR="$FIXTURES_DIR/shell-$COMPUTED_TAG" echo "Installing fixture into $TARGET_FIXTURE_DIR..." rm -rf "$TARGET_FIXTURE_DIR" mkdir -p "$TARGET_FIXTURE_DIR" cp -a "$STAGING_DIR"/. "$TARGET_FIXTURE_DIR"/ FILE_COUNT=$(find "$TARGET_FIXTURE_DIR" -type f | wc -l) echo "Success: shell fixture created at $TARGET_FIXTURE_DIR with $FILE_COUNT files." echo "Verified commit: $RESOLVED_COMMIT" echo "Verified build tag: $COMPUTED_TAG"