diff --git a/.gitea/workflows/ci-cd.yaml b/.gitea/workflows/ci-cd.yaml index 4825af0..e77bc22 100644 --- a/.gitea/workflows/ci-cd.yaml +++ b/.gitea/workflows/ci-cd.yaml @@ -227,10 +227,53 @@ jobs: source VERSION IMAGE_VERSION="$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}" + # Generate Dockerfile.prod with correct dependency hash + DEPS_HASH="${{ needs.build-cache.outputs.deps_hash }}" + echo "Using dependency hash: $DEPS_HASH" + + # Create Dockerfile.prod with the correct cache image tag + cat > Dockerfile.prod << EOF + # DanceLessonsCoach Production Docker Image + # Generated by CI/CD pipeline with dependency hash: $DEPS_HASH + + # Use the build cache image as base + FROM gitea.arcodange.lab/arcodange/dance-lessons-coach-build-cache:$DEPS_HASH AS builder + + # Final minimal image + FROM alpine:3.18 + + WORKDIR /app + + # Install minimal dependencies + RUN apk add --no-cache ca-certificates tzdata + + # Copy binary from builder + COPY --from=builder /workspace/dance-lessons-coach /app/dance-lessons-coach + + # Copy configuration + COPY config.yaml /app/config.yaml + + # Set permissions + RUN chmod +x /app/dance-lessons-coach + + # Set timezone + ENV TZ=UTC + + # Expose port + EXPOSE 8080 + + # Health check + HEALTHCHECK --interval=30s --timeout=3s \ + CMD wget -q --spider http://localhost:8080/api/health || exit 1 + + # Entry point + ENTRYPOINT ["/app/dance-lessons-coach"] + EOF + TAGS="$IMAGE_VERSION latest ${{ github.sha }}" echo "Building Docker image with tags: $TAGS" - # Use the production Dockerfile that leverages the build cache + # Build the production image docker build -t dance-lessons-coach -f Dockerfile.prod . for TAG in $TAGS; do diff --git a/Dockerfile.prod.template b/Dockerfile.prod.template new file mode 100644 index 0000000..992b74f --- /dev/null +++ b/Dockerfile.prod.template @@ -0,0 +1,36 @@ +# DanceLessonsCoach Production Docker Image +# Minimal image using pre-built binary from CI cache +# Template: Replace {{DEPS_HASH}} with actual dependency hash + +# Use the build cache image as base +FROM gitea.arcodange.lab/arcodange/dance-lessons-coach-build-cache:{{DEPS_HASH}} AS builder + +# Final minimal image +FROM alpine:3.18 + +WORKDIR /app + +# Install minimal dependencies +RUN apk add --no-cache ca-certificates tzdata + +# Copy binary from builder +COPY --from=builder /workspace/dance-lessons-coach /app/dance-lessons-coach + +# Copy configuration +COPY config.yaml /app/config.yaml + +# Set permissions +RUN chmod +x /app/dance-lessons-coach + +# Set timezone +ENV TZ=UTC + +# Expose port +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s \ + CMD wget -q --spider http://localhost:8080/api/health || exit 1 + +# Entry point +ENTRYPOINT ["/app/dance-lessons-coach"] \ No newline at end of file diff --git a/scripts/calculate-deps-hash.sh b/scripts/calculate-deps-hash.sh new file mode 100755 index 0000000..c5af319 --- /dev/null +++ b/scripts/calculate-deps-hash.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Calculate dependency hash for Docker cache tag +# This script calculates the hash used for the build cache image tag + +# Calculate hash of go.mod + go.sum +# Use shasum on macOS, sha256sum on Linux +if command -v sha256sum >/dev/null 2>&1; then + DEPS_HASH=$(sha256sum go.mod go.sum | sha256sum | cut -d' ' -f1 | head -c 12) +else + DEPS_HASH=$(shasum -a 256 go.mod go.sum | shasum -a 256 | cut -d' ' -f1 | head -c 12) +fi + +echo "Dependency hash: $DEPS_HASH" +echo "$DEPS_HASH" + +# Export for use in other scripts +if [ -n "$1" ]; then + echo "DEPS_HASH=$DEPS_HASH" > "$1" + echo "Exported to: $1" +fi \ No newline at end of file diff --git a/scripts/test-build-cache-environment.sh b/scripts/test-build-cache-environment.sh new file mode 100755 index 0000000..dcdb0d9 --- /dev/null +++ b/scripts/test-build-cache-environment.sh @@ -0,0 +1,174 @@ +#!/bin/bash +# Test the build cache environment without local Go installation +# This simulates the Gitea act runner environment + +set -e + +echo "๐Ÿงช Testing Build Cache Environment" +echo "==================================" +echo "" + +# 1. Calculate dependency hash +echo "1. Calculating dependency hash..." +DEPS_HASH=$(./scripts/calculate-deps-hash.sh) +echo "โœ… Dependency hash: $DEPS_HASH" +echo "" + +# 2. Build the build cache image +echo "2. Building build cache image..." +docker build -t dance-lessons-coach-build-cache:$DEPS_HASH -f Dockerfile.build . +echo "โœ… Build cache image built: dance-lessons-coach-build-cache:$DEPS_HASH" +echo "" + +# 3. Test Go environment inside the container +echo "3. Testing Go environment inside container..." +docker run --rm dance-lessons-coach-build-cache:$DEPS_HASH sh -c "go version" +docker run --rm dance-lessons-coach-build-cache:$DEPS_HASH sh -c "which swag" +echo "โœ… Go and swag available in container" +echo "" + +# 4. Test Swagger generation +echo "4. Testing Swagger generation..." +docker run --rm -v "$(pwd):/workspace" -w /workspace dance-lessons-coach-build-cache:$DEPS_HASH sh -c "cd pkg/server && go generate" +if [ -f "pkg/server/docs/swagger.json" ]; then + echo "โœ… Swagger documentation generated successfully" +else + echo "โŒ Swagger documentation generation failed" + exit 1 +fi +echo "" + +# 5. Test Go build +echo "5. Testing Go build..." +docker run --rm -v "$(pwd):/workspace" -w /workspace dance-lessons-coach-build-cache:$DEPS_HASH sh -c "go build ./..." +echo "โœ… Go build successful" +echo "" + +# 6. Test Go test +echo "6. Testing Go test..." +docker run --rm -v "$(pwd):/workspace" -w /workspace dance-lessons-coach-build-cache:$DEPS_HASH sh -c "go test ./... -v" +echo "โœ… Go tests passed" +echo "" + +# 7. Test binary build +echo "7. Testing binary build..." +docker run --rm -v "$(pwd):/workspace" -w /workspace dance-lessons-coach-build-cache:$DEPS_HASH sh -c "go build -o /workspace/dance-lessons-coach ./cmd/server" +if [ -f "dance-lessons-coach" ]; then + echo "โœ… Binary built successfully" + ls -la dance-lessons-coach + rm dance-lessons-coach +else + echo "โŒ Binary build failed" + exit 1 +fi +echo "" + +# 8. Test production Dockerfile with the cache +echo "8. Testing production Dockerfile..." +# First, let's create a temporary Dockerfile.prod with the correct hash +TEMP_DOCKERFILE="Dockerfile.prod.test" +cat > "$TEMP_DOCKERFILE" << EOF +# DanceLessonsCoach Production Docker Image +# Minimal image using pre-built binary from CI cache + +# Use the build cache image as base +FROM dance-lessons-coach-build-cache:$DEPS_HASH AS builder + +# Final minimal image +FROM alpine:3.18 + +WORKDIR /app + +# Install minimal dependencies +RUN apk add --no-cache ca-certificates tzdata + +# Copy binary from builder +COPY --from=builder /workspace/dance-lessons-coach /app/dance-lessons-coach + +# Copy configuration +COPY config.yaml /app/config.yaml + +# Set permissions +RUN chmod +x /app/dance-lessons-coach + +# Set timezone +ENV TZ=UTC + +# Expose port +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s \ + CMD wget -q --spider http://localhost:8080/api/health || exit 1 + +# Entry point +ENTRYPOINT ["/app/dance-lessons-coach"] +EOF + +echo "โœ… Created temporary production Dockerfile with correct hash" +echo "" + +# 9. Build production image +echo "9. Building production image..." +docker build -t dance-lessons-coach-prod:$DEPS_HASH -f "$TEMP_DOCKERFILE" . +echo "โœ… Production image built: dance-lessons-coach-prod:$DEPS_HASH" +echo "" + +# 10. Test production image +echo "10. Testing production image..." +docker run -d -p 8081:8080 --name test-prod-container dance-lessons-coach-prod:$DEPS_HASH +sleep 5 + +# Test health endpoint +if curl -s http://localhost:8081/api/health | grep -q "healthy"; then + echo "โœ… Production container is healthy" +else + echo "โŒ Production container health check failed" + docker logs test-prod-container + docker stop test-prod-container + docker rm test-prod-container + rm "$TEMP_DOCKERFILE" + exit 1 +fi + +# Test greet endpoint +if curl -s http://localhost:8081/api/v1/greet/ | grep -q "Hello"; then + echo "โœ… Production container greet endpoint working" +else + echo "โŒ Production container greet endpoint failed" + docker logs test-prod-container + docker stop test-prod-container + docker rm test-prod-container + rm "$TEMP_DOCKERFILE" + exit 1 +fi + +echo "โœ… Production container is working correctly" +echo "" + +# Clean up +echo "11. Cleaning up..." +docker stop test-prod-container > /dev/null 2>&1 || true +docker rm test-prod-container > /dev/null 2>&1 || true +rm "$TEMP_DOCKERFILE" +echo "โœ… Cleanup complete" +echo "" + +echo "๐ŸŽ‰ All tests passed!" +echo "===================" +echo "" +echo "โœ… Build cache environment is working correctly" +echo "โœ… All Go tools available in container" +echo "โœ… Swagger generation works" +echo "โœ… Go build and test work" +echo "โœ… Production Dockerfile works with cache" +echo "โœ… Production container runs successfully" +echo "" +echo "๐Ÿš€ The build cache is ready for CI/CD use!" +echo "" +echo "๐Ÿ’ก To use this in CI/CD:" +echo " 1. The build-cache job will build: dance-lessons-coach-build-cache:$DEPS_HASH" +echo " 2. The CI pipeline will use: docker run dance-lessons-coach-build-cache:$DEPS_HASH ..." +echo " 3. Production build will use: FROM dance-lessons-coach-build-cache:$DEPS_HASH AS builder" +echo "" +echo "๐Ÿ“Š Dependency hash for this test: $DEPS_HASH" \ No newline at end of file