Enhance Dockerfile and Build Workflow #10
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: Build and Push Images | |
| on: | |
| push: | |
| tags: | |
| - 'api-v*.*.*' # api-v1.2.3 | |
| - 'commit-worker-v*.*.*' # commit-worker-v1.2.3 | |
| - 'user-worker-v*.*.*' # user-worker-v1.2.3 | |
| jobs: | |
| build-and-push: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v3 | |
| - name: Extract service and version | |
| id: extract | |
| run: | | |
| TAG=${GITHUB_REF#refs/tags/} | |
| # Extract service name and version from tag | |
| # api-v1.2.3 -> service=api, version=1.2.3 | |
| if [[ $TAG =~ ^api-v(.+)$ ]]; then | |
| echo "SERVICE=api" >> $GITHUB_OUTPUT | |
| echo "VERSION=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT | |
| echo "DOCKERFILE=backend/Dockerfile.prod" >> $GITHUB_OUTPUT | |
| elif [[ $TAG =~ ^commit-worker-v(.+)$ ]]; then | |
| echo "SERVICE=commit-worker" >> $GITHUB_OUTPUT | |
| echo "VERSION=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT | |
| echo "DOCKERFILE=backend/Dockerfile.cloudrun-commit-worker" >> $GITHUB_OUTPUT | |
| elif [[ $TAG =~ ^user-worker-v(.+)$ ]]; then | |
| echo "SERVICE=user-worker" >> $GITHUB_OUTPUT | |
| echo "VERSION=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT | |
| echo "DOCKERFILE=backend/Dockerfile.cloudrun-user-worker" >> $GITHUB_OUTPUT | |
| else | |
| echo "❌ Error: Invalid tag format" | |
| exit 1 | |
| fi | |
| - name: Authenticate to GCP | |
| uses: google-github-actions/auth@v1 | |
| with: | |
| credentials_json: ${{ secrets.GCP_SA_KEY }} | |
| - name: Configure Docker | |
| run: | | |
| gcloud auth configure-docker ${{ secrets.GCP_REGION }}-docker.pkg.dev | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v2 | |
| - name: Verify package-lock.json exists | |
| run: | | |
| if [ ! -f backend/package-lock.json ]; then | |
| echo "❌ ERROR: package-lock.json is missing!" | |
| echo " This file is required for reproducible builds." | |
| echo " Please commit it: git add backend/package-lock.json && git commit -m 'Add package-lock.json'" | |
| exit 1 | |
| fi | |
| echo "✅ package-lock.json found" | |
| - name: Build and push image | |
| id: build | |
| run: | | |
| IMAGE_TAG=${{ secrets.GCP_REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/github-scraper/${{ steps.extract.outputs.SERVICE }}:${{ steps.extract.outputs.VERSION }} | |
| LATEST_TAG=${{ secrets.GCP_REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/github-scraper/${{ steps.extract.outputs.SERVICE }}:latest | |
| echo "🔨 Building image..." | |
| docker buildx build \ | |
| --platform linux/amd64 \ | |
| -f ${{ steps.extract.outputs.DOCKERFILE }} \ | |
| --build-arg VERSION=${{ steps.extract.outputs.VERSION }} \ | |
| --cache-from type=registry,ref=${IMAGE_TAG} \ | |
| --cache-from type=registry,ref=${LATEST_TAG} \ | |
| -t ${IMAGE_TAG} \ | |
| -t ${LATEST_TAG} \ | |
| --push \ | |
| --provenance=false \ | |
| --sbom=false \ | |
| ./backend | |
| echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_OUTPUT | |
| echo "LATEST_TAG=${LATEST_TAG}" >> $GITHUB_OUTPUT | |
| echo "✅ Image built and pushed successfully" | |
| - name: Verify image in registry | |
| run: | | |
| IMAGE_TAG="${{ steps.build.outputs.IMAGE_TAG }}" | |
| EXPECTED_VERSION="${{ steps.extract.outputs.VERSION }}" | |
| echo "🔍 Verifying image in registry..." | |
| # Pull and verify APP_VERSION | |
| docker pull ${IMAGE_TAG} | |
| # Verify APP_VERSION is set | |
| APP_VERSION=$(docker inspect ${IMAGE_TAG} --format='{{range .Config.Env}}{{println .}}{{end}}' | grep "^APP_VERSION=" | cut -d= -f2 || echo "") | |
| if [ "$APP_VERSION" != "$EXPECTED_VERSION" ]; then | |
| echo "❌ ERROR: APP_VERSION mismatch!" | |
| echo " Expected: ${EXPECTED_VERSION}" | |
| echo " Found: ${APP_VERSION:-not set}" | |
| exit 1 | |
| fi | |
| echo "✅ APP_VERSION verified: ${APP_VERSION}" | |
| # Verify node_modules exists (quick check) | |
| if ! docker run --rm --platform linux/amd64 --entrypoint /bin/sh ${IMAGE_TAG} -c "test -d /app/node_modules && test -f /app/node_modules/.bin/prisma && test -d /app/node_modules/fastify" 2>/dev/null; then | |
| echo "❌ ERROR: Critical dependencies missing in image!" | |
| echo " Checking what's in /app:" | |
| docker run --rm --platform linux/amd64 --entrypoint /bin/sh ${IMAGE_TAG} -c "ls -la /app" || true | |
| exit 1 | |
| fi | |
| echo "✅ Dependencies verified: node_modules, prisma, fastify all present" | |
| echo "✅ Image verification passed" | |
| echo "✅ Image ready: ${{ steps.extract.outputs.SERVICE }}:${{ steps.extract.outputs.VERSION }}" | |
| echo " Version injected via build arg: ${{ steps.extract.outputs.VERSION }}" | |
| - name: Cleanup old images | |
| if: success() # Only run if build succeeded | |
| run: | | |
| # Cleanup old images (keeps last 3 versions + latest + deployed version) | |
| export PROJECT_ID=${{ secrets.PROJECT_ID }} | |
| export REGION=${{ secrets.GCP_REGION }} | |
| export REPOSITORY=github-scraper | |
| ./scripts/utils/cleanup-old-images.sh ${{ steps.extract.outputs.SERVICE }} --execute | |
| continue-on-error: true # Don't fail workflow if cleanup fails | |
| - name: Trigger deployment in infra repo | |
| run: | | |
| # Automatically trigger deployment in infra repo (Option C) | |
| curl -X POST https://api.github.com/repos/aalexmrt/github-scraper-infra/dispatches \ | |
| -H "Authorization: token ${{ secrets.DEPLOY_TOKEN }}" \ | |
| -H "Accept: application/vnd.github.v3+json" \ | |
| -d '{ | |
| "event_type":"deploy", | |
| "client_payload":{ | |
| "service":"${{ steps.extract.outputs.SERVICE }}", | |
| "version":"${{ steps.extract.outputs.VERSION }}" | |
| } | |
| }' | |
| echo "✅ Deployment triggered for ${{ steps.extract.outputs.SERVICE }}:${{ steps.extract.outputs.VERSION }}" |