#!/usr/bin/env bash set -euo pipefail cd "$(dirname "${BASH_SOURCE[0]}")/.." image=${1:?Usage: bash deploy/smoke-container.sh IMAGE} container= fixture= cleanup() { local status=$? if [[ -n $container ]]; then if ((status != 0)); then docker logs "$container" >&2 || true fi docker rm -f "$container" >/dev/null || true fi if [[ -n $fixture ]]; then docker volume rm "$fixture" >/dev/null || true fi } trap cleanup EXIT fixture=$(docker volume create) docker run --rm -i --network none --user 0:0 --cap-drop ALL \ --mount "type=volume,source=$fixture,target=/fixture" \ --entrypoint /bin/sh "$image" \ -c 'umask 022; cat > /fixture/config.json' < config.example.json container=$(docker create --network none --read-only --cap-drop ALL \ --security-opt no-new-privileges \ --mount "type=volume,source=$fixture,target=/fixture,readonly" \ --tmpfs /app/backend/data:uid=10001,gid=10001,mode=0700 \ --tmpfs /app/logs:uid=10001,gid=10001,mode=0700 \ --tmpfs /tmp:mode=1777 "$image" start --config /fixture/config.json) docker start "$container" >/dev/null for ((attempt = 0; attempt < 30; attempt++)); do if [[ $(docker inspect -f '{{.State.Health.Status}}' "$container") == healthy ]]; then for path in /marketplace /assets/favicon.svg /api/download/latest; do docker exec "$container" curl -fsS --noproxy '*' --max-time 3 -o /dev/null \ "http://127.0.0.1:3000$path" done docker stop --time 20 "$container" >/dev/null [[ $(docker inspect -f '{{.State.ExitCode}}' "$container") == 0 ]] echo 'Image passed frontend, SQLite API, and graceful shutdown checks.' exit 0 fi if [[ $(docker inspect -f '{{.State.Running}}' "$container") != true ]]; then break fi sleep 2 done echo 'Container did not become healthy.' >&2 exit 1