name: Build, Push and Deploy Florale Emotion Website on: push: branches: [ 'feature/*', 'main', 'master' ] pull_request: branches: [ main, master ] env: HARBOR_REGISTRY: registry.julianvollmer.de PROJECT_NAME: florale-emotion jobs: feature-branch: runs-on: ubuntu-latest timeout-minutes: 60 if: startsWith(github.ref, 'refs/heads/feature/') steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 with: driver-opts: | image=moby/buildkit:v0.12.0 buildkitd-flags: --debug - name: Extract metadata id: meta run: | BRANCH_CLEAN=$(echo "${{ github.ref_name }}" | sed 's/[^a-zA-Z0-9._-]/-/g') SHORT_SHA="${{ github.sha }}" SHORT_SHA="${SHORT_SHA:0:8}" TAG="${BRANCH_CLEAN}-${SHORT_SHA}" echo "tag=${TAG}" >> $GITHUB_OUTPUT echo "frontend_image=${{ env.HARBOR_REGISTRY }}/${{ env.PROJECT_NAME }}/florale-emotion-frontend:${TAG}" >> $GITHUB_OUTPUT echo "backend_image=${{ env.HARBOR_REGISTRY }}/${{ env.PROJECT_NAME }}/florale-emotion-backend:${TAG}" >> $GITHUB_OUTPUT echo "bot_image=${{ env.HARBOR_REGISTRY }}/${{ env.PROJECT_NAME }}/florale-emotion-bot:${TAG}" >> $GITHUB_OUTPUT - name: Login to Harbor Registry run: | echo "Harbor12345" | docker login ${{ env.HARBOR_REGISTRY }} -u admin --password-stdin - name: Build and Push Frontend (Feature) working-directory: ./website run: | IMAGE_NAME=${{ steps.meta.outputs.frontend_image }} for attempt in 1 2 3; do echo "Build attempt $attempt for feature frontend..." docker build --no-cache --progress=plain -t "${IMAGE_NAME}" . || { echo "Build failed on attempt $attempt" if [ $attempt -eq 3 ]; then echo "All build attempts failed" exit 1 fi sleep 10 continue } for push_attempt in 1 2 3; do echo "Push attempt $push_attempt for feature frontend..." docker push "${IMAGE_NAME}" && { echo "Feature frontend push successful on attempt $push_attempt" break } || { echo "Push failed on attempt $push_attempt" if [ $push_attempt -eq 3 ]; then echo "All push attempts failed" exit 1 fi sleep 30 } done break done echo "βœ… Feature frontend build and push completed successfully!" echo "🏷️ Tag: ${TAG}" echo "πŸ“¦ Image: ${IMAGE_NAME}" - name: Build and Push Backend (Feature) working-directory: ./website/backend env: IMAGE_NAME: ${{ steps.meta.outputs.backend_image }} TAG: ${{ steps.meta.outputs.tag }} run: | echo "Building backend image: ${IMAGE_NAME}" docker build --no-cache --progress=plain -t "${IMAGE_NAME}" . echo "Pushing backend image: ${IMAGE_NAME}" docker push "${IMAGE_NAME}" echo "βœ… Feature backend build and push completed successfully!" echo "🏷️ Tag: ${TAG}" echo "πŸ“¦ Image: ${IMAGE_NAME}" - name: Build and Push Social Media Bot (Feature) working-directory: ./website/social-media-bot env: IMAGE_NAME: ${{ steps.meta.outputs.bot_image }} TAG: ${{ steps.meta.outputs.tag }} run: | echo "Building social media bot image: ${IMAGE_NAME}" docker build --no-cache --progress=plain -t "${IMAGE_NAME}" . echo "Pushing social media bot image: ${IMAGE_NAME}" docker push "${IMAGE_NAME}" echo "βœ… Feature social media bot build and push completed successfully!" echo "🏷️ Tag: ${TAG}" echo "πŸ“¦ Image: ${IMAGE_NAME}" - name: Setup kubectl uses: azure/setup-kubectl@v3 with: version: 'latest' - name: Configure kubectl env: KUBECTLSECRET: ${{ secrets.KUBECTLSECRET }} run: | mkdir -p ~/.kube echo "πŸ” Debugging KUBECTLSECRET..." SECRET_LENGTH=${#KUBECTLSECRET} echo "Secret length: $SECRET_LENGTH" # Check if secret is empty if [ "$SECRET_LENGTH" -eq 0 ]; then echo "❌ ERROR: KUBECTLSECRET is empty!" echo "Please configure the KUBECTLSECRET in Gitea repository secrets." echo "Steps:" echo "1. Run: cat ~/.kube/config | base64 -w 0" echo "2. Copy the output" echo "3. Add it as 'KUBECTLSECRET' secret in Gitea" exit 1 fi # Try to decode as base64 first, if that fails, use as plain text if echo "$KUBECTLSECRET" | base64 -d > ~/.kube/config 2>/dev/null; then echo "βœ… KUBECTLSECRET decoded as base64" else echo "⚠️ KUBECTLSECRET is not base64, using as plain text" echo "$KUBECTLSECRET" > ~/.kube/config fi echo "πŸ“ kubeconfig created at ~/.kube/config" chmod 600 ~/.kube/config # Validate kubeconfig if [ ! -s ~/.kube/config ]; then echo "❌ ERROR: kubeconfig file is empty!" exit 1 fi echo "βœ… kubeconfig validation passed" - name: Test kubectl connection run: | kubectl version --client kubectl get nodes - name: Deploy Feature Branch env: FRONTEND_IMAGE: ${{ steps.meta.outputs.frontend_image }} BACKEND_IMAGE: ${{ steps.meta.outputs.backend_image }} BOT_IMAGE: ${{ steps.meta.outputs.bot_image }} BRANCH_NAME: ${{ github.ref_name }} run: | echo "πŸš€ Deploying feature branch: $BRANCH_NAME" echo "Frontend image: $FRONTEND_IMAGE" echo "Backend image: $BACKEND_IMAGE" echo "Bot image: $BOT_IMAGE" # Ensure namespace exists kubectl create namespace florale-emotion --dry-run=client -o yaml | kubectl apply -f - # Ensure Harbor registry secret exists kubectl create secret docker-registry harbor-registry-secret \ --docker-server=${{ env.HARBOR_REGISTRY }} \ --docker-username=admin \ --docker-password=Harbor12345 \ --namespace=florale-emotion \ --dry-run=client -o yaml | kubectl apply -f - # Apply frontend deployment und ersetze das Image-Tag dynamisch (kein latest in Features) sed "s|__IMAGE_TAG__|${{ steps.meta.outputs.tag }}|g" website/k8s/frontend.yaml | kubectl apply -n florale-emotion -f - # Apply backend deployment mit Tag-Ersetzung sed "s|__IMAGE_TAG__|${{ steps.meta.outputs.tag }}|g" website/k8s/backend.yaml | kubectl apply -n florale-emotion -f - # Apply social media bot deployment mit Tag-Ersetzung sed "s|__IMAGE_TAG__|${{ steps.meta.outputs.tag }}|g" website/k8s/bot.yaml | kubectl apply -n florale-emotion -f - # Apply feature branch ingress (using dev subdomain) kubectl apply -f website/k8s/ingress-dev.yaml -n florale-emotion # Wait for rollout echo "⏳ Waiting for rollout to finish..." kubectl rollout status deployment/florale-emotion-frontend -n florale-emotion --timeout=300s echo "βœ… Feature branch deployment complete!" echo "🌐 Dev URL: https://dev.florale-emotion.de" echo "πŸ“¦ Harbor Registry: https://registry.julianvollmer.de" production-branch: runs-on: ubuntu-latest timeout-minutes: 60 if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 with: driver-opts: | image=moby/buildkit:v0.12.0 buildkitd-flags: --debug - name: Extract metadata id: meta run: | BRANCH_CLEAN=$(echo "${{ github.ref_name }}" | sed 's/[^a-zA-Z0-9._-]/-/g') SHORT_SHA="${{ github.sha }}" SHORT_SHA="${SHORT_SHA:0:8}" TAG="${BRANCH_CLEAN}-${SHORT_SHA}" # FΓΌr Master-Branches zusΓ€tzlich latest Tag erstellen echo "Master branch detected, will create latest tags" echo "tag=${TAG}" >> $GITHUB_OUTPUT echo "frontend_image=${{ env.HARBOR_REGISTRY }}/${{ env.PROJECT_NAME }}/florale-emotion-frontend:${TAG}" >> $GITHUB_OUTPUT echo "backend_image=${{ env.HARBOR_REGISTRY }}/${{ env.PROJECT_NAME }}/florale-emotion-backend:${TAG}" >> $GITHUB_OUTPUT echo "bot_image=${{ env.HARBOR_REGISTRY }}/${{ env.PROJECT_NAME }}/florale-emotion-bot:${TAG}" >> $GITHUB_OUTPUT - name: Login to Harbor Registry run: | echo "Harbor12345" | docker login ${{ env.HARBOR_REGISTRY }} -u admin --password-stdin - name: Build and Push Frontend (Production + Latest) working-directory: ./website run: | IMAGE_NAME=${{ steps.meta.outputs.frontend_image }} LATEST_IMAGE="${{ env.HARBOR_REGISTRY }}/${{ env.PROJECT_NAME }}/florale-emotion-frontend:latest" for attempt in 1 2 3; do echo "Build attempt $attempt for production frontend..." docker build --no-cache --progress=plain -t "${IMAGE_NAME}" . || { echo "Build failed on attempt $attempt" if [ $attempt -eq 3 ]; then echo "All build attempts failed" exit 1 fi sleep 10 continue } # Push versioned image for push_attempt in 1 2 3; do echo "Push attempt $push_attempt for production frontend..." docker push "${IMAGE_NAME}" && { echo "Production frontend push successful on attempt $push_attempt" break } || { echo "Push failed on attempt $push_attempt" if [ $push_attempt -eq 3 ]; then echo "All push attempts failed" exit 1 fi sleep 30 } done # Create and push latest tag echo "Creating latest tag for production..." docker tag "${IMAGE_NAME}" "${LATEST_IMAGE}" for push_attempt in 1 2 3; do echo "Push attempt $push_attempt for latest tag..." docker push "${LATEST_IMAGE}" && { echo "Latest tag push successful on attempt $push_attempt" break } || { echo "Latest push failed on attempt $push_attempt" if [ $push_attempt -eq 3 ]; then echo "Latest push attempts failed" exit 1 fi sleep 30 } done break done echo "βœ… Production frontend build and push completed successfully!" echo "🏷️ Tag: ${TAG}" echo "πŸ“¦ Image: ${IMAGE_NAME}" echo "πŸ“¦ Latest: ${LATEST_IMAGE}" - name: Build and Push Backend (Production + Latest) working-directory: ./website/backend env: IMAGE_NAME: ${{ steps.meta.outputs.backend_image }} TAG: ${{ steps.meta.outputs.tag }} run: | echo "Building backend image: ${IMAGE_NAME}" docker build --no-cache --progress=plain -t "${IMAGE_NAME}" . echo "Pushing backend image: ${IMAGE_NAME}" docker push "${IMAGE_NAME}" LATEST_IMAGE="${IMAGE_NAME%:*}:latest" echo "Tagging backend as latest: ${LATEST_IMAGE}" docker tag "${IMAGE_NAME}" "${LATEST_IMAGE}" docker push "${LATEST_IMAGE}" echo "βœ… Production backend build and push completed successfully!" echo "🏷️ Tag: ${TAG}" echo "πŸ“¦ Image: ${IMAGE_NAME}" echo "πŸ“¦ Latest: ${LATEST_IMAGE}" - name: Build and Push Social Media Bot (Production + Latest) working-directory: ./website/social-media-bot env: IMAGE_NAME: ${{ steps.meta.outputs.bot_image }} TAG: ${{ steps.meta.outputs.tag }} run: | echo "Building social media bot image: ${IMAGE_NAME}" docker build --no-cache --progress=plain -t "${IMAGE_NAME}" . echo "Pushing social media bot image: ${IMAGE_NAME}" docker push "${IMAGE_NAME}" LATEST_IMAGE="${IMAGE_NAME%:*}:latest" echo "Tagging social media bot as latest: ${LATEST_IMAGE}" docker tag "${IMAGE_NAME}" "${LATEST_IMAGE}" docker push "${LATEST_IMAGE}" echo "βœ… Production social media bot build and push completed successfully!" echo "🏷️ Tag: ${TAG}" echo "πŸ“¦ Image: ${IMAGE_NAME}" echo "πŸ“¦ Latest: ${LATEST_IMAGE}" - name: Setup kubectl uses: azure/setup-kubectl@v3 with: version: 'latest' - name: Configure kubectl env: KUBECTLSECRET: ${{ secrets.KUBECTLSECRET }} run: | mkdir -p ~/.kube echo "πŸ” Debugging KUBECTLSECRET..." SECRET_LENGTH=${#KUBECTLSECRET} echo "Secret length: $SECRET_LENGTH" # Check if secret is empty if [ "$SECRET_LENGTH" -eq 0 ]; then echo "❌ ERROR: KUBECTLSECRET is empty!" echo "Please configure the KUBECTLSECRET in Gitea repository secrets." echo "Steps:" echo "1. Run: cat ~/.kube/config | base64 -w 0" echo "2. Copy the output" echo "3. Add it as 'KUBECTLSECRET' secret in Gitea" exit 1 fi # Try to decode as base64 first, if that fails, use as plain text if echo "$KUBECTLSECRET" | base64 -d > ~/.kube/config 2>/dev/null; then echo "βœ… KUBECTLSECRET decoded as base64" else echo "⚠️ KUBECTLSECRET is not base64, using as plain text" echo "$KUBECTLSECRET" > ~/.kube/config fi echo "πŸ“ kubeconfig created at ~/.kube/config" chmod 600 ~/.kube/config # Validate kubeconfig if [ ! -s ~/.kube/config ]; then echo "❌ ERROR: kubeconfig file is empty!" exit 1 fi echo "βœ… kubeconfig validation passed" - name: Test kubectl connection run: | kubectl version --client kubectl get nodes - name: Deploy to Production env: FRONTEND_IMAGE: ${{ steps.meta.outputs.frontend_image }} BACKEND_IMAGE: ${{ steps.meta.outputs.backend_image }} BOT_IMAGE: ${{ steps.meta.outputs.bot_image }} run: | echo "πŸš€ Deploying to production..." echo "Frontend image: $FRONTEND_IMAGE" echo "Backend image: $BACKEND_IMAGE" echo "Bot image: $BOT_IMAGE" # Ensure namespace exists kubectl create namespace florale-emotion --dry-run=client -o yaml | kubectl apply -f - # Ensure Harbor registry secret exists kubectl create secret docker-registry harbor-registry-secret \ --docker-server=${{ env.HARBOR_REGISTRY }} \ --docker-username=admin \ --docker-password=Harbor12345 \ --namespace=florale-emotion \ --dry-run=client -o yaml | kubectl apply -f - # Apply frontend deployment und ersetze das Image-Tag mit latest sed "s|__IMAGE_TAG__|latest|g" website/k8s/frontend.yaml | kubectl apply -n florale-emotion -f - # Apply backend deployment mit latest Tag sed "s|__IMAGE_TAG__|latest|g" website/k8s/backend.yaml | kubectl apply -n florale-emotion -f - # Apply social media bot deployment mit latest Tag sed "s|__IMAGE_TAG__|latest|g" website/k8s/bot.yaml | kubectl apply -n florale-emotion -f - # Apply production ingress kubectl apply -f website/k8s/ingress.yaml -n florale-emotion # Wait for rollout echo "⏳ Waiting for rollout to finish..." kubectl rollout status deployment/florale-emotion-frontend -n florale-emotion --timeout=300s echo "βœ… Production deployment complete!" echo "🌐 Website: https://florale-emotion.de" echo "🌐 WWW: https://www.florale-emotion.de" echo "πŸ”§ Backend API: https://api.florale-emotion.de" # Hinweise: # - Angepasst fΓΌr florale-emotion Projekt # - Namespace: florale-emotion # - Dev-Deployment unter dev.florale-emotion.de # - Production unter florale-emotion.de # - UnterstΓΌtzt Frontend, Backend und Social Media Bot # - Robuste Retry-Mechanismen fΓΌr Build und Push # - Eindeutige Tags fΓΌr bessere Nachverfolgung # - Timeout-Schutz fΓΌr alle Deployments