Skip to content

Commit 56140f7

Browse files
shantanu patilclaude
authored andcommitted
Add Phase 6: production Docker & deployment pipeline
Fix CI/CD workflows to map GCP Secret Manager secrets, add GCS storage env vars, service account binding, and post-deploy health checks. Add manual deploy targets (push-api, push-web, deploy-api, deploy-web) to Makefile. Fix Terraform to include WIKI_STORAGE_BACKEND and GCS_BUCKET env vars, remove incorrect runtime secrets from web module. Add scripts copy and healthcheck to backend Dockerfile. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b7898f8 commit 56140f7

6 files changed

Lines changed: 87 additions & 12 deletions

File tree

.github/workflows/deploy-api.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,21 @@ jobs:
7171
--memory=2Gi
7272
--min-instances=0
7373
--max-instances=3
74-
--set-env-vars=ENVIRONMENT=production
74+
--service-account=runtime-sa@gitunderstand.iam.gserviceaccount.com
75+
--set-env-vars=ENVIRONMENT=production,WIKI_STORAGE_BACKEND=gcs,GCS_BUCKET_NAME=gitunderstand-wikicache
76+
--set-secrets=GOOGLE_API_KEY=google-api-key:latest,OPENAI_API_KEY=openai-api-key:latest,CLERK_SECRET_KEY=clerk-secret-key:latest,SUPABASE_URL=supabase-url:latest,SUPABASE_SERVICE_ROLE_KEY=supabase-service-role-key:latest
77+
78+
- name: Verify deployment health
79+
run: |
80+
URL=$(gcloud run services describe ${{ env.SERVICE_NAME }} --region ${{ env.REGION }} --format 'value(status.url)')
81+
for i in 1 2 3 4 5; do
82+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL/health" || echo "000")
83+
if [ "$STATUS" = "200" ]; then echo "Health check passed"; exit 0; fi
84+
echo "Attempt $i: status=$STATUS, retrying..."
85+
sleep 10
86+
done
87+
echo "Health check failed after 5 attempts"
88+
exit 1
7589
7690
- name: Show deployment URL
7791
run: |

.github/workflows/deploy-web.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ jobs:
6161
build-args: |
6262
SERVER_BASE_URL=${{ secrets.SERVER_BASE_URL }}
6363
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=${{ secrets.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY }}
64+
NEXT_PUBLIC_SUPABASE_URL=${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
65+
NEXT_PUBLIC_SUPABASE_ANON_KEY=${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
6466
cache-from: type=gha,scope=web
6567
cache-to: type=gha,mode=max,scope=web
6668

@@ -77,7 +79,20 @@ jobs:
7779
--memory=512Mi
7880
--min-instances=0
7981
--max-instances=5
80-
--set-env-vars=ENVIRONMENT=production
82+
--service-account=runtime-sa@gitunderstand.iam.gserviceaccount.com
83+
--set-env-vars=ENVIRONMENT=production,NODE_ENV=production
84+
85+
- name: Verify deployment health
86+
run: |
87+
URL=$(gcloud run services describe ${{ env.SERVICE_NAME }} --region ${{ env.REGION }} --format 'value(status.url)')
88+
for i in 1 2 3 4 5; do
89+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL/" || echo "000")
90+
if [ "$STATUS" = "200" ]; then echo "Health check passed"; exit 0; fi
91+
echo "Attempt $i: status=$STATUS, retrying..."
92+
sleep 10
93+
done
94+
echo "Health check failed after 5 attempts"
95+
exit 1
8196
8297
- name: Show deployment URL
8398
run: |

Makefile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
## ============================================================
66

77
.PHONY: dev dev-api dev-web test test-api lint build-api build-web \
8+
push-api push-web deploy-api deploy-web deploy \
89
infra-plan infra-apply ingest ingest-batch ingest-batch-skip \
910
ingest-dry-run spec help
1011

@@ -38,6 +39,45 @@ build-api: ## Build the backend Docker image
3839
build-web: ## Build the frontend Docker image
3940
docker build -f docker/Dockerfile.frontend -t gitunderstand-web .
4041

42+
## ------ Docker Push (to Artifact Registry) ---------------------
43+
44+
push-api: build-api ## Build & push API image to GAR
45+
docker tag gitunderstand-api us-central1-docker.pkg.dev/gitunderstand/bettercodewiki/api:latest
46+
docker push us-central1-docker.pkg.dev/gitunderstand/bettercodewiki/api:latest
47+
48+
push-web: build-web ## Build & push Web image to GAR
49+
docker tag gitunderstand-web us-central1-docker.pkg.dev/gitunderstand/bettercodewiki/web:latest
50+
docker push us-central1-docker.pkg.dev/gitunderstand/bettercodewiki/web:latest
51+
52+
## ------ Deploy (manual gcloud) ---------------------------------
53+
54+
deploy-api: ## Deploy API to Cloud Run (uses latest GAR image)
55+
gcloud run deploy gitunderstand-api \
56+
--image us-central1-docker.pkg.dev/gitunderstand/bettercodewiki/api:latest \
57+
--region us-central1 \
58+
--platform managed \
59+
--allow-unauthenticated \
60+
--service-account runtime-sa@gitunderstand.iam.gserviceaccount.com \
61+
--port 8001 \
62+
--cpu 1 --memory 2Gi \
63+
--min-instances 0 --max-instances 3 \
64+
--set-env-vars "ENVIRONMENT=production,WIKI_STORAGE_BACKEND=gcs,GCS_BUCKET_NAME=gitunderstand-wikicache" \
65+
--set-secrets "GOOGLE_API_KEY=google-api-key:latest,OPENAI_API_KEY=openai-api-key:latest,CLERK_SECRET_KEY=clerk-secret-key:latest,SUPABASE_URL=supabase-url:latest,SUPABASE_SERVICE_ROLE_KEY=supabase-service-role-key:latest"
66+
67+
deploy-web: ## Deploy Web to Cloud Run (uses latest GAR image)
68+
gcloud run deploy gitunderstand-web \
69+
--image us-central1-docker.pkg.dev/gitunderstand/bettercodewiki/web:latest \
70+
--region us-central1 \
71+
--platform managed \
72+
--allow-unauthenticated \
73+
--service-account runtime-sa@gitunderstand.iam.gserviceaccount.com \
74+
--port 3000 \
75+
--cpu 1 --memory 512Mi \
76+
--min-instances 0 --max-instances 5 \
77+
--set-env-vars "ENVIRONMENT=production,NODE_ENV=production"
78+
79+
deploy: deploy-api deploy-web ## Deploy both services to Cloud Run
80+
4181
## ------ Infrastructure (Terraform) ---------------------------
4282

4383
infra-plan: ## Preview Terraform changes for production

docker/Dockerfile.backend

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ ENV PATH="/opt/venv/bin:$PATH"
4545

4646
# Copy application code
4747
COPY api/ ./api/
48+
COPY scripts/ ./scripts/
4849

4950
# Create non-root user
5051
RUN groupadd --system --gid 1001 apiuser && \
@@ -59,4 +60,7 @@ EXPOSE 8001
5960
ENV PORT=8001
6061
ENV NODE_ENV=production
6162

63+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
64+
CMD curl -f http://localhost:8001/health || exit 1
65+
6266
CMD ["python", "-m", "api.main"]

docker/docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ services:
3737
- PORT=8001
3838
- NODE_ENV=development
3939
- LOG_LEVEL=${LOG_LEVEL:-INFO}
40+
- WIKI_STORAGE_BACKEND=${WIKI_STORAGE_BACKEND:-local}
41+
- GCS_BUCKET_NAME=${GCS_BUCKET_NAME:-gitunderstand-wikicache}
4042
env_file:
4143
- ../.env
4244
volumes:

infra/environments/prod/main.tf

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,16 @@ module "api" {
8484
service_account = var.runtime_sa_email
8585

8686
env_vars = {
87-
ENVIRONMENT = "production"
87+
ENVIRONMENT = "production"
88+
WIKI_STORAGE_BACKEND = "gcs"
89+
GCS_BUCKET_NAME = "gitunderstand-wikicache"
8890
}
8991

9092
secret_env_vars = {
91-
GOOGLE_API_KEY = "google-api-key"
92-
OPENAI_API_KEY = "openai-api-key"
93-
CLERK_SECRET_KEY = "clerk-secret-key"
94-
SUPABASE_URL = "supabase-url"
93+
GOOGLE_API_KEY = "google-api-key"
94+
OPENAI_API_KEY = "openai-api-key"
95+
CLERK_SECRET_KEY = "clerk-secret-key"
96+
SUPABASE_URL = "supabase-url"
9597
SUPABASE_SERVICE_ROLE_KEY = "supabase-service-role-key"
9698
}
9799

@@ -122,11 +124,9 @@ module "web" {
122124
ENVIRONMENT = "production"
123125
}
124126

125-
secret_env_vars = {
126-
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY = "clerk-publishable-key"
127-
SUPABASE_URL = "supabase-url"
128-
SUPABASE_ANON_KEY = "supabase-anon-key"
129-
}
127+
# NEXT_PUBLIC_* vars and SERVER_BASE_URL are baked in at Docker build time.
128+
# No runtime secrets needed for the frontend service.
129+
secret_env_vars = {}
130130

131131
depends_on = [module.secrets]
132132
}

0 commit comments

Comments
 (0)