Skip to content

Commit e573f15

Browse files
committed
Fix: CI/CD Pipeline
1 parent cf60fa6 commit e573f15

1 file changed

Lines changed: 137 additions & 54 deletions

File tree

.github/workflows/main.yml

Lines changed: 137 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,42 @@ name: PromptMan CI/CD to Google Cloud Run
44
on:
55
push:
66
branches:
7-
- main # Trigger deployment on pushes to the main branch
7+
- main # Deploy when merging/pushing to main
88

99
env:
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

2723
jobs:
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

Comments
 (0)