#!/usr/bin/env bash set -euo pipefail # Smoke the release exactly as a customer receives it. This intentionally # extracts into a fresh directory and never resolves files from the source tree. # # Usage: scripts/smoke-release-artifact.sh dist/casan--vX.Y.Z.tar.gz # Env: CASAN_ARTIFACT_SMOKE_RUN_TESTS=1 # platform: npm ci + tests + build ARCHIVE="${1:-}" [[ -n "$ARCHIVE" && -f "$ARCHIVE" ]] || { echo "ARTIFACT_SMOKE_FAIL reason=archive_missing path=${ARCHIVE:-unset}" >&2 exit 64 } ARCHIVE="$(cd "$(dirname "$ARCHIVE")" && pwd)/$(basename "$ARCHIVE")" WORK="$(mktemp -d)" cleanup() { rm -rf "$WORK"; } trap cleanup EXIT tar -xzf "$ARCHIVE" -C "$WORK" roots=() while IFS= read -r directory; do roots+=("$directory") done < <(find "$WORK" -mindepth 1 -maxdepth 1 -type d -print) [[ "${#roots[@]}" -eq 1 ]] || { echo "ARTIFACT_SMOKE_FAIL reason=unexpected_root_count count=${#roots[@]}" >&2 exit 1 } ROOT="${roots[0]}" required=(BUNDLE-MANIFEST.txt SHA256SUMS VERSION bin/casan packages/casan-harness) for path in "${required[@]}"; do [[ -e "$ROOT/$path" ]] || { echo "ARTIFACT_SMOKE_FAIL reason=required_path_missing path=$path" >&2 exit 1 } done for path in \ packages/casan-harness/tests \ packages/casan-harness/level5 \ packages/casan-harness/scripts/bash/ci-harness-gate.sh \ packages/casan-harness/scripts/bash/security-gate.sh \ packages/casan-harness/scripts/bash/test-integrity.py \ packages/casan-devkit/tests; do [[ ! -e "$ROOT/$path" ]] || { echo "ARTIFACT_SMOKE_FAIL reason=source_only_path_present path=$path" >&2 exit 1 } done (cd "$ROOT" && shasum -a 256 -c SHA256SUMS >/dev/null) VERSION="$(tr -d '[:space:]' < "$ROOT/VERSION")" CLI_VERSION="$(cd "$ROOT" && ./bin/casan version)" [[ "$CLI_VERSION" == "casan $VERSION" ]] || { echo "ARTIFACT_SMOKE_FAIL reason=cli_version_mismatch expected=$VERSION actual=$CLI_VERSION" >&2 exit 1 } (cd "$ROOT" && ./bin/casan help >/dev/null) STATUS="$(awk '/^status:/ {print $2}' "$ROOT/BUNDLE-MANIFEST.txt")" BUNDLE="$(awk '/^bundle:/ {print $2}' "$ROOT/BUNDLE-MANIFEST.txt")" if [[ "$BUNDLE" == "platform" ]]; then platform_required=( packages/casan-control-panel/backend/package.json packages/casan-control-panel/frontend/package.json Dockerfile.control-panel-api Dockerfile.control-panel-ui docker-compose.control-panel.yml docker-compose.control-panel.local.yml nginx/control-panel.conf infra/production/README.md docs/security/CONTROL_PANEL_AUTH_HARDENING.md ) for path in "${platform_required[@]}"; do [[ -e "$ROOT/$path" ]] || { echo "ARTIFACT_SMOKE_FAIL reason=platform_path_missing path=$path" >&2 exit 1 } done [[ -f "$ROOT/PREVIEW-INCOMPLETE.txt" ]] || { echo "ARTIFACT_SMOKE_FAIL reason=preview_marker_missing" >&2 exit 1 } if [[ "${CASAN_ARTIFACT_SMOKE_RUN_TESTS:-0}" == "1" ]]; then command -v npm >/dev/null 2>&1 || { echo "ARTIFACT_SMOKE_FAIL reason=npm_missing" >&2 exit 1 } (cd "$ROOT" && npm ci && npm run console:test && npm run console:build) fi fi echo "ARTIFACT_SMOKE_PASS bundle=$BUNDLE status=$STATUS version=$VERSION extracted=true checksums=true"