Spaces:
Sleeping
Sleeping
| # | |
| # validate-submission.sh — OpenEnv Submission Validator | |
| # | |
| # Checks that the Docker image builds, the server boots and responds to /reset, | |
| # and `openenv validate` passes. Works locally (no HF Space required) or against | |
| # a deployed HF Space. | |
| # | |
| # Prerequisites: | |
| # - Docker: https://docs.docker.com/get-docker/ | |
| # - openenv-core: pip install openenv-core | |
| # - curl (usually pre-installed) | |
| # | |
| # Run (local mode — builds image and runs container on localhost): | |
| # ./validate-submission.sh | |
| # ./validate-submission.sh --local [repo_dir] | |
| # | |
| # Run (remote mode — pings an already-deployed HF Space): | |
| # ./validate-submission.sh <ping_url> [repo_dir] | |
| # | |
| # Examples: | |
| # ./validate-submission.sh | |
| # ./validate-submission.sh --local ./my-repo | |
| # ./validate-submission.sh https://my-team.hf.space | |
| # | |
| set -uo pipefail | |
| DOCKER_BUILD_TIMEOUT=600 | |
| if [ -t 1 ]; then | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BOLD='\033[1m' | |
| NC='\033[0m' | |
| else | |
| RED='' GREEN='' YELLOW='' BOLD='' NC='' | |
| fi | |
| run_with_timeout() { | |
| local secs="$1"; shift | |
| if command -v timeout &>/dev/null; then | |
| timeout "$secs" "$@" | |
| elif command -v gtimeout &>/dev/null; then | |
| gtimeout "$secs" "$@" | |
| else | |
| "$@" & | |
| local pid=$! | |
| ( sleep "$secs" && kill "$pid" 2>/dev/null ) & | |
| local watcher=$! | |
| wait "$pid" 2>/dev/null | |
| local rc=$? | |
| kill "$watcher" 2>/dev/null | |
| wait "$watcher" 2>/dev/null | |
| return $rc | |
| fi | |
| } | |
| portable_mktemp() { | |
| local prefix="${1:-validate}" | |
| mktemp "${TMPDIR:-/tmp}/${prefix}-XXXXXX" 2>/dev/null || mktemp | |
| } | |
| CLEANUP_FILES=() | |
| LOCAL_CONTAINER="" | |
| cleanup() { | |
| rm -f "${CLEANUP_FILES[@]+"${CLEANUP_FILES[@]}"}" | |
| if [ -n "$LOCAL_CONTAINER" ]; then | |
| docker rm -f "$LOCAL_CONTAINER" >/dev/null 2>&1 || true | |
| fi | |
| } | |
| trap cleanup EXIT | |
| LOCAL_MODE=false | |
| if [ $# -eq 0 ] || [ "${1:-}" = "--local" ]; then | |
| LOCAL_MODE=true | |
| REPO_DIR="${2:-.}" | |
| PING_URL="" | |
| else | |
| PING_URL="${1:-}" | |
| REPO_DIR="${2:-.}" | |
| fi | |
| if ! REPO_DIR="$(cd "$REPO_DIR" 2>/dev/null && pwd)"; then | |
| printf "Error: directory '%s' not found\n" "${2:-.}" | |
| exit 1 | |
| fi | |
| PING_URL="${PING_URL%/}" | |
| export PING_URL | |
| PASS=0 | |
| log() { printf "[%s] %b\n" "$(date -u +%H:%M:%S)" "$*"; } | |
| pass() { log "${GREEN}PASSED${NC} -- $1"; PASS=$((PASS + 1)); } | |
| fail() { log "${RED}FAILED${NC} -- $1"; } | |
| hint() { printf " ${YELLOW}Hint:${NC} %b\n" "$1"; } | |
| stop_at() { | |
| printf "\n" | |
| printf "${RED}${BOLD}Validation stopped at %s.${NC} Fix the above before continuing.\n" "$1" | |
| exit 1 | |
| } | |
| printf "\n" | |
| printf "${BOLD}========================================${NC}\n" | |
| printf "${BOLD} OpenEnv Submission Validator${NC}\n" | |
| printf "${BOLD}========================================${NC}\n" | |
| log "Repo: $REPO_DIR" | |
| if [ "$LOCAL_MODE" = true ]; then | |
| log "Mode: LOCAL (build + run container on localhost:8000)" | |
| else | |
| log "Mode: REMOTE" | |
| log "Ping URL: $PING_URL" | |
| fi | |
| printf "\n" | |
| log "${BOLD}Step 1/3: Running docker build${NC} ..." | |
| if ! command -v docker &>/dev/null; then | |
| fail "docker command not found" | |
| hint "Install Docker: https://docs.docker.com/get-docker/" | |
| stop_at "Step 1" | |
| fi | |
| if [ -f "$REPO_DIR/Dockerfile" ]; then | |
| DOCKER_CONTEXT="$REPO_DIR" | |
| elif [ -f "$REPO_DIR/server/Dockerfile" ]; then | |
| DOCKER_CONTEXT="$REPO_DIR/server" | |
| else | |
| fail "No Dockerfile found in repo root or server/ directory" | |
| stop_at "Step 1" | |
| fi | |
| log " Found Dockerfile in $DOCKER_CONTEXT" | |
| LOCAL_IMAGE_TAG="cicd-doctor-validate:local" | |
| BUILD_OK=false | |
| BUILD_OUTPUT=$(run_with_timeout "$DOCKER_BUILD_TIMEOUT" docker build -t "$LOCAL_IMAGE_TAG" "$DOCKER_CONTEXT" 2>&1) && BUILD_OK=true | |
| if [ "$BUILD_OK" = true ]; then | |
| pass "Docker build succeeded" | |
| else | |
| fail "Docker build failed (timeout=${DOCKER_BUILD_TIMEOUT}s)" | |
| printf "%s\n" "$BUILD_OUTPUT" | tail -20 | |
| stop_at "Step 1" | |
| fi | |
| if [ "$LOCAL_MODE" = true ]; then | |
| log "${BOLD}Step 2/3: Starting local container${NC} and pinging /reset ..." | |
| LOCAL_PORT=8000 | |
| LOCAL_CONTAINER="cicd-doctor-validate-$$" | |
| PING_URL="http://localhost:${LOCAL_PORT}" | |
| if ! docker run -d --rm --name "$LOCAL_CONTAINER" -p "${LOCAL_PORT}:8000" "$LOCAL_IMAGE_TAG" >/dev/null 2>&1; then | |
| fail "Failed to start container on port ${LOCAL_PORT}" | |
| hint "Is port ${LOCAL_PORT} already in use? Try: lsof -i :${LOCAL_PORT}" | |
| stop_at "Step 2" | |
| fi | |
| log " Container ${LOCAL_CONTAINER} started; waiting for server to come up ..." | |
| CURL_OUTPUT=$(portable_mktemp "validate-curl") | |
| CLEANUP_FILES+=("$CURL_OUTPUT") | |
| HTTP_CODE="000" | |
| for i in $(seq 1 30); do | |
| HTTP_CODE=$(curl -s -o "$CURL_OUTPUT" -w "%{http_code}" -X POST \ | |
| -H "Content-Type: application/json" -d '{}' \ | |
| "$PING_URL/reset" --max-time 5 2>/dev/null || printf "000") | |
| if [ "$HTTP_CODE" = "200" ]; then | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| if [ "$HTTP_CODE" = "200" ]; then | |
| pass "Local container responds to /reset on $PING_URL" | |
| else | |
| fail "Local container did not respond to /reset (last HTTP $HTTP_CODE)" | |
| log " --- container logs (tail) ---" | |
| docker logs "$LOCAL_CONTAINER" 2>&1 | tail -30 | |
| stop_at "Step 2" | |
| fi | |
| else | |
| log "${BOLD}Step 2/3: Pinging HF Space${NC} ($PING_URL/reset) ..." | |
| CURL_OUTPUT=$(portable_mktemp "validate-curl") | |
| CLEANUP_FILES+=("$CURL_OUTPUT") | |
| HTTP_CODE=$(curl -s -o "$CURL_OUTPUT" -w "%{http_code}" -X POST \ | |
| -H "Content-Type: application/json" -d '{}' \ | |
| "$PING_URL/reset" --max-time 30 2>"$CURL_OUTPUT" || printf "000") | |
| if [ "$HTTP_CODE" = "200" ]; then | |
| pass "HF Space is live and responds to /reset" | |
| elif [ "$HTTP_CODE" = "000" ]; then | |
| fail "HF Space not reachable (connection failed or timed out)" | |
| hint "Check your network connection and that the Space is running." | |
| hint "Try: curl -s -o /dev/null -w '%%{http_code}' -X POST $PING_URL/reset" | |
| stop_at "Step 2" | |
| else | |
| fail "HF Space /reset returned HTTP $HTTP_CODE (expected 200)" | |
| hint "Make sure your Space is running and the URL is correct." | |
| hint "Try opening $PING_URL in your browser first." | |
| stop_at "Step 2" | |
| fi | |
| fi | |
| log "${BOLD}Step 3/3: Running openenv validate${NC} ..." | |
| if ! command -v openenv &>/dev/null; then | |
| fail "openenv command not found" | |
| hint "Install it: pip install openenv-core" | |
| stop_at "Step 3" | |
| fi | |
| VALIDATE_OK=false | |
| VALIDATE_OUTPUT=$(cd "$REPO_DIR" && openenv validate 2>&1) && VALIDATE_OK=true | |
| if [ "$VALIDATE_OK" = true ]; then | |
| pass "openenv validate passed" | |
| [ -n "$VALIDATE_OUTPUT" ] && log " $VALIDATE_OUTPUT" | |
| else | |
| fail "openenv validate failed" | |
| printf "%s\n" "$VALIDATE_OUTPUT" | |
| stop_at "Step 3" | |
| fi | |
| printf "\n" | |
| printf "${BOLD}========================================${NC}\n" | |
| printf "${GREEN}${BOLD} All 3/3 checks passed!${NC}\n" | |
| printf "${GREEN}${BOLD} Your submission is ready to submit.${NC}\n" | |
| printf "${BOLD}========================================${NC}\n" | |
| printf "\n" | |
| exit 0 |