#!/usr/bin/env bash # Verify sia.storage's S3 gateway handles the operations Forgejo needs for LFS. # Requires: awscli >= 2.x, `xxd`, and env vars from ../.env # # Blocks LFS support if any step fails: # 1. PutObject (small: avatar) # 2. Multipart upload (large: 100 MB LFS-shaped blob, 3 parts) # 3. GetObject (read-back) # 4. HeadObject (metadata) # 5. ListObjectsV2 (pagination) # 6. DeleteObject (cleanup) # # Usage: ./smoke-test-s3.sh set -euo pipefail # shellcheck disable=SC1091 [[ -f ../.env ]] && source ../.env : "${SIA_STORAGE_ENDPOINT:?SIA_STORAGE_ENDPOINT not set}" : "${SIA_STORAGE_ACCESS_KEY:?SIA_STORAGE_ACCESS_KEY not set}" : "${SIA_STORAGE_SECRET_KEY:?SIA_STORAGE_SECRET_KEY not set}" : "${SIA_STORAGE_BUCKET:?SIA_STORAGE_BUCKET not set}" export AWS_ACCESS_KEY_ID="$SIA_STORAGE_ACCESS_KEY" export AWS_SECRET_ACCESS_KEY="$SIA_STORAGE_SECRET_KEY" export AWS_DEFAULT_REGION=us-east-1 S3="aws s3api --endpoint-url https://${SIA_STORAGE_ENDPOINT}" S3CLI="aws s3 --endpoint-url https://${SIA_STORAGE_ENDPOINT}" KEY_SMALL="smoke/small.bin" KEY_LARGE="smoke/large-100mb.bin" step() { printf "\n\033[1;36m▸ %s\033[0m\n" "$*"; } ok() { printf " \033[32m✓\033[0m %s\n" "$*"; } die() { printf " \033[31m✗ %s\033[0m\n" "$*"; exit 1; } trap 'rm -f /tmp/smoke-small /tmp/smoke-large /tmp/smoke-large-back' EXIT step "1. PutObject — small blob" head -c 4096 /dev/urandom > /tmp/smoke-small SMALL_HASH=$(sha256sum /tmp/smoke-small | cut -d' ' -f1) $S3CLI cp /tmp/smoke-small "s3://${SIA_STORAGE_BUCKET}/${KEY_SMALL}" >/dev/null || die "PutObject failed" ok "uploaded (sha256=${SMALL_HASH:0:12}…)" step "2. Multipart upload — 100 MB in 3 parts (this is the critical LFS test)" head -c 104857600 /dev/urandom > /tmp/smoke-large LARGE_HASH=$(sha256sum /tmp/smoke-large | cut -d' ' -f1) # Force multipart with a small threshold + chunk size aws configure set default.s3.multipart_threshold 25MB aws configure set default.s3.multipart_chunksize 33MB $S3CLI cp /tmp/smoke-large "s3://${SIA_STORAGE_BUCKET}/${KEY_LARGE}" >/dev/null || die "multipart PUT failed" ok "multipart upload succeeded (sha256=${LARGE_HASH:0:12}…)" step "3. GetObject — read-back the large blob" $S3CLI cp "s3://${SIA_STORAGE_BUCKET}/${KEY_LARGE}" /tmp/smoke-large-back >/dev/null || die "GET failed" BACK_HASH=$(sha256sum /tmp/smoke-large-back | cut -d' ' -f1) [[ "$BACK_HASH" == "$LARGE_HASH" ]] || die "checksum mismatch after multipart round-trip" ok "checksum matches" step "4. HeadObject" SIZE=$($S3 head-object --bucket "$SIA_STORAGE_BUCKET" --key "$KEY_LARGE" --query 'ContentLength' --output text) || die "HEAD failed" [[ "$SIZE" == "104857600" ]] || die "ContentLength mismatch: $SIZE" ok "size = $SIZE" step "5. ListObjectsV2 — prefix scan" COUNT=$($S3 list-objects-v2 --bucket "$SIA_STORAGE_BUCKET" --prefix "smoke/" --query 'length(Contents)' --output text) || die "LIST failed" [[ "$COUNT" -ge 2 ]] || die "expected >=2 objects under smoke/, got $COUNT" ok "found $COUNT objects" step "6. DeleteObject — cleanup" $S3 delete-object --bucket "$SIA_STORAGE_BUCKET" --key "$KEY_SMALL" >/dev/null $S3 delete-object --bucket "$SIA_STORAGE_BUCKET" --key "$KEY_LARGE" >/dev/null ok "deleted test objects" printf "\n\033[1;32mAll checks passed. sia.storage is safe to use as Forgejo's LFS backend.\033[0m\n"