@@ -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+ # Get the image_uri from the step that confirms build success
35+ backend_image_uri : ${{ steps.wait_backend_build.outputs.image_uri }}
36+ frontend_image_uri : ${{ steps.wait_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 : |
66- 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 \
69- --tag "$IMAGE_URI" \
58+ TARGET_IMAGE_URI="${{ env.ARTIFACT_REGISTRY_REPO }}/${{ env.BACKEND_IMAGE_NAME }}:${{ env.IMAGE_TAG }}"
59+ echo "Submitting Backend Build for Image: $TARGET_IMAGE_URI"
60+
61+ BUILD_ID_RAW=$(gcloud builds submit ./backend \
62+ --tag "$TARGET_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+ BUILD_ID=$(echo "$BUILD_ID_RAW" | grep -o -E '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}' | head -n 1)
68+
69+ if [ -z "$BUILD_ID" ]; then
70+ echo "::warning::Could not extract BUILD_ID using UUID pattern from 'gcloud builds submit' output (Backend). Raw output was: [$BUILD_ID_RAW]. Attempting fallback."
71+ 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)
72+ if [ -z "$BUILD_ID_FALLBACK" ]; then
73+ echo "::error::Could not determine BUILD_ID even via fallback list command (Backend)."
74+ exit 1
75+ else
76+ BUILD_ID=$(echo "$BUILD_ID_FALLBACK" | xargs)
77+ echo "Using BUILD_ID from fallback (Backend): $BUILD_ID"
78+ fi
79+ fi
80+
81+ if [ -z "$BUILD_ID" ]; then
82+ echo "::error::Failed to obtain a valid BUILD_ID for Backend."
83+ exit 1
84+ fi
7385
74- - name : Build and Push Frontend Docker Image (using Cloud Build and frontend/cloudbuild.yaml)
75- id : build_frontend
86+ echo "Extracted Cloud Build ID (Backend): $BUILD_ID"
87+ echo "build_id=$BUILD_ID" >> $GITHUB_OUTPUT
88+ # Output the *intended* image URI from this step
89+ echo "target_image_uri=$TARGET_IMAGE_URI" >> $GITHUB_OUTPUT
90+
91+ - name : Wait for Backend Build and Verify
92+ id : wait_backend_build
7693 run : |
77- 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 \
94+ BUILD_ID="${{ steps.submit_backend_build.outputs.build_id }}"
95+ TARGET_IMAGE_URI="${{ steps.submit_backend_build.outputs.target_image_uri }}" # Get the intended URI
96+ echo "Waiting for Backend Build ID: $BUILD_ID to complete..."
97+ gcloud builds log --stream "$BUILD_ID" --project=${{ env.GCP_PROJECT_ID }} || echo "Log streaming potentially failed for Backend, checking status..."
98+
99+ FINAL_STATUS=$(gcloud builds describe "$BUILD_ID" --project=${{ env.GCP_PROJECT_ID }} --format='value(status)')
100+ echo "Backend Build final status: $FINAL_STATUS"
101+ if [[ "$FINAL_STATUS" == "SUCCESS" ]]; then
102+ echo "Backend build $BUILD_ID succeeded."
103+ # Only set the output if the build was successful
104+ echo "image_uri=$TARGET_IMAGE_URI" >> $GITHUB_OUTPUT
105+ elif [ "$FINAL_STATUS" == "WORKING" ]; then
106+ echo "::warning::Backend Build $BUILD_ID still in WORKING state after log stream finished. Proceeding cautiously but setting image_uri."
107+ echo "image_uri=$TARGET_IMAGE_URI" >> $GITHUB_OUTPUT # Still set if working, deploy might catch it later
108+ else
109+ echo "::error::Backend build $BUILD_ID failed or has unexpected status $FINAL_STATUS"
110+ exit 1
111+ fi
112+
113+ - name : Submit Frontend Build and Get ID
114+ id : submit_frontend_build
115+ run : |
116+ TARGET_IMAGE_URI="${{ env.ARTIFACT_REGISTRY_REPO }}/${{ env.FRONTEND_IMAGE_NAME }}:${{ env.IMAGE_TAG }}"
117+ echo "Submitting Frontend Build for Image: $TARGET_IMAGE_URI"
118+
119+ BUILD_ID_RAW=$(gcloud builds submit ./frontend \
83120 --config=frontend/cloudbuild.yaml \
84- --substitutions=_REACT_APP_GA_MEASUREMENT_ID="${{ secrets.PROD_REACT_APP_GA_MEASUREMENT_ID }}",_IMAGE_TAG_NAME="$IMAGE_URI " \
121+ --substitutions=_REACT_APP_GA_MEASUREMENT_ID="${{ secrets.PROD_REACT_APP_GA_MEASUREMENT_ID }}",_IMAGE_TAG_NAME="$TARGET_IMAGE_URI " \
85122 --project=${{ env.GCP_PROJECT_ID }} \
86- --quiet
87- echo "image_uri=$IMAGE_URI" >> $GITHUB_OUTPUT # Set output for subsequent jobs
123+ --quiet \
124+ --format='value(id)')
125+
126+ BUILD_ID=$(echo "$BUILD_ID_RAW" | grep -o -E '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}' | head -n 1)
127+
128+ if [ -z "$BUILD_ID" ]; then
129+ echo "::warning::Could not extract BUILD_ID using UUID pattern from 'gcloud builds submit' output (Frontend). Raw output was: [$BUILD_ID_RAW]. Attempting fallback."
130+ BUILD_ID_FALLBACK=$(gcloud builds list --project=${{ env.GCP_PROJECT_ID }} --filter="sourceProvenance.resolvedRepoSource.commitSha='${{ github.sha }}' AND substitutions._IMAGE_TAG_NAME='$TARGET_IMAGE_URI' AND buildTriggerId=''" --sort-by=~createTime --format='value(id)' --limit=1)
131+ if [ -z "$BUILD_ID_FALLBACK" ]; then
132+ echo "::error::Could not determine BUILD_ID even via fallback list command (Frontend)."
133+ exit 1
134+ else
135+ BUILD_ID=$(echo "$BUILD_ID_FALLBACK" | xargs)
136+ echo "Using BUILD_ID from fallback (Frontend): $BUILD_ID"
137+ fi
138+ fi
139+
140+ if [ -z "$BUILD_ID" ]; then
141+ echo "::error::Failed to obtain a valid BUILD_ID for Frontend."
142+ exit 1
143+ fi
144+
145+ echo "Extracted Cloud Build ID (Frontend): $BUILD_ID"
146+ echo "build_id=$BUILD_ID" >> $GITHUB_OUTPUT
147+ echo "target_image_uri=$TARGET_IMAGE_URI" >> $GITHUB_OUTPUT
148+
149+ - name : Wait for Frontend Build and Verify
150+ id : wait_frontend_build
151+ run : |
152+ BUILD_ID="${{ steps.submit_frontend_build.outputs.build_id }}"
153+ TARGET_IMAGE_URI="${{ steps.submit_frontend_build.outputs.target_image_uri }}"
154+ echo "Waiting for Frontend Build ID: $BUILD_ID to complete..."
155+ gcloud builds log --stream "$BUILD_ID" --project=${{ env.GCP_PROJECT_ID }} || echo "Log streaming potentially failed for Frontend, checking status..."
156+
157+ FINAL_STATUS=$(gcloud builds describe "$BUILD_ID" --project=${{ env.GCP_PROJECT_ID }} --format='value(status)')
158+ echo "Frontend Build final status: $FINAL_STATUS"
159+ if [[ "$FINAL_STATUS" == "SUCCESS" ]]; then
160+ echo "Frontend build $BUILD_ID succeeded."
161+ echo "image_uri=$TARGET_IMAGE_URI" >> $GITHUB_OUTPUT
162+ elif [ "$FINAL_STATUS" == "WORKING" ]; then
163+ echo "::warning::Build $BUILD_ID still in WORKING state after log stream finished. Proceeding cautiously but setting image_uri."
164+ echo "image_uri=$TARGET_IMAGE_URI" >> $GITHUB_OUTPUT
165+ else
166+ echo "::error::Frontend build $BUILD_ID failed or has unexpected status $FINAL_STATUS"
167+ exit 1
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