[v0.142.7] Docker Image Build and Push (Production) #505
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Unstract Docker Image Build and Push (Production) | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Docker image tag" | |
| required: true | |
| set_as_latest: | |
| description: "Set as latest release" | |
| type: boolean | |
| default: false | |
| required: false | |
| release: | |
| types: | |
| - created | |
| run-name: "[${{ github.event.release.tag_name || github.event.inputs.tag }}] Docker Image Build and Push (Production)" | |
| jobs: | |
| build-and-push: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| service_name: | |
| [ | |
| backend, | |
| frontend, | |
| platform-service, | |
| prompt-service, | |
| runner, | |
| x2text-service, | |
| ] | |
| steps: | |
| - name: Checkout code for release | |
| if: github.event_name == 'release' | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.release.tag_name }} | |
| - name: Checkout code for branch | |
| if: github.event_name != 'release' | |
| uses: actions/checkout@v4 | |
| # Set up QEMU for ARM64 emulation | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: linux/amd64,linux/arm64/v8 | |
| # Set up Docker Buildx for better caching and multi-arch builds | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| platforms: linux/amd64,linux/arm64/v8 | |
| # Log in to Docker Hub | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| # Set version tag based on event type | |
| - name: Set version tag | |
| id: set-tag | |
| run: echo "DOCKER_VERSION_TAG=${{ github.event.release.tag_name || github.event.inputs.tag }}" >> $GITHUB_ENV | |
| # Set up additional tags for release builds | |
| - name: Set image tags | |
| id: tags | |
| run: | | |
| # Check if service exists in the config | |
| echo "Checking if service ${{ matrix.service_name }} exists in docker-compose.build.yaml" | |
| if ! grep -q "^ ${{ matrix.service_name }}:" ./docker/docker-compose.build.yaml; then | |
| echo "Service ${{ matrix.service_name }} not found in docker-compose.build.yaml" && exit 1 | |
| fi | |
| # Set latest tag for releases or when explicitly requested | |
| echo "SEMVER_IMAGE_TAG=unstract/${{ matrix.service_name }}:${{ env.DOCKER_VERSION_TAG }}" >> $GITHUB_ENV | |
| # Set latest tag if it's a release or if set_as_latest is true | |
| if [ "${{ github.event_name }}" = "release" ] || [ "${{ github.event.inputs.set_as_latest }}" = "true" ]; then | |
| echo "LATEST_IMAGE_TAG=unstract/${{ matrix.service_name }}:latest" >> $GITHUB_ENV | |
| else | |
| echo "LATEST_IMAGE_TAG=" >> $GITHUB_ENV | |
| fi | |
| # Build and push using Docker Bake | |
| - name: Build and push image | |
| uses: docker/bake-action@v5 | |
| env: | |
| VERSION: ${{ env.DOCKER_VERSION_TAG }} | |
| with: | |
| files: ./docker/docker-compose.build.yaml | |
| targets: ${{ matrix.service_name }} | |
| push: true | |
| set: | | |
| *.tags=${{ env.SEMVER_IMAGE_TAG }} | |
| ${{ env.LATEST_IMAGE_TAG && format('*.tags={0}', env.LATEST_IMAGE_TAG) || '' }} | |
| *.context=. | |
| *.args.VERSION=${{ env.DOCKER_VERSION_TAG }} | |
| *.platform=linux/amd64,linux/arm64/v8 | |
| *.cache-from=type=gha,scope=${{ matrix.service_name }} | |
| *.cache-to=type=gha,mode=max,scope=${{ matrix.service_name }} | |
| # Capture build result and write to artifact | |
| - name: Write build status | |
| if: always() | |
| run: | | |
| mkdir -p build-status | |
| cat > build-status/${{ matrix.service_name }}.json << EOF | |
| { | |
| "service": "${{ matrix.service_name }}", | |
| "status": "${{ job.status }}", | |
| "tag": "${{ env.DOCKER_VERSION_TAG }}" | |
| } | |
| EOF | |
| # Upload status for summary job | |
| - name: Upload build status | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-status-${{ matrix.service_name }} | |
| path: build-status/${{ matrix.service_name }}.json | |
| retention-days: 1 | |
| # Summary job that runs after all builds | |
| build-summary: | |
| needs: build-and-push | |
| runs-on: ubuntu-latest | |
| if: always() | |
| steps: | |
| # Download all build status artifacts | |
| - name: Download build statuses | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: build-status-* | |
| merge-multiple: true | |
| path: build-status | |
| - name: Generate build summary | |
| id: summary | |
| run: | | |
| # Initialize variables | |
| TOTAL_SERVICES=6 | |
| OVERALL_RESULT='${{ needs.build-and-push.result }}' | |
| SUCCESS_COUNT=0 | |
| FAILED_COUNT=0 | |
| FAILED_SERVICES="" | |
| SUCCESS_SERVICES="" | |
| # Process individual service results if artifacts exist | |
| if [ -d "build-status" ]; then | |
| for status_file in build-status/*.json; do | |
| if [ -f "$status_file" ]; then | |
| SERVICE=$(jq -r '.service' "$status_file") | |
| STATUS=$(jq -r '.status' "$status_file") | |
| if [ "$STATUS" = "success" ]; then | |
| SUCCESS_COUNT=$((SUCCESS_COUNT + 1)) | |
| SUCCESS_SERVICES="${SUCCESS_SERVICES}${SERVICE}, " | |
| else | |
| FAILED_COUNT=$((FAILED_COUNT + 1)) | |
| FAILED_SERVICES="${FAILED_SERVICES}${SERVICE}, " | |
| fi | |
| fi | |
| done | |
| # Trim trailing comma and space | |
| SUCCESS_SERVICES=${SUCCESS_SERVICES%, } | |
| FAILED_SERVICES=${FAILED_SERVICES%, } | |
| fi | |
| # Set overall status based on results | |
| if [ "$OVERALL_RESULT" = "success" ]; then | |
| SUMMARY_STATUS="✅ All Docker builds successful" | |
| SUMMARY_COLOR="good" | |
| ALL_SUCCESS="true" | |
| elif [ "$OVERALL_RESULT" = "cancelled" ]; then | |
| SUMMARY_STATUS="⚠️ Docker builds cancelled" | |
| SUMMARY_COLOR="warning" | |
| ALL_SUCCESS="false" | |
| else | |
| SUMMARY_STATUS="❌ Some Docker builds failed" | |
| SUMMARY_COLOR="danger" | |
| ALL_SUCCESS="false" | |
| fi | |
| echo "summary_status=$SUMMARY_STATUS" >> $GITHUB_OUTPUT | |
| echo "summary_color=$SUMMARY_COLOR" >> $GITHUB_OUTPUT | |
| echo "total_count=$TOTAL_SERVICES" >> $GITHUB_OUTPUT | |
| echo "all_success=$ALL_SUCCESS" >> $GITHUB_OUTPUT | |
| echo "overall_result=$OVERALL_RESULT" >> $GITHUB_OUTPUT | |
| echo "success_count=$SUCCESS_COUNT" >> $GITHUB_OUTPUT | |
| echo "failed_count=$FAILED_COUNT" >> $GITHUB_OUTPUT | |
| echo "failed_services=$FAILED_SERVICES" >> $GITHUB_OUTPUT | |
| echo "success_services=$SUCCESS_SERVICES" >> $GITHUB_OUTPUT | |
| # Create a concise summary for Slack | |
| if [ "$ALL_SUCCESS" = "true" ]; then | |
| SLACK_DETAILS="All $TOTAL_SERVICES services built successfully ✅" | |
| else | |
| SLACK_DETAILS="❌ Failed: $FAILED_SERVICES | ✅ Succeeded: $SUCCESS_SERVICES" | |
| fi | |
| echo "slack_details=$SLACK_DETAILS" >> $GITHUB_OUTPUT | |
| # Write to GitHub Summary | |
| - name: Write GitHub Summary | |
| run: | | |
| echo "# 🐳 Docker Build Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Status**: ${{ steps.summary.outputs.summary_status }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Tag**: ${{ github.event.release.tag_name || github.event.inputs.tag }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Total Services**: ${{ steps.summary.outputs.total_count }}" >> $GITHUB_STEP_SUMMARY | |
| # Show counts if available | |
| if [ -n "${{ steps.summary.outputs.success_count }}" ] && [ "${{ steps.summary.outputs.success_count }}" != "0" ]; then | |
| echo "**Successful**: ${{ steps.summary.outputs.success_count }}/${{ steps.summary.outputs.total_count }}" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ -n "${{ steps.summary.outputs.failed_count }}" ] && [ "${{ steps.summary.outputs.failed_count }}" != "0" ]; then | |
| echo "**Failed**: ${{ steps.summary.outputs.failed_count }}/${{ steps.summary.outputs.total_count }}" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Show service status table | |
| echo "## Service Build Status" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Process and display individual service statuses | |
| if [ -d "build-status" ]; then | |
| echo "| Service | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|---------|--------|" >> $GITHUB_STEP_SUMMARY | |
| # Define services in order | |
| for service in backend frontend platform-service prompt-service runner x2text-service; do | |
| if [ -f "build-status/${service}.json" ]; then | |
| STATUS=$(jq -r '.status' "build-status/${service}.json") | |
| if [ "$STATUS" = "success" ]; then | |
| echo "| ${service} | ✅ Success |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| ${service} | ❌ Failed |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| else | |
| echo "| ${service} | ⚠️ Unknown |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| done | |
| else | |
| # Fallback if no artifacts (shouldn't happen but just in case) | |
| if [ "${{ steps.summary.outputs.all_success }}" = "true" ]; then | |
| echo "All services built successfully! ✅" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Build workflow result: **${{ steps.summary.outputs.overall_result }}**" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Unable to determine individual service status. Please check the job logs above." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| fi | |
| # Show failed services if any | |
| if [ -n "${{ steps.summary.outputs.failed_services }}" ]; then | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## ❌ Failed Services" >> $GITHUB_STEP_SUMMARY | |
| echo "${{ steps.summary.outputs.failed_services }}" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "---" >> $GITHUB_STEP_SUMMARY | |
| echo "[View Workflow Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY | |
| # Send summary notification to Slack | |
| - name: Send Slack summary notification | |
| if: always() | |
| uses: slackapi/[email protected] | |
| with: | |
| webhook-type: incoming-webhook | |
| payload: | | |
| { | |
| "text": "${{ steps.summary.outputs.summary_status }}", | |
| "attachments": [ | |
| { | |
| "color": "${{ steps.summary.outputs.summary_color }}", | |
| "fields": [ | |
| { | |
| "title": "Docker Tag", | |
| "value": "${{ github.event.release.tag_name || github.event.inputs.tag }}", | |
| "short": true | |
| }, | |
| { | |
| "title": "Total Services", | |
| "value": "${{ steps.summary.outputs.total_count }}", | |
| "short": true | |
| }, | |
| { | |
| "title": "Repository", | |
| "value": "${{ github.repository }}", | |
| "short": true | |
| }, | |
| { | |
| "title": "Workflow", | |
| "value": "${{ github.workflow }}", | |
| "short": true | |
| }, | |
| { | |
| "title": "Build Details", | |
| "value": "${{ steps.summary.outputs.slack_details }}", | |
| "short": false | |
| }, | |
| { | |
| "title": "Workflow Run", | |
| "value": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", | |
| "short": false | |
| } | |
| ], | |
| "footer": "GitHub Actions" | |
| } | |
| ] | |
| } | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} |