🤖 ci: optimize CI/CD with Docker cache and remove Buildx
Some checks failed
CI/CD Pipeline / CI Pipeline (push) Has been cancelled
CI/CD Pipeline / Build Docker Cache (push) Has been cancelled

This commit is contained in:
2026-04-07 11:52:33 +02:00
parent c6a2d63a6d
commit 3029e93175
3 changed files with 156 additions and 49 deletions

View File

@@ -143,9 +143,6 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Gitea Container Registry
uses: docker/login-action@v3
with:
@@ -162,72 +159,40 @@ jobs:
if docker pull "$IMAGE_NAME" >/dev/null 2>&1; then
echo "✅ Using Docker build cache"
echo "CACHE_AVAILABLE=true" >> $GITHUB_ENV
echo "CACHE_IMAGE=$IMAGE_NAME" >> $GITHUB_ENV
else
echo "⚠️ Building without cache (first run or new dependencies)"
echo "CACHE_AVAILABLE=false" >> $GITHUB_ENV
fi
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.26.1'
cache: true
- name: Install dependencies
run: go mod tidy
# SINGLE swag installation - reused for all steps
- name: Install swag (once)
run: go install github.com/swaggo/swag/cmd/swag@latest
- name: Version bump (main branch only)
if: github.ref == 'refs/heads/main'
run: |
# Analyze last commit message
LAST_COMMIT=$(git log -1 --pretty=%B | head -1)
# Automatic version bump based on commit type
if echo "$LAST_COMMIT" | grep -q "^✨ feat:"; then
echo "🎯 Feature commit detected - bumping MINOR version"
./scripts/version-bump.sh minor
elif echo "$LAST_COMMIT" | grep -q "^🐛 fix:"; then
echo "🐛 Fix commit detected - bumping PATCH version"
./scripts/version-bump.sh patch
elif echo "$LAST_COMMIT" | grep -q "BREAKING CHANGE"; then
echo "💥 Breaking change detected - bumping MAJOR version"
./scripts/version-bump.sh major
else
echo "⏭️ No automatic version bump needed"
fi
- name: Generate Swagger Docs
- name: Generate Swagger Docs using Docker cache
run: |
if [ "${{ env.CACHE_AVAILABLE }}" = "true" ]; then
echo "Running in Docker cache..."
docker run --rm -v "$(pwd):/workspace" -w /workspace dance-lessons-coach-build-cache:latest sh -c "cd pkg/server && go generate"
docker run --rm -v "$(pwd):/workspace" -w /workspace ${{ env.CACHE_IMAGE }} sh -c "cd pkg/server && go generate"
else
echo "Running natively..."
cd pkg/server && go generate
fi
- name: Build all packages
- name: Build all packages using Docker cache
run: |
if [ "${{ env.CACHE_AVAILABLE }}" = "true" ]; then
echo "Running in Docker cache..."
docker run --rm -v "$(pwd):/workspace" -w /workspace dance-lessons-coach-build-cache:latest sh -c "go build ./..."
docker run --rm -v "$(pwd):/workspace" -w /workspace ${{ env.CACHE_IMAGE }} sh -c "go build ./..."
else
echo "Running natively..."
go build ./...
fi
- name: Run tests with coverage
- name: Run tests with coverage using Docker cache
run: |
if [ "${{ env.CACHE_AVAILABLE }}" = "true" ]; then
echo "Running in Docker cache..."
docker run --rm \
-v "$(pwd):/workspace" \
-w /workspace \
dance-lessons-coach-build-cache:latest \
${{ env.CACHE_IMAGE }} \
sh -c "go test ./... -coverprofile=coverage.out -v && go tool cover -func=coverage.out > coverage.txt"
else
echo "Running natively..."
@@ -271,10 +236,6 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.PACKAGES_TOKEN }}
- name: Set up Docker Buildx
if: github.ref == 'refs/heads/main'
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
if: github.ref == 'refs/heads/main'
run: |
@@ -283,7 +244,9 @@ jobs:
TAGS="$IMAGE_VERSION latest ${{ github.sha }}"
echo "Building Docker image with tags: $TAGS"
docker build -t dance-lessons-coach .
# Use the production Dockerfile that leverages the build cache
docker build -t dance-lessons-coach -f Dockerfile.prod .
for TAG in $TAGS; do
IMAGE_NAME="${{ env.CI_REGISTRY }}/${{ env.GITEA_ORG }}/${{ env.GITEA_REPO }}:$TAG"

35
Dockerfile.prod Normal file
View File

@@ -0,0 +1,35 @@
# DanceLessonsCoach Production Docker Image
# Minimal image using pre-built binary from CI cache
# Use the build cache image as base
FROM gitea.arcodange.lab/arcodange/dance-lessons-coach-build-cache:latest 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"]

View File

@@ -31,12 +31,14 @@ This approach is simpler, more reliable, and works consistently with self-signed
## Decision
**Replace Docker Buildx with traditional docker build + push** for the CI/CD pipeline.
**Replace Docker Buildx with traditional docker build + push** for the CI/CD pipeline and implement a two-stage Docker build strategy.
### Implementation
#### 1. Build Cache Strategy
```yaml
# New approach
# Build cache using traditional docker build
- name: Build and push Docker cache image
if: steps.check_cache.outputs.cache_hit == 'false'
run: |
@@ -55,14 +57,121 @@ This approach is simpler, more reliable, and works consistently with self-signed
echo "✅ Build cache image pushed successfully"
```
#### 2. Production Build Strategy
```yaml
# Production build using Dockerfile.prod
- name: Build and push Docker image
if: github.ref == 'refs/heads/main'
run: |
source VERSION
IMAGE_VERSION="$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}"
TAGS="$IMAGE_VERSION latest ${{ github.sha }}"
echo "Building Docker image with tags: $TAGS"
# Use the production Dockerfile that leverages the build cache
docker build -t dance-lessons-coach -f Dockerfile.prod .
for TAG in $TAGS; do
IMAGE_NAME="${{ env.CI_REGISTRY }}/${{ env.GITEA_ORG }}/${{ env.GITEA_REPO }}:$TAG"
echo "Tagging and pushing: $IMAGE_NAME"
docker tag dance-lessons-coach "$IMAGE_NAME"
docker push "$IMAGE_NAME"
done
```
#### 3. Dockerfile Structure
**Dockerfile.build** - Build environment with all dependencies:
```dockerfile
FROM golang:1.26.1-alpine AS builder
# Install build dependencies
RUN apk add --no-cache git bash curl make gcc musl-dev bc grep sed jq ca-certificates
# Install Go tools
RUN go install github.com/swaggo/swag/cmd/swag@latest
# Copy and verify dependencies
COPY go.mod go.sum ./
RUN go mod download && go mod verify
WORKDIR /workspace
```
**Dockerfile.prod** - Minimal production image:
```dockerfile
# Use the build cache image as base
FROM gitea.arcodange.lab/arcodange/dance-lessons-coach-build-cache:latest 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 and entrypoint
RUN chmod +x /app/dance-lessons-coach
ENV TZ=UTC
EXPOSE 8080
ENTRYPOINT ["/app/dance-lessons-coach"]
```
**Dockerfile** - Development Dockerfile (kept for local development):
```dockerfile
# Multi-stage build for development
FROM golang:1.26.1-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . ./
RUN go build -o /dance-lessons-coach ./cmd/server
FROM alpine:3.18
WORKDIR /app
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /dance-lessons-coach /app/dance-lessons-coach
COPY config.yaml /app/config.yaml
RUN chmod +x /app/dance-lessons-coach
ENV TZ=UTC
EXPOSE 8080
ENTRYPOINT ["/app/dance-lessons-coach"]
```
## Benefits
### CI/CD Pipeline Benefits
1. **Simplicity**: Traditional approach is easier to understand and debug
2. **Reliability**: Consistent behavior across different environments
3. **Certificate Handling**: Works seamlessly with self-signed certificates
4. **Performance**: Faster execution without Buildx overhead
5. **Compatibility**: Better compatibility with GitHub Actions environment
### Two-Stage Build Benefits
1. **Separation of Concerns**: Clear separation between build environment and production runtime
2. **Optimized Production Image**: Minimal Alpine-based image with only necessary dependencies
3. **Reusable Build Cache**: Build environment can be reused across multiple CI runs
4. **Faster CI Execution**: Pre-built build cache reduces CI execution time
5. **Consistent Builds**: All builds use the same build environment
### Development vs Production Clarity
1. **Development Dockerfile**: Full build environment for local development
2. **Production Dockerfile**: Minimal runtime environment for deployment
3. **Build Cache Dockerfile**: Optimized build environment for CI/CD
4. **Clear Documentation**: Each Dockerfile has a specific purpose
## Trade-offs
### What We Lose