Files
dance-lessons-coach/scripts/test-local-ci-cd.sh
Gabriel Radureau 9213c97da6 🔧 fix: optimize Docker cache usage by removing redundant go mod tidy
- Remove unnecessary go mod tidy execution when using Docker cache
- Dependencies are already pre-installed in the cache image
- Only run go mod tidy when not using cache (native execution)
- This leverages the true purpose of the Docker build cache

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-07 23:22:30 +02:00

375 lines
14 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Local CI/CD Testing Script
# Simulates the CI/CD pipeline but builds Docker image locally
# Use this for local development and testing without Gitea
set -e
echo "🚀 Local CI/CD Testing"
echo "======================"
echo ""
# 1. Setup
echo "1. Setting up environment..."
if ! command -v go >/dev/null 2>&1; then
echo "❌ Go not found. Please install Go 1.26.1+"
exit 1
fi
if ! command -v docker >/dev/null 2>&1; then
echo "⚠️ Docker not found. Docker build steps will be skipped"
HAS_DOCKER=false
else
HAS_DOCKER=true
# Check for docker compose plugin
if ! docker compose version >/dev/null 2>&1; then
echo "⚠️ Docker Compose plugin not found. Installing..."
sudo apt-get update && sudo apt-get install -y docker-compose-plugin
fi
fi
echo "✅ Environment ready"
echo ""
# 2. Calculate dependency hash (match CI workflow)
echo "2. Calculating dependency hash..."
DEPS_HASH=$(sha256sum go.mod go.sum | sha256sum | cut -d' ' -f1 | head -c 12)
echo "Dependency hash: $DEPS_HASH"
echo "✅ Dependency hash calculated"
echo ""
# 3. Check for Docker cache
echo "3. Checking for Docker build cache..."
if [ "$HAS_DOCKER" = true ]; then
IMAGE_NAME="gitea.arcodange.lab/arcodange/dance-lessons-coach-build-cache:$DEPS_HASH"
# Try to pull the cache image
if docker pull "$IMAGE_NAME" >/dev/null 2>&1; then
echo "✅ Cache hit - using existing build cache"
USE_DOCKER_CACHE=true
else
echo "⚠️ Cache miss - will build without cache"
USE_DOCKER_CACHE=false
fi
else
echo "⚠️ Docker not available - running natively"
USE_DOCKER_CACHE=false
fi
echo ""
# 4. Start PostgreSQL with Docker Compose
echo "4. Starting PostgreSQL..."
if [ "$HAS_DOCKER" = true ]; then
docker compose -f docker-compose.yml up -d postgres
# Wait for PostgreSQL to be ready
echo "Waiting for PostgreSQL to be ready..."
for i in {1..30}; do
if docker exec dance-lessons-coach-postgres pg_isready -U postgres; then
echo "✅ PostgreSQL is ready!"
break
fi
echo "Waiting for PostgreSQL... ($i/30)"
sleep 2
done
# Set PostgreSQL environment variables
export PGHOST="dance-lessons-coach-postgres"
export PGPORT=5432
export PGUSER=postgres
export PGPASSWORD=postgres
export PGDATABASE=dance_lessons_coach_bdd_test
else
echo "⚠️ Docker not available - PostgreSQL tests will be skipped"
fi
echo ""
# 5. Check dependencies
echo "5. Checking dependencies..."
if [ "$USE_DOCKER_CACHE" = true ]; then
echo "✅ Using pre-installed dependencies from Docker cache"
# No need to run go mod tidy - dependencies are already in the cache
else
echo "Running natively - ensuring dependencies are up to date..."
go mod tidy
fi
echo "✅ Dependencies ready"
echo ""
# 6. Generate Swagger Docs
echo "6. Generating Swagger documentation..."
if [ "$USE_DOCKER_CACHE" = true ]; then
echo "Running in Docker Compose container..."
docker compose -f docker-compose.build.yml exec -w /workspace/pkg/server build-cache sh -c "go generate"
else
echo "Running natively..."
cd pkg/server && go generate
cd ../..
fi
echo "✅ Swagger documentation generated"
echo ""
# 7. Build and test
echo "7. Building and testing..."
if [ "$USE_DOCKER_CACHE" = true ]; then
echo "Running in Docker Compose container..."
docker compose -f docker-compose.build.yml exec -w /workspace build-cache sh -c "go build ./..."
else
echo "Running natively..."
go build ./...
fi
echo "✅ Code compiled successfully"
if [ "$USE_DOCKER_CACHE" = true ]; then
echo "Running in Docker Compose container with PostgreSQL..."
docker compose -f docker-compose.build.yml exec \
-e PGHOST=dance-lessons-coach-postgres \
-e PGPORT=5432 \
-e PGUSER=postgres \
-e PGPASSWORD=postgres \
-e PGDATABASE=dance_lessons_coach_bdd_test \
-w /workspace \
build-cache \
sh -c "go test ./... -coverprofile=coverage.out -v && go tool cover -func=coverage.out > coverage.txt"
else
echo "Running natively..."
go test ./... -cover -v
fi
echo "✅ Tests passed"
echo ""
# 8. Build binaries
echo "8. Building binaries..."
if [ "$USE_DOCKER_CACHE" = true ]; then
echo "Running in Docker Compose container..."
docker compose -f docker-compose.build.yml exec -w /workspace build-cache sh -c "./scripts/build.sh"
else
echo "Running natively..."
./scripts/build.sh
fi
echo "✅ Binaries built"
ls -la bin/
echo ""
# 9. Version bump simulation
echo "9. Version bump simulation..."
LAST_COMMIT=$(git log -1 --pretty=%B | head -1)
echo "Last commit: $LAST_COMMIT"
if echo "$LAST_COMMIT" | grep -q "^feat:"; then
echo "🎯 Feature commit detected - would bump MINOR version"
echo "Run: ./scripts/version-bump.sh minor"
elif echo "$LAST_COMMIT" | grep -q "^fix:"; then
echo "🐛 Fix commit detected - would bump PATCH version"
echo "Run: ./scripts/version-bump.sh patch"
elif echo "$LAST_COMMIT" | grep -q "BREAKING CHANGE"; then
echo "💥 Breaking change detected - would bump MAJOR version"
echo "Run: ./scripts/version-bump.sh major"
else
echo "⏭️ No automatic version bump needed"
fi
# Show current version
source VERSION
CURRENT_VERSION="$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}"
echo "📊 Current version: $CURRENT_VERSION"
echo ""
# 7. Local Docker build instructions
if [ "$HAS_DOCKER" = true ]; then
echo "🐳 LOCAL DOCKER BUILD INSTRUCTIONS"
echo "================================"
echo ""
echo "1. Build Docker image locally (development):"
echo " docker build -t dance-lessons-coach:$CURRENT_VERSION ."
echo ""
echo "2. Build production image using docker/Dockerfile.prod:"
echo " # Note: Local docker/Dockerfile.prod uses 'latest' tag for testing"
echo " docker build -t dance-lessons-coach-prod:$CURRENT_VERSION -f docker/Dockerfile.prod ."
echo " # For CI/CD, the workflow generates correct docker/Dockerfile.prod with dependency hash"
echo ""
echo "3. Compare image sizes:"
echo " docker images | grep dance-lessons-coach"
echo ""
echo "4. Tag the image:"
echo " docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest"
echo ""
echo "3. Test the local image (check port availability first):"
echo " docker run -d -p 8080:8080 dance-lessons-coach:$CURRENT_VERSION"
echo " # Or use alternative port if 8080 is in use:"
echo " docker run -d -p 8081:8080 dance-lessons-coach:$CURRENT_VERSION"
echo ""
echo "4. Branch-specific container naming (recommended):"
echo " BRANCH=\"(git rev-parse --abbrev-ref HEAD | tr '/' '-')"
echo " docker run -d -p 8080:8080 --name dance-lessons-coach-\"$BRANCH\" dance-lessons-coach:$CURRENT_VERSION"
echo ""
echo "5. Test API endpoints:"
echo " curl http://localhost:8080/api/health"
echo " curl http://localhost:8080/api/v1/greet/YourName"
echo ""
echo "5. Clean up:"
echo " docker stop <container_id> && docker rm <container_id>"
echo ""
echo "💡 Tip: Use 'docker images' to see your built images"
echo "💡 Use 'docker ps' to see running containers"
echo ""
# Ask if user wants to build Docker image now
read -p "🚀 Do you want to build the Docker image now? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "🐳 Building Docker image..."
read -p "📋 Build (d)development or (p)production image? [d/p]: " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Pp]$ ]]; then
echo "🏗️ Building production image with docker/Dockerfile.prod..."
docker build -t dance-lessons-coach-prod:$CURRENT_VERSION -f docker/Dockerfile.prod .
docker tag dance-lessons-coach-prod:$CURRENT_VERSION dance-lessons-coach-prod:latest
echo "✅ Production Docker image built: dance-lessons-coach-prod:$CURRENT_VERSION"
CONTAINER_IMAGE="dance-lessons-coach-prod:$CURRENT_VERSION"
else
echo "🏗️ Building development image with Dockerfile..."
docker build -t dance-lessons-coach:$CURRENT_VERSION .
docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest
echo "✅ Development Docker image built: dance-lessons-coach:$CURRENT_VERSION"
CONTAINER_IMAGE="dance-lessons-coach:$CURRENT_VERSION"
fi
echo ""
# Check if port 8080 is available
echo "🔍 Checking port availability..."
if lsof -i :8080 > /dev/null 2>&1; then
echo "⚠️ Port 8080 is already in use"
read -p "🚀 Do you want to use a different port? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
read -p "Enter port number (e.g., 8081): " CUSTOM_PORT
echo ""
PORT=$CUSTOM_PORT
else
echo " Using port 8080 anyway (may fail if service is running)"
PORT=8080
fi
else
echo "✅ Port 8080 is available"
PORT=8080
fi
read -p "🚀 Do you want to run the container now on port $PORT? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
# Get current branch name for container naming
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD | tr '/' '-')
CONTAINER_NAME="dance-lessons-coach-$BRANCH_NAME"
echo "🐳 Preparing container '$CONTAINER_NAME' on port $PORT..."
# Remove existing container if it exists
if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then
echo "⚠️ Container '$CONTAINER_NAME' already exists - removing it..."
docker stop "$CONTAINER_NAME" > /dev/null 2>&1 || true
docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true
echo "✅ Old container removed"
fi
# Also remove the generic test container if it exists
if docker ps -a --format '{{.Names}}' | grep -q "^dance-lessons-coach-test$"; then
echo "⚠️ Generic test container exists - removing it..."
docker stop dance-lessons-coach-test > /dev/null 2>&1 || true
docker rm dance-lessons-coach-test > /dev/null 2>&1 || true
echo "✅ Old generic container removed"
fi
echo "🐳 Starting container '$CONTAINER_NAME' on port $PORT..."
docker run -d -p $PORT:8080 --name "$CONTAINER_NAME" "$CONTAINER_IMAGE"
echo "✅ Container '$CONTAINER_NAME' started on port $PORT"
echo ""
# Wait for container to be ready
echo "🕒 Waiting for container to be ready..."
MAX_ATTEMPTS=10
ATTEMPT=1
READY=false
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
READY=true
break
fi
sleep 1
ATTEMPT=$((ATTEMPT + 1))
echo "🕒 Attempt $ATTEMPT/$MAX_ATTEMPTS..."
done
if [ "$READY" = true ]; then
echo "✅ Container is ready!"
else
echo "❌ Container failed to start properly"
echo "📋 Container logs:"
docker logs dance-lessons-coach-test
echo ""
echo "💡 Check container status with: docker ps -a"
echo "💡 View full logs with: docker logs dance-lessons-coach-test"
continue # Skip endpoint testing
fi
echo "📋 Testing endpoints..."
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
echo "✅ Health check passed"
else
echo "❌ Health check failed"
fi
if curl -s http://localhost:$PORT/api/v1/greet/ | grep -q "Hello"; then
echo "✅ Greet endpoint working"
else
echo "❌ Greet endpoint failed"
fi
echo ""
echo "📖 Swagger UI available at: http://localhost:$PORT/swagger/"
echo "💡 Press Ctrl+C to stop the container when done"
echo " Or run: docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME"
fi
fi
else
echo " Docker not available - skipping Docker build instructions"
fi
echo ""
echo "✅ LOCAL CI/CD TEST COMPLETE"
echo "==========================="
echo ""
echo "📋 What was tested:"
echo " ✅ Dependency hash calculation (matching CI workflow)"
echo " ✅ Docker cache detection and usage"
echo " ✅ PostgreSQL service with Docker Compose"
echo " ✅ Go dependencies installation"
echo " ✅ Swagger documentation generation"
echo " ✅ Code compilation"
echo " ✅ Unit tests with coverage"
echo " ✅ Binary build"
echo " ✅ Version bump simulation"
if [ "$HAS_DOCKER" = true ]; then
echo " ✅ Docker build (development and/or production if chosen)"
fi
echo ""
echo "🎯 When ready for production:"
echo " Push to main branch to trigger full CI/CD pipeline"
echo " Docker image will be built and pushed to Gitea Container Registry"
echo ""
echo "💡 Local testing complete! Your changes are ready for CI/CD."
echo "💡 This script now matches the Gitea workflow structure and behavior."
# ⚠️ IMPORTANT: Local Dockerfile.prod uses 'latest' tag for testing only
# ✅ CI/CD workflow generates correct Dockerfile.prod with dependency hash