Skip to content

Enhance Dockerfile and Build Workflow #10

Enhance Dockerfile and Build Workflow

Enhance Dockerfile and Build Workflow #10

Workflow file for this run

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 }}"