PROBLEM: - PPO/DQN/TFT/MAMBA2 hyperopt stopped at 23/50 trials (46% completion) - Root cause: Optimizer incorrectly divided remaining trials by n_particles - Sequential execution (mutex-locked models) means 1 eval per iteration, not n_particles FIX: - Remove division by n_particles in PSO budget calculation - Each iteration now evaluates exactly 1 trial (sequential execution) - Expected: 3 initial + 47 PSO iterations = 50 trials total ✅ IMPACT: - All hyperopt runs will now complete full trial count - No performance impact (same execution pattern) - Fixes PPO, DQN, TFT, and MAMBA2 hyperopt early termination Files modified: - ml/src/hyperopt/optimizer.rs: Fix budget calculation (lines 320-328) - scripts/validate_gitlab_cicd.sh: Add CI/CD configuration validator - scripts/build_docker_images.sh: Fix entrypoint override for validation Testing: - Code compiles successfully (2m 27s build time) - GitLab CI/CD validator passes all checks - Will be validated in CI/CD pipeline 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
388 lines
12 KiB
Bash
Executable File
388 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
# =============================================================================
|
|
# GitLab CI/CD Configuration Validator
|
|
# =============================================================================
|
|
# Purpose: Validate .gitlab-ci.yml configuration and check prerequisites
|
|
# Usage: ./scripts/validate_gitlab_cicd.sh
|
|
# =============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m'
|
|
|
|
print_header() {
|
|
echo -e "\n${CYAN}==============================================================================${NC}"
|
|
echo -e "${CYAN}$1${NC}"
|
|
echo -e "${CYAN}==============================================================================${NC}\n"
|
|
}
|
|
|
|
print_success() {
|
|
echo -e "${GREEN}[✓]${NC} $1"
|
|
}
|
|
|
|
print_error() {
|
|
echo -e "${RED}[✗]${NC} $1"
|
|
}
|
|
|
|
print_warning() {
|
|
echo -e "${YELLOW}[!]${NC} $1"
|
|
}
|
|
|
|
print_info() {
|
|
echo -e "${BLUE}[i]${NC} $1"
|
|
}
|
|
|
|
# =============================================================================
|
|
# Validation Functions
|
|
# =============================================================================
|
|
|
|
validate_gitlab_ci_file() {
|
|
print_header "1. Validating .gitlab-ci.yml Syntax"
|
|
|
|
if [ ! -f .gitlab-ci.yml ]; then
|
|
print_error ".gitlab-ci.yml not found in current directory"
|
|
return 1
|
|
fi
|
|
print_success ".gitlab-ci.yml file exists"
|
|
|
|
# Check if gitlab-ci-local is installed for local validation
|
|
if command -v gitlab-ci-local &> /dev/null; then
|
|
print_info "Validating syntax with gitlab-ci-local..."
|
|
if gitlab-ci-local --list 2>&1 | grep -q "build:docker"; then
|
|
print_success "Syntax validation passed"
|
|
else
|
|
print_error "Syntax validation failed"
|
|
return 1
|
|
fi
|
|
else
|
|
print_warning "gitlab-ci-local not installed (optional, for local validation)"
|
|
print_info "Install: npm install -g gitlab-ci-local"
|
|
fi
|
|
|
|
# Check required stages
|
|
print_info "Checking required stages..."
|
|
for stage in "build" "test" "deploy"; do
|
|
if grep -q "\- $stage" .gitlab-ci.yml; then
|
|
print_success "Stage '$stage' defined"
|
|
else
|
|
print_error "Stage '$stage' missing"
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
validate_docker_config() {
|
|
print_header "2. Validating Docker Configuration"
|
|
|
|
# Check Docker availability
|
|
if ! command -v docker &> /dev/null; then
|
|
print_error "Docker is not installed"
|
|
return 1
|
|
fi
|
|
print_success "Docker installed: $(docker --version)"
|
|
|
|
# Check Docker daemon
|
|
if ! docker info &> /dev/null; then
|
|
print_error "Docker daemon is not running"
|
|
return 1
|
|
fi
|
|
print_success "Docker daemon running"
|
|
|
|
# Check BuildKit support
|
|
if docker buildx version &> /dev/null; then
|
|
print_success "BuildKit available: $(docker buildx version | head -n1)"
|
|
else
|
|
print_warning "BuildKit not available (will use legacy builder)"
|
|
fi
|
|
|
|
# Check Dockerfile
|
|
if [ -f Dockerfile.foxhunt-build ]; then
|
|
print_success "Dockerfile.foxhunt-build exists"
|
|
else
|
|
print_error "Dockerfile.foxhunt-build not found"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
validate_docker_hub_config() {
|
|
print_header "3. Validating Docker Hub Configuration"
|
|
|
|
# Check if Docker Hub credentials are set (for local testing)
|
|
if [ -n "${DOCKER_HUB_USERNAME:-}" ] && [ -n "${DOCKER_HUB_PASSWORD:-}" ]; then
|
|
print_success "Docker Hub credentials found in environment"
|
|
|
|
# Test Docker Hub login
|
|
print_info "Testing Docker Hub authentication..."
|
|
if echo "$DOCKER_HUB_PASSWORD" | docker login -u "$DOCKER_HUB_USERNAME" --password-stdin docker.io &> /dev/null; then
|
|
print_success "Docker Hub authentication successful"
|
|
else
|
|
print_error "Docker Hub authentication failed"
|
|
return 1
|
|
fi
|
|
else
|
|
print_warning "Docker Hub credentials not set in environment (OK for GitLab CI/CD)"
|
|
print_info "For GitLab CI/CD, set these in: Settings → CI/CD → Variables"
|
|
print_info " - DOCKER_HUB_USERNAME (your Docker Hub username)"
|
|
print_info " - DOCKER_HUB_PASSWORD (Docker Hub access token, NOT password)"
|
|
fi
|
|
|
|
# Check Docker Hub repository
|
|
print_info "Checking Docker Hub repository configuration..."
|
|
REPO=$(grep "DOCKER_HUB_REPO:" .gitlab-ci.yml | awk '{print $2}')
|
|
if [ -n "$REPO" ]; then
|
|
print_success "Docker Hub repository: $REPO"
|
|
else
|
|
print_error "DOCKER_HUB_REPO not defined in .gitlab-ci.yml"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
validate_build_artifacts() {
|
|
print_header "4. Validating Build Artifacts"
|
|
|
|
# Check if hyperopt binaries exist
|
|
print_info "Checking for hyperopt binaries..."
|
|
local binaries=(
|
|
"hyperopt_mamba2_demo"
|
|
"hyperopt_dqn_demo"
|
|
"hyperopt_ppo_demo"
|
|
"hyperopt_tft_demo"
|
|
)
|
|
|
|
local missing_count=0
|
|
for binary in "${binaries[@]}"; do
|
|
if [ -f "target/release/$binary" ]; then
|
|
print_success "$binary exists ($(du -h "target/release/$binary" | cut -f1))"
|
|
else
|
|
print_warning "$binary not found (will be built in CI/CD)"
|
|
((missing_count++))
|
|
fi
|
|
done
|
|
|
|
if [ $missing_count -eq ${#binaries[@]} ]; then
|
|
print_warning "No hyperopt binaries found locally (expected for clean checkout)"
|
|
print_info "Binaries will be built during Docker image build"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
validate_gitlab_runner() {
|
|
print_header "5. Validating GitLab Runner Configuration"
|
|
|
|
# Check if gitlab-runner is installed
|
|
if command -v gitlab-runner &> /dev/null; then
|
|
print_success "GitLab Runner installed: $(gitlab-runner --version | head -n1)"
|
|
|
|
# Check registered runners
|
|
print_info "Checking registered runners..."
|
|
if gitlab-runner list 2>&1 | grep -q "Executor"; then
|
|
print_success "GitLab Runner registered"
|
|
gitlab-runner list 2>&1 | grep "Executor" | while read -r line; do
|
|
print_info " $line"
|
|
done
|
|
else
|
|
print_warning "No GitLab Runners registered locally"
|
|
fi
|
|
else
|
|
print_warning "GitLab Runner not installed locally (OK if using GitLab.com shared runners)"
|
|
print_info "For self-hosted runner: https://docs.gitlab.com/runner/install/"
|
|
fi
|
|
|
|
# Check .gitlab-ci.yml for runner tags
|
|
print_info "Checking runner tags in .gitlab-ci.yml..."
|
|
if grep -q "tags:" .gitlab-ci.yml; then
|
|
grep "tags:" .gitlab-ci.yml | while read -r line; do
|
|
print_info " $line"
|
|
done
|
|
print_success "Runner tags configured"
|
|
else
|
|
print_warning "No runner tags specified (will use any available runner)"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
validate_ci_variables() {
|
|
print_header "6. Validating CI/CD Variables"
|
|
|
|
print_info "Required GitLab CI/CD Variables (set in GitLab UI):"
|
|
print_info " - DOCKER_HUB_USERNAME (required)"
|
|
print_info " - DOCKER_HUB_PASSWORD (required, use access token)"
|
|
|
|
print_info ""
|
|
print_info "Optional CI/CD Variables:"
|
|
print_info " - SLACK_WEBHOOK_URL (for notifications)"
|
|
print_info " - DISCORD_WEBHOOK_URL (for notifications)"
|
|
|
|
# Check if variables are referenced in .gitlab-ci.yml
|
|
if grep -q "DOCKER_HUB_USERNAME" .gitlab-ci.yml && grep -q "DOCKER_HUB_PASSWORD" .gitlab-ci.yml; then
|
|
print_success "Required variables referenced in .gitlab-ci.yml"
|
|
else
|
|
print_error "Required variables not referenced in .gitlab-ci.yml"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
validate_deployment_config() {
|
|
print_header "7. Validating Deployment Configuration"
|
|
|
|
# Check deployment stages
|
|
if grep -q "deploy:runpod:" .gitlab-ci.yml; then
|
|
print_success "RunPod deployment stage configured"
|
|
else
|
|
print_warning "RunPod deployment stage not found"
|
|
fi
|
|
|
|
if grep -q "deploy:runpod-staging:" .gitlab-ci.yml; then
|
|
print_success "Staging deployment stage configured"
|
|
else
|
|
print_warning "Staging deployment stage not found"
|
|
fi
|
|
|
|
# Check manual approval for production
|
|
if grep -q "when: manual" .gitlab-ci.yml; then
|
|
print_success "Manual approval configured for production deployment"
|
|
else
|
|
print_warning "No manual approval for production (all deployments automatic)"
|
|
fi
|
|
|
|
# Check environment configuration
|
|
if grep -q "environment:" .gitlab-ci.yml; then
|
|
print_success "Environment configuration present"
|
|
else
|
|
print_warning "No environment configuration"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
simulate_local_build() {
|
|
print_header "8. Simulating Local Build (Optional)"
|
|
|
|
read -p "Run local Docker build simulation? (y/N) " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
print_info "Skipping local build simulation"
|
|
return 0
|
|
fi
|
|
|
|
print_info "Building Docker image locally (this may take 5-10 minutes)..."
|
|
|
|
export DOCKER_BUILDKIT=1
|
|
export GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
|
export BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
|
|
|
if docker build \
|
|
-f Dockerfile.foxhunt-build \
|
|
-t jgrusewski/foxhunt:test-local \
|
|
--build-arg GIT_COMMIT="$GIT_COMMIT" \
|
|
--build-arg BUILD_DATE="$BUILD_DATE" \
|
|
. 2>&1 | tail -20; then
|
|
print_success "Local Docker build completed"
|
|
|
|
# Validate binaries in image
|
|
print_info "Validating binaries in image..."
|
|
local binaries=(
|
|
"hyperopt_mamba2_demo"
|
|
"hyperopt_dqn_demo"
|
|
"hyperopt_ppo_demo"
|
|
"hyperopt_tft_demo"
|
|
)
|
|
|
|
for binary in "${binaries[@]}"; do
|
|
if docker run --rm --entrypoint="" jgrusewski/foxhunt:test-local test -f "/usr/local/bin/$binary"; then
|
|
print_success "$binary exists in image"
|
|
else
|
|
print_error "$binary missing in image"
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
# Show image size
|
|
local size=$(docker image inspect jgrusewski/foxhunt:test-local --format='{{.Size}}' | awk '{ sum=$1; hum[1024**3]="GB"; hum[1024**2]="MB"; hum[1024]="KB"; for (x=1024**3; x>=1024; x/=1024) { if (sum>=x) { printf "%.2f %s", sum/x, hum[x]; break } }}')
|
|
print_info "Image size: $size"
|
|
|
|
else
|
|
print_error "Local Docker build failed"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# =============================================================================
|
|
# Main Execution
|
|
# =============================================================================
|
|
|
|
main() {
|
|
print_header "GitLab CI/CD Configuration Validator"
|
|
|
|
print_info "Current directory: $(pwd)"
|
|
print_info "Git repository: $(git remote get-url origin 2>/dev/null || echo 'Not a git repository')"
|
|
print_info "Git branch: $(git branch --show-current 2>/dev/null || echo 'Unknown')"
|
|
print_info "Git commit: $(git rev-parse --short HEAD 2>/dev/null || echo 'Unknown')"
|
|
|
|
local exit_code=0
|
|
|
|
# Run all validations
|
|
validate_gitlab_ci_file || exit_code=1
|
|
validate_docker_config || exit_code=1
|
|
validate_docker_hub_config || exit_code=1
|
|
validate_build_artifacts || exit_code=1
|
|
validate_gitlab_runner || exit_code=1
|
|
validate_ci_variables || exit_code=1
|
|
validate_deployment_config || exit_code=1
|
|
|
|
# Optional: simulate local build
|
|
if [ $exit_code -eq 0 ]; then
|
|
simulate_local_build || exit_code=1
|
|
fi
|
|
|
|
# Final summary
|
|
print_header "Validation Summary"
|
|
|
|
if [ $exit_code -eq 0 ]; then
|
|
print_success "All validations passed!"
|
|
print_info ""
|
|
print_info "Next steps:"
|
|
print_info " 1. Set GitLab CI/CD variables in GitLab UI (if not already set)"
|
|
print_info " Settings → CI/CD → Variables"
|
|
print_info " - DOCKER_HUB_USERNAME"
|
|
print_info " - DOCKER_HUB_PASSWORD (Docker Hub access token)"
|
|
print_info ""
|
|
print_info " 2. Push to main branch to trigger pipeline:"
|
|
print_info " git push origin main"
|
|
print_info ""
|
|
print_info " 3. Monitor pipeline in GitLab:"
|
|
print_info " Project → CI/CD → Pipelines"
|
|
print_info ""
|
|
print_info " 4. Manual approval required for production deployment"
|
|
print_info ""
|
|
else
|
|
print_error "Some validations failed"
|
|
print_info ""
|
|
print_info "Please fix the issues above before enabling GitLab CI/CD"
|
|
print_info ""
|
|
fi
|
|
|
|
exit $exit_code
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|