Skip to content

Commit 8a2b5e0

Browse files
committed
Add GitHub Actions workflow for Cloud Run deploy
1 parent 7fd1fbe commit 8a2b5e0

2 files changed

Lines changed: 135 additions & 10 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Deploy to Cloud Run
2+
3+
on:
4+
push:
5+
branches: [deploy-github]
6+
workflow_dispatch:
7+
8+
env:
9+
PROJECT_ID: fullstackpro-python
10+
REGION: us-central1
11+
SERVICE: fastapi-hello
12+
13+
jobs:
14+
deploy:
15+
runs-on: ubuntu-latest
16+
17+
permissions:
18+
contents: read
19+
id-token: write # required for Workload Identity Federation
20+
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
25+
- name: Authenticate to Google Cloud
26+
uses: google-github-actions/auth@v2
27+
with:
28+
workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }}
29+
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
30+
31+
- name: Set up gcloud
32+
uses: google-github-actions/setup-gcloud@v2
33+
34+
- name: Deploy to Cloud Run
35+
run: |
36+
gcloud run deploy "$SERVICE" \
37+
--source . \
38+
--project "$PROJECT_ID" \
39+
--region "$REGION" \
40+
--platform managed \
41+
--allow-unauthenticated \
42+
--quiet
43+
44+
- name: Show service URL
45+
run: |
46+
gcloud run services describe "$SERVICE" \
47+
--project "$PROJECT_ID" \
48+
--region "$REGION" \
49+
--format="value(status.url)"

README.md

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33
A minimal FastAPI "Hello, World!" service, managed with [uv](https://docs.astral.sh/uv/).
44

5-
This `main` branch contains only the app and local dev setup. Deployment is demonstrated on two separate branches:
6-
7-
- **[`deploy-script`](../../tree/deploy-script)** — deploy with a local `gcloud` script ([deploy.sh](deploy.sh)).
8-
- **[`deploy-github`](../../tree/deploy-github)** — deploy from GitHub via a GitHub Actions workflow.
5+
**Branch: `deploy-github`** — deploys to Google Cloud Run automatically when you push to this branch, using a GitHub Actions workflow with Workload Identity Federation for keyless auth.
96

107
## Stack
118

@@ -23,7 +20,9 @@ This `main` branch contains only the app and local dev setup. Deployment is demo
2320
├── uv.lock # locked dependency graph
2421
├── .python-version # pins Python 3.13 for uv
2522
├── Dockerfile # uv-based container build
26-
└── .dockerignore
23+
├── .dockerignore
24+
└── .github/workflows/
25+
└── deploy.yml # GitHub Actions → Cloud Run
2726
```
2827

2928
## Endpoints
@@ -73,12 +72,89 @@ docker run --rm -p 8080:8080 fastapi-basic
7372

7473
The image installs deps with `uv sync --frozen --no-dev` against `uv.lock`, so the container matches local exactly.
7574

76-
## Deploying
75+
## Deploy to Google Cloud Run (via GitHub Actions)
76+
77+
A push to the `deploy-github` branch triggers [.github/workflows/deploy.yml](.github/workflows/deploy.yml), which builds and deploys the service with `gcloud run deploy --source .`.
7778

78-
Check out one of the deploy branches:
79+
### One-time GCP setup
80+
81+
You only need to do this once per project. Replace `PROJECT_ID` and `GITHUB_REPO` (e.g. `your-org/your-repo`) with your values.
7982

8083
```bash
81-
git checkout deploy-script # local gcloud-based deploy
82-
# or
83-
git checkout deploy-github # GitHub Actions deploy
84+
PROJECT_ID="fullstackpro-python"
85+
GITHUB_REPO="OWNER/REPO"
86+
SA_NAME="github-deployer"
87+
POOL="github-pool"
88+
PROVIDER="github-provider"
89+
90+
gcloud config set project "$PROJECT_ID"
91+
PROJECT_NUMBER=$(gcloud projects describe "$PROJECT_ID" --format="value(projectNumber)")
92+
93+
# 1. Enable APIs
94+
gcloud services enable \
95+
run.googleapis.com \
96+
cloudbuild.googleapis.com \
97+
artifactregistry.googleapis.com \
98+
iamcredentials.googleapis.com
99+
100+
# 2. Create deployer service account
101+
gcloud iam service-accounts create "$SA_NAME" \
102+
--display-name="GitHub Actions Cloud Run deployer"
103+
SA_EMAIL="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
104+
105+
# 3. Grant it the roles it needs
106+
for role in roles/run.admin roles/iam.serviceAccountUser roles/cloudbuild.builds.editor roles/artifactregistry.writer roles/storage.admin; do
107+
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
108+
--member="serviceAccount:${SA_EMAIL}" --role="$role"
109+
done
110+
111+
# 4. Create a Workload Identity Pool + GitHub OIDC provider
112+
gcloud iam workload-identity-pools create "$POOL" \
113+
--location=global --display-name="GitHub Pool"
114+
115+
gcloud iam workload-identity-pools providers create-oidc "$PROVIDER" \
116+
--location=global --workload-identity-pool="$POOL" \
117+
--display-name="GitHub OIDC" \
118+
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
119+
--attribute-condition="assertion.repository=='${GITHUB_REPO}'" \
120+
--issuer-uri="https://token.actions.githubusercontent.com"
121+
122+
# 5. Let the GitHub repo impersonate the service account
123+
gcloud iam service-accounts add-iam-policy-binding "$SA_EMAIL" \
124+
--role=roles/iam.workloadIdentityUser \
125+
--member="principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL}/attribute.repository/${GITHUB_REPO}"
126+
127+
# 6. Print the values to put in GitHub secrets
128+
echo "GCP_WIF_PROVIDER = projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL}/providers/${PROVIDER}"
129+
echo "GCP_SERVICE_ACCOUNT = ${SA_EMAIL}"
84130
```
131+
132+
### Add GitHub repo secrets
133+
134+
In your GitHub repo → **Settings → Secrets and variables → Actions → New repository secret**, add:
135+
136+
| Name | Value |
137+
| --------------------- | ------------------------------------------------------------------------------------- |
138+
| `GCP_WIF_PROVIDER` | `projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/providers/github-provider` |
139+
| `GCP_SERVICE_ACCOUNT` | `github-deployer@PROJECT_ID.iam.gserviceaccount.com` |
140+
141+
(The previous step's `echo` lines print the exact values.)
142+
143+
### Trigger a deploy
144+
145+
```bash
146+
git checkout deploy-github
147+
# make a change…
148+
git commit -am "trigger deploy"
149+
git push origin deploy-github
150+
```
151+
152+
Watch the run under the repo's **Actions** tab. The final step prints the Cloud Run service URL.
153+
154+
### Customising
155+
156+
- Change `PROJECT_ID`, `REGION`, or `SERVICE` in [.github/workflows/deploy.yml](.github/workflows/deploy.yml).
157+
- To deploy on pushes to a different branch, edit the `on.push.branches` list.
158+
- For a private service, drop `--allow-unauthenticated` and add IAM bindings.
159+
160+
For a manual, local deploy instead, see the [`deploy-script`](../../tree/deploy-script) branch.

0 commit comments

Comments
 (0)