Skip to content

Commit faa83be

Browse files
committed
add user env for apis
1 parent 3546ed2 commit faa83be

1 file changed

Lines changed: 331 additions & 0 deletions

File tree

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
# Deploys a personal Feeds API (Cloud Run) and Operations API (Cloud Function) to the DEV project.
2+
#
3+
# Completely isolated — no Terraform, no shared infrastructure changes.
4+
# Only two new named resources are created/updated (both scoped to your suffix):
5+
# - Cloud Run: developer-feed-api-<name_suffix>
6+
# - Cloud Function: operations-api-personal-<name_suffix>
7+
#
8+
# Both use the same dev secrets/databases as the shared dev environment (read-safe).
9+
#
10+
# Prerequisites: none — reuses all existing dev environment vars and secrets:
11+
# Secrets (already set for api-dev.yml): DEV_GCP_MOBILITY_FEEDS_SA_KEY, OP_SERVICE_ACCOUNT_TOKEN
12+
# Variables (already set for api-dev.yml): DEV_MOBILITY_FEEDS_PROJECT_ID,
13+
# DEV_MOBILITY_FEEDS_DEPLOYER_SERVICE_ACCOUNT, MOBILITY_FEEDS_REGION
14+
#
15+
# Usage: Actions → "Deploy Personal Feeds API" → Run workflow → fill in name_suffix
16+
name: Deploy Personal Feeds API
17+
18+
on:
19+
workflow_dispatch:
20+
inputs:
21+
name_suffix:
22+
description: >
23+
Personal identifier suffix. Creates:
24+
Cloud Run: developer-feed-api-<suffix>
25+
Cloud Function: operations-api-personal-<suffix>
26+
Use your name or branch (e.g. "david", "feat-1723").
27+
required: true
28+
default: 'dev'
29+
deploy_feeds_api:
30+
description: 'Deploy Feeds API (Cloud Run)'
31+
required: false
32+
default: 'true'
33+
deploy_operations_api:
34+
description: 'Deploy Operations API (Cloud Function)'
35+
required: false
36+
default: 'true'
37+
skip_tests:
38+
description: 'Skip build/lint tests (faster deploy)'
39+
required: false
40+
default: 'true'
41+
42+
env:
43+
python_version: '3.11'
44+
java_version: '11'
45+
REGION: ${{ vars.MOBILITY_FEEDS_REGION }}
46+
PROJECT_ID: ${{ vars.DEV_MOBILITY_FEEDS_PROJECT_ID }}
47+
ARTIFACT_REPO: feeds-dev
48+
# Dev VPC connector lives in the QA project (matches Terraform logic in infra/main.tf)
49+
VPC_CONNECTOR: vpc-connector-qa
50+
VPC_CONNECTOR_PROJECT: mobility-feeds-qa
51+
52+
jobs:
53+
# ── Optional: run tests before deploying ──────────────────────────────────
54+
build-test:
55+
if: ${{ github.event.inputs.skip_tests != 'true' }}
56+
uses: ./.github/workflows/build-test.yml
57+
with:
58+
SKIP_TESTS: false
59+
60+
# ── Shared setup: generate code stubs needed by both deploy jobs ──────────
61+
generate-and-build:
62+
runs-on: ubuntu-latest
63+
permissions: write-all
64+
needs: [] # runs in parallel with build-test (or immediately if tests skipped)
65+
steps:
66+
- name: Checkout code
67+
uses: actions/checkout@v4
68+
69+
- name: Extract commit hash and version from git
70+
run: ./scripts/extract-hash-and-version.sh
71+
72+
- name: Set up JDK ${{ env.java_version }}
73+
uses: actions/setup-java@v4
74+
with:
75+
java-version: ${{ env.java_version }}
76+
distribution: 'temurin'
77+
78+
- uses: actions/setup-python@v5
79+
with:
80+
python-version: ${{ env.python_version }}
81+
82+
- name: Create local .env
83+
run: |
84+
echo "POSTGRES_USER=postgres_user" > config/.env.local
85+
echo "PGUSER=postgres_user" >> config/.env.local
86+
echo "POSTGRES_PASSWORD=postgres_password" >> config/.env.local
87+
echo "POSTGRES_DB=postgres_db" >> config/.env.local
88+
echo "POSTGRES_PORT=5432" >> config/.env.local
89+
echo "POSTGRES_USER_DB=MobilityDatabaseUsers" >> config/.env.local
90+
echo "POSTGRES_USER_TEST_DB=MobilityDatabaseUsersTest" >> config/.env.local
91+
echo "POSTGRES_HOST=localhost" >> config/.env.local
92+
echo "ENVIRONMENT=dev" >> config/.env.local
93+
94+
- name: Generate API stubs and DB models
95+
run: |
96+
scripts/setup-openapi-generator.sh
97+
scripts/api-gen.sh
98+
scripts/api-operations-gen.sh
99+
scripts/db-gen.sh
100+
101+
- name: Build operations API function zip
102+
if: ${{ github.event.inputs.deploy_operations_api == 'true' }}
103+
run: scripts/function-python-build.sh --function_name operations_api
104+
105+
- name: Upload generated feeds_gen
106+
uses: actions/upload-artifact@v4
107+
with:
108+
name: feeds_gen
109+
path: api/src/feeds_gen/
110+
111+
- name: Upload generated database_gen
112+
uses: actions/upload-artifact@v4
113+
with:
114+
name: database_gen
115+
path: api/src/shared/database_gen/
116+
117+
- name: Upload generated users_database_gen
118+
uses: actions/upload-artifact@v4
119+
with:
120+
name: users_database_gen
121+
path: api/src/shared/users_database_gen/
122+
123+
- name: Upload generated user_service_gen
124+
uses: actions/upload-artifact@v4
125+
with:
126+
name: user_service_gen
127+
path: api/src/user_service_gen/
128+
129+
- name: Upload operations API build
130+
if: ${{ github.event.inputs.deploy_operations_api == 'true' }}
131+
uses: actions/upload-artifact@v4
132+
with:
133+
name: operations_api_build
134+
path: functions-python/operations_api/.dist/
135+
136+
# ── Feeds API: build Docker image and deploy to personal Cloud Run ─────────
137+
deploy-feeds-api:
138+
if: ${{ github.event.inputs.deploy_feeds_api == 'true' }}
139+
runs-on: ubuntu-latest
140+
permissions: write-all
141+
needs: [generate-and-build]
142+
outputs:
143+
service_name: ${{ steps.names.outputs.service_name }}
144+
service_url: ${{ steps.get_url.outputs.url }}
145+
steps:
146+
- name: Checkout code
147+
uses: actions/checkout@v4
148+
149+
- name: Set service name and image tag
150+
id: names
151+
run: |
152+
echo "service_name=developer-feed-api-${{ github.event.inputs.name_suffix }}" >> "$GITHUB_OUTPUT"
153+
echo "image_tag=personal-$(date +%s)-${GITHUB_SHA::8}" >> "$GITHUB_OUTPUT"
154+
155+
- name: Download generated artifacts
156+
uses: actions/download-artifact@v4
157+
with:
158+
name: feeds_gen
159+
path: api/src/feeds_gen/
160+
161+
- uses: actions/download-artifact@v4
162+
with:
163+
name: database_gen
164+
path: api/src/shared/database_gen/
165+
166+
- uses: actions/download-artifact@v4
167+
with:
168+
name: users_database_gen
169+
path: api/src/shared/users_database_gen/
170+
171+
- uses: actions/download-artifact@v4
172+
with:
173+
name: user_service_gen
174+
path: api/src/user_service_gen/
175+
176+
- name: Create local .env (required by Dockerfile)
177+
run: |
178+
echo "ENVIRONMENT=dev" > config/.env.local
179+
180+
- name: Authenticate to Google Cloud
181+
uses: google-github-actions/auth@v2
182+
with:
183+
credentials_json: ${{ secrets.DEV_GCP_MOBILITY_FEEDS_SA_KEY }}
184+
185+
- name: Login to Artifact Registry
186+
uses: docker/login-action@v3
187+
with:
188+
registry: ${{ env.REGION }}-docker.pkg.dev
189+
username: _json_key_base64
190+
password: ${{ secrets.DEV_GCP_MOBILITY_FEEDS_SA_KEY }}
191+
192+
- name: Build & Push Docker image
193+
run: |
194+
scripts/docker-build-push.sh \
195+
-project_id "${{ env.PROJECT_ID }}" \
196+
-repo_name "${{ env.ARTIFACT_REPO }}" \
197+
-service "${{ steps.names.outputs.service_name }}" \
198+
-region "${{ env.REGION }}" \
199+
-version "${{ steps.names.outputs.image_tag }}"
200+
201+
- name: GCloud Setup
202+
uses: google-github-actions/setup-gcloud@v2
203+
204+
- name: Deploy to personal Cloud Run
205+
# Creates/updates ONLY developer-feed-api-<suffix> — shared dev Cloud Run is untouched.
206+
# Secret names follow the same DEV_<KEY> pattern used by Terraform (infra/feed-api/main.tf).
207+
run: |
208+
IMAGE="${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.ARTIFACT_REPO }}/${{ steps.names.outputs.service_name }}:${{ steps.names.outputs.image_tag }}"
209+
gcloud run deploy "${{ steps.names.outputs.service_name }}" \
210+
--image "$IMAGE" \
211+
--platform managed \
212+
--region "${{ env.REGION }}" \
213+
--allow-unauthenticated \
214+
--project "${{ env.PROJECT_ID }}" \
215+
--service-account "${{ vars.DEV_MOBILITY_FEEDS_DEPLOYER_SERVICE_ACCOUNT }}" \
216+
--update-secrets "FEEDS_DATABASE_URL=DEV_FEEDS_DATABASE_URL:latest,USERS_DATABASE_URL=DEV_USERS_DATABASE_URL:latest,S2S_JWT_SECRET=DEV_S2S_JWT_SECRET:latest" \
217+
--set-env-vars "PROJECT_ID=${{ env.PROJECT_ID }}" \
218+
--vpc-connector "projects/${{ env.VPC_CONNECTOR_PROJECT }}/locations/${{ env.REGION }}/connectors/${{ env.VPC_CONNECTOR }}" \
219+
--vpc-egress all
220+
221+
- name: Get service URL
222+
id: get_url
223+
run: |
224+
URL=$(gcloud run services describe "${{ steps.names.outputs.service_name }}" \
225+
--platform managed \
226+
--region "${{ env.REGION }}" \
227+
--project "${{ env.PROJECT_ID }}" \
228+
--format 'value(status.url)')
229+
echo "url=${URL}" >> "$GITHUB_OUTPUT"
230+
231+
# ── Operations API: build zip and deploy to personal Cloud Function ────────
232+
deploy-operations-api:
233+
if: ${{ github.event.inputs.deploy_operations_api == 'true' }}
234+
runs-on: ubuntu-latest
235+
permissions: write-all
236+
needs: [generate-and-build]
237+
outputs:
238+
function_name: ${{ steps.names.outputs.function_name }}
239+
function_url: ${{ steps.get_url.outputs.url }}
240+
steps:
241+
- name: Checkout code
242+
uses: actions/checkout@v4
243+
244+
- name: Set function name
245+
id: names
246+
run: |
247+
echo "function_name=operations-api-personal-${{ github.event.inputs.name_suffix }}" >> "$GITHUB_OUTPUT"
248+
249+
- name: Download operations API build
250+
uses: actions/download-artifact@v4
251+
with:
252+
name: operations_api_build
253+
path: functions-python/operations_api/.dist/
254+
255+
- name: Authenticate to Google Cloud
256+
uses: google-github-actions/auth@v2
257+
with:
258+
credentials_json: ${{ secrets.DEV_GCP_MOBILITY_FEEDS_SA_KEY }}
259+
260+
- name: Load OPERATIONS_OAUTH2_CLIENT_ID from 1Password
261+
uses: 1password/load-secrets-action@v2
262+
with:
263+
export-env: true
264+
env:
265+
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
266+
OPERATIONS_OAUTH2_CLIENT_ID: "op://rbiv7rvkkrsdlpcrz3bmv7nmcu/GCP_RETOOL_OAUTH2_CREDS/username"
267+
268+
- name: GCloud Setup
269+
uses: google-github-actions/setup-gcloud@v2
270+
271+
- name: Deploy to personal Cloud Function
272+
# Creates/updates ONLY operations-api-personal-<suffix> — shared operations-api is untouched.
273+
# Secret names follow the same DEV_<KEY> pattern used by Terraform (infra/functions-python/main.tf).
274+
run: |
275+
gcloud functions deploy "${{ steps.names.outputs.function_name }}" \
276+
--gen2 \
277+
--project "${{ env.PROJECT_ID }}" \
278+
--region "${{ env.REGION }}" \
279+
--runtime python311 \
280+
--entry-point main \
281+
--source functions-python/operations_api/.dist/build \
282+
--service-account "${{ vars.DEV_MOBILITY_FEEDS_DEPLOYER_SERVICE_ACCOUNT }}" \
283+
--memory 1Gi \
284+
--timeout 540s \
285+
--max-instances 10 \
286+
--min-instances 0 \
287+
--concurrency 100 \
288+
--cpu 1 \
289+
--ingress-settings ALLOW_ALL \
290+
--allow-unauthenticated \
291+
--set-env-vars "ENVIRONMENT=dev,PROJECT_ID=${{ env.PROJECT_ID }},GCP_REGION=${{ env.REGION }},GOOGLE_CLIENT_ID=${OPERATIONS_OAUTH2_CLIENT_ID}" \
292+
--set-secrets "FEEDS_DATABASE_URL=DEV_FEEDS_DATABASE_URL:latest,USERS_DATABASE_URL=DEV_USERS_DATABASE_URL:latest"
293+
294+
- name: Get function URL
295+
id: get_url
296+
run: |
297+
URL=$(gcloud functions describe "${{ steps.names.outputs.function_name }}" \
298+
--gen2 \
299+
--region "${{ env.REGION }}" \
300+
--project "${{ env.PROJECT_ID }}" \
301+
--format 'value(serviceConfig.uri)')
302+
echo "url=${URL}" >> "$GITHUB_OUTPUT"
303+
304+
# ── Summary ───────────────────────────────────────────────────────────────
305+
summary:
306+
runs-on: ubuntu-latest
307+
needs: [deploy-feeds-api, deploy-operations-api]
308+
if: always()
309+
steps:
310+
- name: Write job summary
311+
run: |
312+
echo "## 🚀 Personal API deployment" >> "$GITHUB_STEP_SUMMARY"
313+
echo "" >> "$GITHUB_STEP_SUMMARY"
314+
echo "| Service | Name | URL |" >> "$GITHUB_STEP_SUMMARY"
315+
echo "|---------|------|-----|" >> "$GITHUB_STEP_SUMMARY"
316+
317+
FEEDS_URL="${{ needs.deploy-feeds-api.outputs.service_url }}"
318+
FEEDS_NAME="${{ needs.deploy-feeds-api.outputs.service_name }}"
319+
if [ -n "$FEEDS_URL" ]; then
320+
echo "| Feeds API | \`${FEEDS_NAME}\` | [${FEEDS_URL}/docs/](${FEEDS_URL}/docs/) |" >> "$GITHUB_STEP_SUMMARY"
321+
fi
322+
323+
OPS_URL="${{ needs.deploy-operations-api.outputs.function_url }}"
324+
OPS_NAME="${{ needs.deploy-operations-api.outputs.function_name }}"
325+
if [ -n "$OPS_URL" ]; then
326+
echo "| Operations API | \`${OPS_NAME}\` | [${OPS_URL}](${OPS_URL}) |" >> "$GITHUB_STEP_SUMMARY"
327+
fi
328+
329+
echo "" >> "$GITHUB_STEP_SUMMARY"
330+
echo "> ⚠️ These are personal services sharing the **dev databases** (read/write)." >> "$GITHUB_STEP_SUMMARY"
331+
echo "> No shared Cloud Run, Terraform state, load balancer, or IAM was modified." >> "$GITHUB_STEP_SUMMARY"

0 commit comments

Comments
 (0)