- Fix step pattern escaping in pkg/bdd/steps/steps.go:80 - Update CI/CD workflow to use run-bdd-tests.sh script - Enhance run-bdd-tests.sh for both local and CI environments - Add strict validation for undefined/pending/skipped steps - Update BDD testing documentation with pattern requirements The CI/CD pipeline now properly validates BDD tests and fails on any undefined, pending, or skipped steps. All 22 BDD scenarios are passing with correct step pattern registration.
312 lines
11 KiB
YAML
312 lines
11 KiB
YAML
---
|
|
# dance-lessons-coach Unified CI/CD Workflow
|
|
# Single, optimized workflow that replaces all previous workflows
|
|
# Fast execution with minimal repetition and maximum artifact sharing
|
|
|
|
name: CI/CD Pipeline
|
|
|
|
on:
|
|
workflow_dispatch: {}
|
|
push:
|
|
branches:
|
|
- main
|
|
- 'ci/**'
|
|
- 'feature/**'
|
|
- 'fix/**'
|
|
- 'refactor/**'
|
|
paths-ignore:
|
|
- 'README.md'
|
|
- 'doc/**'
|
|
- 'adr/**'
|
|
- '.gitea/**'
|
|
- 'documentation/**'
|
|
- '*.md'
|
|
- '.vibe/**'
|
|
- 'features/**'
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
types: [opened, synchronize, reopened, labeled]
|
|
# Only run PR CI if the commit doesn't already have passing branch CI
|
|
if: |
|
|
github.event_name == 'pull_request' &&
|
|
(github.event.action == 'opened' ||
|
|
github.event.action == 'synchronize' ||
|
|
github.event.action == 'reopened')
|
|
paths-ignore:
|
|
- 'README.md'
|
|
- 'doc/**'
|
|
- 'adr/**'
|
|
- '.gitea/**'
|
|
- 'documentation/**'
|
|
- '*.md'
|
|
- '.vibe/**'
|
|
- 'features/**'
|
|
|
|
# cancel any previously-started runs of this workflow on the same branch
|
|
concurrency:
|
|
group: ${{ github.ref }}-${{ github.workflow }}
|
|
cancel-in-progress: true
|
|
|
|
# Arcodange-specific environment variables
|
|
env:
|
|
GITEA_INTERNAL: "https://gitea.arcodange.lab/"
|
|
GITEA_EXTERNAL: "https://gitea.arcodange.fr/"
|
|
GITEA_ORG: "arcodange"
|
|
GITEA_REPO: "dance-lessons-coach"
|
|
CI_REGISTRY: "gitea.arcodange.lab"
|
|
|
|
jobs:
|
|
build-cache:
|
|
name: Build Docker Cache
|
|
runs-on: ubuntu-latest-ca
|
|
if: "!contains(github.event.head_commit.message, '[skip ci]') && github.actor != 'ci-bot'"
|
|
outputs:
|
|
deps_hash: ${{ steps.calculate_hash.outputs.deps_hash }}
|
|
cache_hit: ${{ steps.check_cache.outputs.cache_hit }}
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Calculate dependency hash
|
|
id: calculate_hash
|
|
run: |
|
|
# Calculate hash of go.mod + go.sum + Dockerfile.build (inline, no script needed)
|
|
DEPS_HASH=$(sha256sum go.mod go.sum docker/Dockerfile.build | sha256sum | cut -d' ' -f1 | head -c 12)
|
|
echo "Dependency hash: $DEPS_HASH"
|
|
echo "deps_hash=$DEPS_HASH" >> $GITHUB_OUTPUT
|
|
|
|
- name: Check for existing cache (optimized with fallback)
|
|
id: check_cache
|
|
run: |
|
|
# Check if image exists in registry using optimized approach with fallback
|
|
IMAGE_NAME="${{ env.CI_REGISTRY }}/${{ env.GITEA_ORG }}/${{ env.GITEA_REPO }}-build-cache:${{ steps.calculate_hash.outputs.deps_hash }}"
|
|
|
|
# Fast check using docker manifest inspect (lighter than pull)
|
|
echo "🔍 Checking cache: $IMAGE_NAME"
|
|
|
|
# Try manifest inspect first (fastest method, but experimental)
|
|
if docker manifest inspect "$IMAGE_NAME" >/dev/null 2>&1; then
|
|
echo "✅ Cache hit - using existing build cache (manifest inspect)"
|
|
echo "cache_hit=true" >> $GITHUB_OUTPUT
|
|
else
|
|
# Fallback to docker pull if manifest inspect fails (more reliable)
|
|
echo "⚠️ Manifest inspect failed, falling back to docker pull..."
|
|
if docker pull "$IMAGE_NAME" >/dev/null 2>&1; then
|
|
echo "✅ Cache hit - using existing build cache (fallback: docker pull)"
|
|
echo "cache_hit=true" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "⚠️ Cache miss - will build new cache image"
|
|
echo "cache_hit=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
fi
|
|
|
|
- name: Login to Gitea Container Registry
|
|
if: steps.check_cache.outputs.cache_hit == 'false'
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.CI_REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.PACKAGES_TOKEN }}
|
|
|
|
|
|
|
|
- name: Build and push Docker cache image
|
|
if: steps.check_cache.outputs.cache_hit == 'false'
|
|
run: |
|
|
IMAGE_NAME="${{ env.CI_REGISTRY }}/${{ env.GITEA_ORG }}/${{ env.GITEA_REPO }}-build-cache:${{ steps.calculate_hash.outputs.deps_hash }}"
|
|
echo "Building cache image: $IMAGE_NAME"
|
|
|
|
# Build the image using traditional docker build
|
|
docker build \
|
|
--file docker/Dockerfile.build \
|
|
--tag "$IMAGE_NAME" \
|
|
.
|
|
|
|
# Push the image
|
|
docker push "$IMAGE_NAME"
|
|
|
|
echo "✅ Build cache image pushed successfully"
|
|
|
|
ci-pipeline:
|
|
name: CI Pipeline
|
|
needs: build-cache
|
|
runs-on: ubuntu-latest-ca
|
|
if: "!contains(github.event.head_commit.message, '[skip ci]') && github.actor != 'ci-bot'"
|
|
|
|
container:
|
|
image: ${{ env.CI_REGISTRY }}/${{ env.GITEA_ORG }}/${{ env.GITEA_REPO }}-build-cache:${{ needs.build-cache.outputs.deps_hash }}
|
|
|
|
services:
|
|
postgres:
|
|
image: postgres:15
|
|
env:
|
|
POSTGRES_USER: postgres
|
|
POSTGRES_PASSWORD: postgres
|
|
POSTGRES_DB: dance_lessons_coach_bdd_test
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set database environment variables
|
|
run: |
|
|
echo "DLC_DATABASE_HOST=postgres" >> $GITHUB_ENV
|
|
echo "DLC_DATABASE_PORT=5432" >> $GITHUB_ENV
|
|
echo "DLC_DATABASE_USER=postgres" >> $GITHUB_ENV
|
|
echo "DLC_DATABASE_PASSWORD=postgres" >> $GITHUB_ENV
|
|
echo "DLC_DATABASE_NAME=dance_lessons_coach_bdd_test" >> $GITHUB_ENV
|
|
echo "DLC_DATABASE_SSL_MODE=disable" >> $GITHUB_ENV
|
|
|
|
- name: Generate Swagger Docs
|
|
run: go generate ./pkg/server
|
|
|
|
- name: Build all packages
|
|
run: go build ./...
|
|
|
|
|
|
- name: Wait for PostgreSQL to be ready
|
|
run: |
|
|
echo "Waiting for PostgreSQL to be ready..."
|
|
for i in {1..30}; do
|
|
if pg_isready -h postgres -p 5432 -U postgres -d dance_lessons_coach_bdd_test; then
|
|
echo "✅ PostgreSQL is ready!"
|
|
break
|
|
fi
|
|
echo "Waiting for PostgreSQL... ($i/30)"
|
|
sleep 2
|
|
done
|
|
|
|
# Verify PostgreSQL is accessible
|
|
if ! pg_isready -h postgres -p 5432 -U postgres -d dance_lessons_coach_bdd_test; then
|
|
echo "❌ PostgreSQL failed to start"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Run BDD tests with strict validation
|
|
run: |
|
|
echo "Running BDD tests with strict validation..."
|
|
# Use the run-bdd-tests.sh script which fails on undefined/pending steps
|
|
# In CI environment, PostgreSQL is already running as a service
|
|
export DLC_DATABASE_HOST=postgres
|
|
export DLC_DATABASE_PORT=5432
|
|
export DLC_DATABASE_USER=postgres
|
|
export DLC_DATABASE_PASSWORD=postgres
|
|
export DLC_DATABASE_NAME=dance_lessons_coach_bdd_test
|
|
export DLC_DATABASE_SSL_MODE=disable
|
|
./scripts/run-bdd-tests.sh
|
|
|
|
- name: Run unit tests with coverage
|
|
run: |
|
|
echo "Running unit tests with PostgreSQL service..."
|
|
# Run unit tests excluding BDD tests (already run above)
|
|
go test ./pkg/... ./cmd/... -coverprofile=coverage.out -v && go tool cover -func=coverage.out > coverage.txt
|
|
|
|
# Extract coverage percentage
|
|
COVERAGE=$(grep "total:" coverage.txt | grep -oP '\d+\.\d+' | head -1)
|
|
echo "Coverage: ${COVERAGE}%"
|
|
|
|
# Update coverage badge using script
|
|
export PACKAGES_TOKEN="${{ secrets.PACKAGES_TOKEN }}"
|
|
export GITHUB_REF_NAME="${{ github.ref_name }}"
|
|
./scripts/ci-update-coverage-badge.sh "$COVERAGE"
|
|
|
|
- name: Run go fmt
|
|
run: go fmt ./...
|
|
|
|
- name: Run swag fmt
|
|
run: swag fmt
|
|
|
|
- name: Build binaries
|
|
run: ./scripts/build.sh
|
|
|
|
# NOTE: Artifact upload disabled - actions/upload-artifact@v4 not available on Gitea
|
|
# TODO: Replace with Gitea-specific upload action when available
|
|
# - name: Upload Swagger documentation
|
|
# uses: actions/upload-artifact@v4
|
|
# with:
|
|
# name: swagger-docs
|
|
# path: pkg/server/docs/swagger.json
|
|
# retention-days: 1
|
|
|
|
# Docker build and push (main branch only)
|
|
- name: Login to Gitea Container Registry
|
|
if: github.ref == 'refs/heads/main'
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.CI_REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.PACKAGES_TOKEN }}
|
|
|
|
- name: Build and push Docker image
|
|
if: github.ref == 'refs/heads/main'
|
|
run: |
|
|
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 > docker/Dockerfile.prod << EOF
|
|
# dance-lessons-coach 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"
|
|
|
|
# Build the production image
|
|
docker build -t dance-lessons-coach -f docker/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
|
|
|
|
- name: Show published images
|
|
if: github.ref == 'refs/heads/main'
|
|
run: |
|
|
source VERSION
|
|
IMAGE_VERSION="$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}"
|
|
echo "📦 Published Docker images:"
|
|
echo " - ${{ env.CI_REGISTRY }}/${{ env.GITEA_ORG }}/${{ env.GITEA_REPO }}:$IMAGE_VERSION"
|
|
echo " - ${{ env.CI_REGISTRY }}/${{ env.GITEA_ORG }}/${{ env.GITEA_REPO }}:latest"
|
|
echo " - ${{ env.CI_REGISTRY }}/${{ env.GITEA_ORG }}/${{ env.GITEA_REPO }}:${{ github.sha }}"
|