@@ -4,49 +4,42 @@ name: PromptMan CI/CD to Google Cloud Run
44on :
55 push :
66 branches :
7- - main # Trigger deployment on pushes to the main branch
7+ - main # Deploy when merging/pushing to main
88
99env :
10- # GCP Settings from GitHub Secrets
10+ # GCP Settings from Secrets
1111 GCP_PROJECT_ID : ${{ secrets.GCP_PROJECT_ID }}
1212 GCP_REGION : ${{ secrets.GCP_REGION }}
13- ARTIFACT_REGISTRY_REPO : ${{ secrets.ARTIFACT_REGISTRY_REPO }} # e.g., asia-south1-docker.pkg.dev/your-project-id/promptman-repo
14-
15- # Image and Service Naming Conventions
13+ ARTIFACT_REGISTRY_REPO : ${{ secrets.ARTIFACT_REGISTRY_REPO }}
14+ # Image and Service Names
1615 BACKEND_IMAGE_NAME : promptman-backend
1716 FRONTEND_IMAGE_NAME : promptman-frontend
1817 BACKEND_SERVICE_NAME : promptman-backend
1918 FRONTEND_SERVICE_NAME : promptman-frontend
20-
21- # VPC Connector Name (ensure this exists in your GCP project and region)
22- VPC_CONNECTOR_NAME : promptman-connector
23-
24- # Use the commit SHA for unique Docker image tags for better traceability
19+ VPC_CONNECTOR_NAME : promptman-connector
20+ # Use commit SHA for unique image tags
2521 IMAGE_TAG : ${{ github.sha }}
2622
2723jobs :
28- # =======================================================
29- # 1. Build & Push Docker Images using Google Cloud Build
30- # =======================================================
3124 build_and_push_images :
3225 name : Build & Push Images
3326 runs-on : ubuntu-latest
34- # This job runs only on pushes to the main branch
3527 if : github.event_name == 'push' && github.ref == 'refs/heads/main'
3628
3729 permissions :
38- contents : ' read' # Required to checkout the repository code
39- id-token : ' write' # Required for Workload Identity Federation
30+ contents : ' read'
31+ id-token : ' write'
4032
41- outputs : # Define outputs to pass image URIs to deployment jobs
42- backend_image_uri : ${{ steps.build_backend.outputs.image_uri }}
43- frontend_image_uri : ${{ steps.build_frontend.outputs.image_uri }}
33+ outputs :
34+ # Output the target image URIs based on the submit steps
35+ backend_image_uri : ${{ steps.submit_backend_build.outputs.image_uri }}
36+ frontend_image_uri : ${{ steps.submit_frontend_build.outputs.image_uri }}
4437
4538 steps :
4639 - name : Checkout code
4740 uses : actions/checkout@v4
4841
49- - name : Authenticate to Google Cloud (via Workload Identity Federation)
42+ - name : Authenticate to Google Cloud
5043 id : auth
5144 uses : google-github-actions/auth@v2
5245 with :
@@ -56,56 +49,141 @@ jobs:
5649 - name : Set up Google Cloud SDK
5750 uses : google-github-actions/setup-gcloud@v2
5851
59- # Configure Docker helper to authenticate with Artifact Registry (uses gcloud credentials)
6052 - name : Configure Docker for Artifact Registry
6153 run : gcloud auth configure-docker ${{ env.GCP_REGION }}-docker.pkg.dev --quiet
6254
63- - name : Build and Push Backend Docker Image (using Cloud Build)
64- id : build_backend
55+ - name : Submit Backend Build and Get ID
56+ id : submit_backend_build
6557 run : |
6658 IMAGE_URI="${{ env.ARTIFACT_REGISTRY_REPO }}/${{ env.BACKEND_IMAGE_NAME }}:${{ env.IMAGE_TAG }}"
67- echo "Building Backend Image to: $IMAGE_URI"
68- gcloud builds submit ./backend \
59+ echo "Submitting Backend Build for Image: $IMAGE_URI"
60+
61+ BUILD_ID_RAW=$(gcloud builds submit ./backend \
6962 --tag "$IMAGE_URI" \
7063 --project=${{ env.GCP_PROJECT_ID }} \
71- --quiet # Suppress verbose output from gcloud
72- echo "image_uri=$IMAGE_URI" >> $GITHUB_OUTPUT # Set output for subsequent jobs
64+ --quiet \
65+ --format='value(id)')
66+
67+ if [ -n "$BUILD_ID_RAW" ]; then
68+ BUILD_ID=$(echo "$BUILD_ID_RAW" | tr -d '\r\n' | xargs)
69+ else
70+ BUILD_ID=""
71+ fi
72+
73+ if [ -z "$BUILD_ID" ]; then
74+ echo "::warning::Could not get BUILD_ID directly from 'gcloud builds submit' output (Backend). Output was: [$BUILD_ID_RAW]. Attempting fallback."
75+ BUILD_ID_FALLBACK=$(gcloud builds list --project=${{ env.GCP_PROJECT_ID }} --filter="sourceProvenance.resolvedRepoSource.commitSha='${{ github.sha }}' AND buildTriggerId=''" --sort-by=~createTime --format='value(id)' --limit=1)
76+ if [ -z "$BUILD_ID_FALLBACK" ]; then
77+ echo "::error::Could not determine BUILD_ID even via fallback list command (Backend)."
78+ exit 1
79+ else
80+ BUILD_ID=$(echo "$BUILD_ID_FALLBACK" | xargs)
81+ echo "Using BUILD_ID from fallback (Backend): $BUILD_ID"
82+ fi
83+ fi
84+
85+ if [ -z "$BUILD_ID" ]; then
86+ echo "::error::Failed to obtain a valid BUILD_ID for Backend."
87+ exit 1
88+ fi
7389
74- - name : Build and Push Frontend Docker Image (using Cloud Build and frontend/cloudbuild.yaml)
75- id : build_frontend
90+ echo "Cleaned Cloud Build ID (Backend): $BUILD_ID"
91+ echo "build_id=$BUILD_ID" >> $GITHUB_OUTPUT
92+ echo "image_uri=$IMAGE_URI" >> $GITHUB_OUTPUT
93+
94+ - name : Wait for Backend Build and Verify
95+ id : wait_backend_build
96+ run : |
97+ BUILD_ID="${{ steps.submit_backend_build.outputs.build_id }}"
98+ echo "Waiting for Backend Build ID: $BUILD_ID to complete..."
99+ gcloud builds log --stream "$BUILD_ID" --project=${{ env.GCP_PROJECT_ID }} || echo "Log streaming potentially failed for Backend, checking status..."
100+
101+ FINAL_STATUS=$(gcloud builds describe "$BUILD_ID" --project=${{ env.GCP_PROJECT_ID }} --format='value(status)')
102+ echo "Backend Build final status: $FINAL_STATUS"
103+ if [[ "$FINAL_STATUS" != "SUCCESS" && "$FINAL_STATUS" != "WORKING" ]]; then
104+ echo "::error::Backend build $BUILD_ID failed or has unexpected status $FINAL_STATUS"
105+ exit 1
106+ elif [ "$FINAL_STATUS" == "WORKING" ]; then
107+ echo "::warning::Backend Build $BUILD_ID still in WORKING state after log stream finished. Proceeding cautiously."
108+ else
109+ echo "Backend build $BUILD_ID succeeded."
110+ fi
111+
112+ - name : Submit Frontend Build and Get ID
113+ id : submit_frontend_build
76114 run : |
77115 IMAGE_URI="${{ env.ARTIFACT_REGISTRY_REPO }}/${{ env.FRONTEND_IMAGE_NAME }}:${{ env.IMAGE_TAG }}"
78- echo "Building Frontend Image to: $IMAGE_URI"
79- # Pass substitutions required by frontend/cloudbuild.yaml:
80- # _REACT_APP_GA_MEASUREMENT_ID comes from GitHub Secrets
81- # _IMAGE_TAG_NAME is constructed here
82- gcloud builds submit ./frontend \
116+ echo "Submitting Frontend Build for Image: $IMAGE_URI"
117+
118+ BUILD_ID_RAW=$(gcloud builds submit ./frontend \
83119 --config=frontend/cloudbuild.yaml \
84120 --substitutions=_REACT_APP_GA_MEASUREMENT_ID="${{ secrets.PROD_REACT_APP_GA_MEASUREMENT_ID }}",_IMAGE_TAG_NAME="$IMAGE_URI" \
85121 --project=${{ env.GCP_PROJECT_ID }} \
86- --quiet
87- echo "image_uri=$IMAGE_URI" >> $GITHUB_OUTPUT # Set output for subsequent jobs
122+ --quiet \
123+ --format='value(id)')
124+
125+ if [ -n "$BUILD_ID_RAW" ]; then
126+ BUILD_ID=$(echo "$BUILD_ID_RAW" | tr -d '\r\n' | xargs)
127+ else
128+ BUILD_ID=""
129+ fi
130+
131+ if [ -z "$BUILD_ID" ]; then
132+ echo "::warning::Could not get BUILD_ID directly from 'gcloud builds submit' output (Frontend). Output was: [$BUILD_ID_RAW]. Attempting fallback."
133+ BUILD_ID_FALLBACK=$(gcloud builds list --project=${{ env.GCP_PROJECT_ID }} --filter="sourceProvenance.resolvedRepoSource.commitSha='${{ github.sha }}' AND substitutions._IMAGE_TAG_NAME='$IMAGE_URI' AND buildTriggerId=''" --sort-by=~createTime --format='value(id)' --limit=1)
134+ if [ -z "$BUILD_ID_FALLBACK" ]; then
135+ echo "::error::Could not determine BUILD_ID even via fallback list command (Frontend)."
136+ exit 1
137+ else
138+ BUILD_ID=$(echo "$BUILD_ID_FALLBACK" | xargs)
139+ echo "Using BUILD_ID from fallback (Frontend): $BUILD_ID"
140+ fi
141+ fi
142+
143+ if [ -z "$BUILD_ID" ]; then
144+ echo "::error::Failed to obtain a valid BUILD_ID for Frontend."
145+ exit 1
146+ fi
147+
148+ echo "Cleaned Cloud Build ID (Frontend): $BUILD_ID"
149+ echo "build_id=$BUILD_ID" >> $GITHUB_OUTPUT
150+ echo "image_uri=$IMAGE_URI" >> $GITHUB_OUTPUT
151+
152+ - name : Wait for Frontend Build and Verify
153+ id : wait_frontend_build
154+ run : |
155+ BUILD_ID="${{ steps.submit_frontend_build.outputs.build_id }}"
156+ echo "Waiting for Frontend Build ID: $BUILD_ID to complete..."
157+ gcloud builds log --stream "$BUILD_ID" --project=${{ env.GCP_PROJECT_ID }} || echo "Log streaming potentially failed for Frontend, checking status..."
158+
159+ FINAL_STATUS=$(gcloud builds describe "$BUILD_ID" --project=${{ env.GCP_PROJECT_ID }} --format='value(status)')
160+ echo "Frontend Build final status: $FINAL_STATUS"
161+ if [[ "$FINAL_STATUS" != "SUCCESS" && "$FINAL_STATUS" != "WORKING" ]]; then
162+ echo "::error::Frontend build $BUILD_ID failed or has unexpected status $FINAL_STATUS"
163+ exit 1
164+ elif [ "$FINAL_STATUS" == "WORKING" ]; then
165+ echo "::warning::Build $BUILD_ID still in WORKING state after log stream finished. Proceeding cautiously."
166+ else
167+ echo "Frontend build $BUILD_ID succeeded."
168+ fi
88169
89- # ==========================================
90- # 2. Deploy Backend Service to Cloud Run
91- # ==========================================
92170 deploy_backend :
93171 name : Deploy Backend Service
94172 runs-on : ubuntu-latest
95- needs : build_and_push_images # Depends on images being successfully built and pushed
173+ needs : build_and_push_images
96174 if : github.event_name == 'push' && github.ref == 'refs/heads/main'
97175
98176 permissions :
99177 contents : ' read'
100178 id-token : ' write'
101179
102- outputs : # Output the deployed backend URL and host for the frontend deployment
180+ outputs :
103181 backend_url : ${{ steps.deploy_backend_service.outputs.url }}
104182 backend_host : ${{ steps.deploy_backend_service.outputs.host }}
105183
106184 steps :
107185 - name : Checkout code
108- uses : actions/checkout@v4 # Though not strictly needed for deploy-from-image, good for consistency
186+ uses : actions/checkout@v4
109187
110188 - name : Authenticate to Google Cloud
111189 id : auth
@@ -118,11 +196,16 @@ jobs:
118196 uses : google-github-actions/setup-gcloud@v2
119197
120198 - name : Deploy Backend to Cloud Run
121- id : deploy_backend_service # Step ID for referencing outputs
199+ id : deploy_backend_service
122200 run : |
123- echo "Deploying Backend Image: ${{ needs.build_and_push_images.outputs.backend_image_uri }}"
201+ IMAGE_URI="${{ needs.build_and_push_images.outputs.backend_image_uri }}" # Correctly references output
202+ echo "Deploying Backend Image: $IMAGE_URI"
203+ if [ -z "$IMAGE_URI" ]; then
204+ echo "::error::Backend Image URI is empty. Build might have failed to output."
205+ exit 1
206+ fi
124207 gcloud run deploy ${{ env.BACKEND_SERVICE_NAME }} \
125- --image "${{ needs.build_and_push_images.outputs.backend_image_uri }} " \
208+ --image "$IMAGE_URI " \
126209 --platform managed \
127210 --region ${{ env.GCP_REGION }} \
128211 --port 8000 \
@@ -136,22 +219,17 @@ jobs:
136219 --project=${{ env.GCP_PROJECT_ID }} \
137220 --quiet
138221
139- # Get the URL of the deployed service to pass to the frontend deployment
140222 SERVICE_URL=$(gcloud run services describe ${{ env.BACKEND_SERVICE_NAME }} --platform managed --region ${{ env.GCP_REGION }} --format='value(status.url)' --project=${{ env.GCP_PROJECT_ID }})
141- SERVICE_HOST=$(echo $SERVICE_URL | sed -e 's|^[^/]*//||' -e 's|/.*$||') # Extracts hostname
223+ SERVICE_HOST=$(echo $SERVICE_URL | sed -e 's|^[^/]*//||' -e 's|/.*$||')
142224 echo "Backend Service URL: $SERVICE_URL"
143225 echo "Backend Service Host: $SERVICE_HOST"
144- # Set outputs for the next job
145226 echo "url=$SERVICE_URL" >> $GITHUB_OUTPUT
146227 echo "host=$SERVICE_HOST" >> $GITHUB_OUTPUT
147228
148- # ==========================================
149- # 3. Deploy Frontend Service to Cloud Run
150- # ==========================================
151229 deploy_frontend :
152230 name : Deploy Frontend Service
153231 runs-on : ubuntu-latest
154- needs : deploy_backend # Depends on backend deployment to get its URL
232+ needs : [build_and_push_images, deploy_backend] # Depends on both image build and backend deployment
155233 if : github.event_name == 'push' && github.ref == 'refs/heads/main'
156234
157235 permissions :
@@ -176,17 +254,22 @@ jobs:
176254 run : |
177255 BACKEND_URL="${{ needs.deploy_backend.outputs.backend_url }}"
178256 BACKEND_HOST="${{ needs.deploy_backend.outputs.backend_host }}"
257+ IMAGE_URI="${{ needs.build_and_push_images.outputs.frontend_image_uri }}" # Correctly references output
179258
180259 if [ -z "$BACKEND_URL" ] || [ -z "$BACKEND_HOST" ]; then
181260 echo "::error::Backend URL or Host not received from previous job. Check deploy_backend job outputs."
182261 exit 1
183262 fi
263+ if [ -z "$IMAGE_URI" ]; then
264+ echo "::error::Frontend Image URI is empty. Build might have failed to output."
265+ exit 1
266+ fi
184267
185- echo "Deploying Frontend Image: ${{ needs.build_and_push_images.outputs.frontend_image_uri }} "
268+ echo "Deploying Frontend Image: $IMAGE_URI "
186269 echo "Configuring Nginx with Backend URL: $BACKEND_URL and Host: $BACKEND_HOST"
187270
188271 gcloud run deploy ${{ env.FRONTEND_SERVICE_NAME }} \
189- --image "${{ needs.build_and_push_images.outputs.frontend_image_uri }} " \
272+ --image "$IMAGE_URI " \
190273 --platform managed \
191274 --region ${{ env.GCP_REGION }} \
192275 --port 80 \
0 commit comments