2026-09-11 13:40:14 -04:00
#!/usr/bin/env bash
# Stubbed aws/curl tests for scripts/verify-cloudfront-release.sh.
set -euo pipefail
ROOT = " $( cd " $( dirname " $0 " ) /.. " && pwd ) "
VERIFY = " ${ ROOT } /scripts/verify-cloudfront-release.sh "
CURRENT = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-2-1"
PREVIOUS = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-1-1"
NEW_HASH = "1111111111111111111111111111111111111111111111111111111111111111"
OLD_HASH = "0000000000000000000000000000000000000000000000000000000000000000"
2026-09-11 17:10:51 -04:00
# Vite writes index.html with a trailing newline. Keep it in the fixture so
# the expected hash covers every served byte, exactly like dist/index.html.
INDEX_HTML = $'<!doctype html><html><head><script type="module" src="/assets/app.js"></script></head><body></body></html>\n'
2026-09-11 13:40:14 -04:00
INDEX_HASH = " $( printf '%s' " ${ INDEX_HTML } " | python3 -c 'import hashlib,sys; print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest())' ) "
failures = 0
assert_exit( ) {
local name = " $1 " expected = " $2 " got = " $3 " log = " $4 "
if [ [ " ${ got } " != " ${ expected } " ] ] ; then
echo " FAIL: ${ name } : expected exit ${ expected } , got ${ got } " >& 2
sed -n '1,80p' " ${ log } " >& 2
failures = $(( failures + 1 ))
else
echo " PASS: ${ name } "
fi
}
make_stubs( ) {
local bin = " $1 "
mkdir -p " ${ bin } "
cat > " ${ bin } /aws " << 'AWS'
#!/usr/bin/env bash
set -euo pipefail
state_dir = " ${ STUB_STATE } "
if [ [ " ${ 1 :- } " = = "s3" ] ] ; then
cat " ${ state_dir } /pointer.json "
exit 0
fi
cat " ${ state_dir } /distribution.json "
AWS
cat > " ${ bin } /curl " << 'CURL'
#!/usr/bin/env bash
set -euo pipefail
state_dir = " ${ STUB_STATE } "
method = "GET"
url = ""
dump = ""
output = ""
write_out = ""
args = ( " $@ " )
i = 0
while [ [ $i -lt ${# args [@] } ] ] ; do
arg = " ${ args [ $i ] } "
case " ${ arg } " in
-X) i = $(( i + 1 )) ; method = " ${ args [ $i ] } " ; ;
-D) i = $(( i + 1 )) ; dump = " ${ args [ $i ] } " ; ;
-o) i = $(( i + 1 )) ; output = " ${ args [ $i ] } " ; ;
-w) i = $(( i + 1 )) ; write_out = " ${ args [ $i ] } " ; ;
-H| --max-time| -s| -S| -f| -fsS| -sS) ; ;
http*) url = " ${ arg } " ; ;
esac
i = $(( i + 1 ))
done
if [ [ " ${ method } " = = "OPTIONS" ] ] ; then
[ [ -n " ${ dump } " ] ] && printf 'HTTP/1.1 204 No Content\nAccess-Control-Allow-Origin: https://dev.seahaven.com\n\n' > " ${ dump } "
[ [ -n " ${ write_out } " ] ] && printf '204'
exit 0
fi
if [ [ " ${ url } " = = *"/assets/" * ] ] ; then
[ [ -n " ${ dump } " ] ] && printf 'HTTP/1.1 200 OK\nCache-Control: public,max-age=31536000,immutable\n\n' > " ${ dump } "
2026-09-11 15:38:07 -04:00
if [ [ -f " ${ state_dir } /asset.js " ] ] ; then
body = " $( cat " ${ state_dir } /asset.js " ) "
else
body = 'const api="https://api.dev.seahaven.com/api";'
fi
[ [ -n " ${ output } " ] ] && printf '%s' " ${ body } " > " ${ output } "
[ [ -z " ${ output } " ] ] && printf '%s' " ${ body } "
2026-09-11 13:40:14 -04:00
exit 0
fi
2026-09-11 17:10:51 -04:00
# Serve index.html byte-for-byte, trailing newline included, like real curl.
2026-09-11 13:40:14 -04:00
[ [ -n " ${ dump } " ] ] && printf 'HTTP/1.1 200 OK\nCache-Control: no-cache,no-store,must-revalidate\n\n' > " ${ dump } "
if [ [ -n " ${ output } " ] ] ; then
2026-09-11 17:10:51 -04:00
cat " ${ state_dir } /index.html " > " ${ output } "
2026-09-11 13:40:14 -04:00
else
2026-09-11 17:10:51 -04:00
cat " ${ state_dir } /index.html "
2026-09-11 13:40:14 -04:00
fi
exit 0
CURL
chmod +x " ${ bin } /aws " " ${ bin } /curl "
}
dist_json( ) {
local status = " $1 " current_path = " $2 "
python3 -c ' import json,sys
status, path = sys.argv[ 1] , sys.argv[ 2]
print( json.dumps( {
"Distribution" : {
"Status" : status,
"DistributionConfig" : {
"Origins" : { "Items" : [
{ "Id" : "current" , "OriginPath" : path} ,
{ "Id" : "previous" , "OriginPath" : "" } ,
] }
}
}
} ) ) ' " ${ status } " " ${ current_path } "
}
pointer_json( ) {
python3 -c 'import json,sys; print(json.dumps({"current": sys.argv[1], "previous": sys.argv[2]}))' " $1 " " $2 "
}
run_case( ) {
local name = " $1 "
local dir
dir = " $( mktemp -d) "
make_stubs " ${ dir } /bin "
printf '%s' " ${ INDEX_HTML } " > " ${ dir } /index.html "
export STUB_STATE = " ${ dir } "
export PATH = " ${ dir } /bin: ${ PATH } "
export DISTRIBUTION_ID = "E2CWLM1AFB964P"
export EXPECTED_LABEL = " ${ CURRENT } "
export EXPECTED_INDEX_SHA256 = " ${ NEW_HASH } "
export PREVIOUS_INDEX_SHA256 = " ${ OLD_HASH } "
export SITE_URL = "https://dev.seahaven.com"
export SITE_BUCKET = "seahaven-shoc-frontend-dev"
export BUDGET = 3
export INTERVAL = 0
local log = " ${ dir } /log.txt "
set +e
bash " ${ VERIFY } " > " ${ log } " 2>& 1
local code = $?
set -e
assert_exit " ${ name } " " $2 " " ${ code } " " ${ log } "
rm -rf " ${ dir } "
}
# 1. Right config, then propagates (InProgress -> Deployed, hash already matches).
{
dir = " $( mktemp -d) "
make_stubs " ${ dir } /bin "
printf '%s' " ${ INDEX_HTML } " > " ${ dir } /index.html "
pointer_json " ${ CURRENT } " " ${ PREVIOUS } " > " ${ dir } /pointer.json "
printf 'InProgress\n' > " ${ dir } /status "
cat > " ${ dir } /bin/aws " << AWS
#!/usr/bin/env bash
set -euo pipefail
if [ [ "\${1:-}" = = "s3" ] ] ; then
cat " ${ dir } /pointer.json "
exit 0
fi
status = "\$(cat " ${ dir } /status")"
python3 -c 'import json,sys; print(json.dumps({"Distribution":{"Status":sys.argv[1],"DistributionConfig":{"Origins":{"Items":[{"Id":"current","OriginPath":"/releases/${CURRENT}"},{"Id":"previous","OriginPath":""}]}}}}))' "\${status}"
echo Deployed > " ${ dir } /status "
AWS
chmod +x " ${ dir } /bin/aws "
export STUB_STATE = " ${ dir } " PATH = " ${ dir } /bin: ${ PATH } "
export DISTRIBUTION_ID = "E2CWLM1AFB964P" EXPECTED_LABEL = " ${ CURRENT } "
export EXPECTED_INDEX_SHA256 = " ${ INDEX_HASH } " PREVIOUS_INDEX_SHA256 = " ${ OLD_HASH } "
export SITE_URL = "https://dev.seahaven.com" SITE_BUCKET = "seahaven-shoc-frontend-dev"
export BUDGET = 5 INTERVAL = 0
set +e
bash " ${ VERIFY } " > " ${ dir } /log.txt " 2>& 1
code = $?
set -e
assert_exit "right-config-then-propagates" 0 " ${ code } " " ${ dir } /log.txt "
rm -rf " ${ dir } "
}
# 2. Right config never propagates (Deployed, stale hash).
{
dir = " $( mktemp -d) "
make_stubs " ${ dir } /bin "
pointer_json " ${ CURRENT } " " ${ PREVIOUS } " > " ${ dir } /pointer.json "
dist_json "Deployed" " /releases/ ${ CURRENT } " > " ${ dir } /distribution.json "
printf 'stale' > " ${ dir } /index.html "
export STUB_STATE = " ${ dir } " PATH = " ${ dir } /bin: ${ PATH } "
export DISTRIBUTION_ID = "E2CWLM1AFB964P" EXPECTED_LABEL = " ${ CURRENT } "
export EXPECTED_INDEX_SHA256 = " ${ NEW_HASH } " PREVIOUS_INDEX_SHA256 = " $( printf 'stale' | python3 -c 'import hashlib,sys; print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest())' ) "
export SITE_URL = "https://dev.seahaven.com" SITE_BUCKET = "seahaven-shoc-frontend-dev"
export BUDGET = 2 INTERVAL = 0
set +e
bash " ${ VERIFY } " > " ${ dir } /log.txt " 2>& 1
code = $?
set -e
assert_exit "right-config-never-propagates" 1 " ${ code } " " ${ dir } /log.txt "
grep -q "last observed" " ${ dir } /log.txt " || { echo "FAIL: timeout missing last observed state" >& 2; failures = $(( failures + 1 )) ; }
rm -rf " ${ dir } "
}
# 3. Wrong origin path fails fast.
{
dir = " $( mktemp -d) "
make_stubs " ${ dir } /bin "
pointer_json " ${ CURRENT } " " ${ PREVIOUS } " > " ${ dir } /pointer.json "
dist_json "Deployed" " /releases/ ${ PREVIOUS } " > " ${ dir } /distribution.json "
printf '%s' " ${ INDEX_HTML } " > " ${ dir } /index.html "
export STUB_STATE = " ${ dir } " PATH = " ${ dir } /bin: ${ PATH } "
export DISTRIBUTION_ID = "E2CWLM1AFB964P" EXPECTED_LABEL = " ${ CURRENT } "
export EXPECTED_INDEX_SHA256 = " ${ NEW_HASH } " PREVIOUS_INDEX_SHA256 = " ${ OLD_HASH } "
export SITE_URL = "https://dev.seahaven.com" SITE_BUCKET = "seahaven-shoc-frontend-dev"
export BUDGET = 2 INTERVAL = 0
set +e
bash " ${ VERIFY } " > " ${ dir } /log.txt " 2>& 1
code = $?
set -e
assert_exit "wrong-origin-path" 1 " ${ code } " " ${ dir } /log.txt "
grep -q "origin_path" " ${ dir } /log.txt " || { echo "FAIL: wrong origin path did not name origin_path" >& 2; failures = $(( failures + 1 )) ; }
rm -rf " ${ dir } "
}
# 4. Wrong pointer fails fast.
{
dir = " $( mktemp -d) "
make_stubs " ${ dir } /bin "
pointer_json " ${ PREVIOUS } " " ${ PREVIOUS } " > " ${ dir } /pointer.json "
dist_json "Deployed" " /releases/ ${ CURRENT } " > " ${ dir } /distribution.json "
printf '%s' " ${ INDEX_HTML } " > " ${ dir } /index.html "
export STUB_STATE = " ${ dir } " PATH = " ${ dir } /bin: ${ PATH } "
export DISTRIBUTION_ID = "E2CWLM1AFB964P" EXPECTED_LABEL = " ${ CURRENT } "
export EXPECTED_INDEX_SHA256 = " ${ NEW_HASH } " PREVIOUS_INDEX_SHA256 = " ${ OLD_HASH } "
export SITE_URL = "https://dev.seahaven.com" SITE_BUCKET = "seahaven-shoc-frontend-dev"
export BUDGET = 2 INTERVAL = 0
set +e
bash " ${ VERIFY } " > " ${ dir } /log.txt " 2>& 1
code = $?
set -e
assert_exit "wrong-pointer" 1 " ${ code } " " ${ dir } /log.txt "
grep -q "pointer current" " ${ dir } /log.txt " || { echo "FAIL: wrong pointer did not name pointer current" >& 2; failures = $(( failures + 1 )) ; }
rm -rf " ${ dir } "
}
# 5. Never Deployed.
{
dir = " $( mktemp -d) "
make_stubs " ${ dir } /bin "
pointer_json " ${ CURRENT } " " ${ PREVIOUS } " > " ${ dir } /pointer.json "
dist_json "InProgress" " /releases/ ${ CURRENT } " > " ${ dir } /distribution.json "
printf '%s' " ${ INDEX_HTML } " > " ${ dir } /index.html "
export STUB_STATE = " ${ dir } " PATH = " ${ dir } /bin: ${ PATH } "
export DISTRIBUTION_ID = "E2CWLM1AFB964P" EXPECTED_LABEL = " ${ CURRENT } "
export EXPECTED_INDEX_SHA256 = " ${ NEW_HASH } " PREVIOUS_INDEX_SHA256 = " ${ OLD_HASH } "
export SITE_URL = "https://dev.seahaven.com" SITE_BUCKET = "seahaven-shoc-frontend-dev"
export BUDGET = 2 INTERVAL = 0
set +e
bash " ${ VERIFY } " > " ${ dir } /log.txt " 2>& 1
code = $?
set -e
assert_exit "never-deployed" 1 " ${ code } " " ${ dir } /log.txt "
grep -q "last observed" " ${ dir } /log.txt " || { echo "FAIL: never-deployed missing last observed state" >& 2; failures = $(( failures + 1 )) ; }
rm -rf " ${ dir } "
}
2026-09-11 15:38:07 -04:00
# 6. Hash-matched Deployed release whose JS assets omit the baked API URL.
{
dir = " $( mktemp -d) "
make_stubs " ${ dir } /bin "
pointer_json " ${ CURRENT } " " ${ PREVIOUS } " > " ${ dir } /pointer.json "
dist_json "Deployed" " /releases/ ${ CURRENT } " > " ${ dir } /distribution.json "
printf '%s' " ${ INDEX_HTML } " > " ${ dir } /index.html "
printf 'const x=1;' > " ${ dir } /asset.js "
export STUB_STATE = " ${ dir } " PATH = " ${ dir } /bin: ${ PATH } "
export DISTRIBUTION_ID = "E2CWLM1AFB964P" EXPECTED_LABEL = " ${ CURRENT } "
export EXPECTED_INDEX_SHA256 = " ${ INDEX_HASH } " PREVIOUS_INDEX_SHA256 = " ${ OLD_HASH } "
export SITE_URL = "https://dev.seahaven.com" SITE_BUCKET = "seahaven-shoc-frontend-dev"
export BUDGET = 2 INTERVAL = 0
set +e
bash " ${ VERIFY } " > " ${ dir } /log.txt " 2>& 1
code = $?
set -e
assert_exit "missing-baked-api-url" 1 " ${ code } " " ${ dir } /log.txt "
grep -q "baked dev API URL" " ${ dir } /log.txt " || { echo "FAIL: missing API URL did not name baked dev API URL" >& 2; failures = $(( failures + 1 )) ; }
rm -rf " ${ dir } "
}
# 7. Hash-matched Deployed release whose JS assets contain the staging API URL.
{
dir = " $( mktemp -d) "
make_stubs " ${ dir } /bin "
pointer_json " ${ CURRENT } " " ${ PREVIOUS } " > " ${ dir } /pointer.json "
dist_json "Deployed" " /releases/ ${ CURRENT } " > " ${ dir } /distribution.json "
printf '%s' " ${ INDEX_HTML } " > " ${ dir } /index.html "
printf 'const api="https://api.staging.seahaven.com/api";' > " ${ dir } /asset.js "
export STUB_STATE = " ${ dir } " PATH = " ${ dir } /bin: ${ PATH } "
export DISTRIBUTION_ID = "E2CWLM1AFB964P" EXPECTED_LABEL = " ${ CURRENT } "
export EXPECTED_INDEX_SHA256 = " ${ INDEX_HASH } " PREVIOUS_INDEX_SHA256 = " ${ OLD_HASH } "
export SITE_URL = "https://dev.seahaven.com" SITE_BUCKET = "seahaven-shoc-frontend-dev"
export BUDGET = 2 INTERVAL = 0
set +e
bash " ${ VERIFY } " > " ${ dir } /log.txt " 2>& 1
code = $?
set -e
assert_exit "forbidden-staging-api-url" 1 " ${ code } " " ${ dir } /log.txt "
grep -q "forbidden URL api.staging.seahaven.com" " ${ dir } /log.txt " || { echo "FAIL: staging API URL did not name forbidden URL" >& 2; failures = $(( failures + 1 )) ; }
rm -rf " ${ dir } "
}
2026-09-11 13:40:14 -04:00
if [ [ " ${ failures } " -ne 0 ] ] ; then
echo " FAIL: ${ failures } verify-cloudfront-release cases failed " >& 2
exit 1
fi
echo "PASS: CloudFront release verify checks"