Skip to content
This repository was archived by the owner on Mar 30, 2026. It is now read-only.

Commit 46e3367

Browse files
committed
feat: Enhance GitHub App integration with secret management and webhook handling
1 parent efbc70d commit 46e3367

File tree

19 files changed

+588
-64
lines changed

19 files changed

+588
-64
lines changed

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,6 @@ vignettes/*.pdf
299299
# R Environment Variables
300300
.Renviron
301301

302-
# pkgdown site
303-
docs/
304302

305303
# translation temp files
306304
po/*~

cloudbuild.yaml

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
11
steps:
22
- name: gcr.io/cloud-builders/docker
3-
args: ["build", "-t", "gcr.io/$PROJECT_ID/ons-github-app:$COMMIT_SHA", "."]
3+
args: ["build", "-t", "gcr.io/$PROJECT_ID/${_SERVICE_NAME}:$COMMIT_SHA", "."]
44
- name: gcr.io/cloud-builders/docker
5-
args: ["push", "gcr.io/$PROJECT_ID/ons-github-app:$COMMIT_SHA"]
5+
args: ["push", "gcr.io/$PROJECT_ID/${_SERVICE_NAME}:$COMMIT_SHA"]
6+
- name: gcr.io/google.com/cloudsdktool/cloud-sdk
7+
entrypoint: bash
8+
args:
9+
- -eu
10+
- -c
11+
- |
12+
if [[ -z "${_GITHUB_APP_ID}" ]]; then
13+
echo "ERROR: Missing required substitution _GITHUB_APP_ID (your GitHub App ID)."
14+
echo "Set it on your trigger or pass: gcloud builds submit --substitutions=_GITHUB_APP_ID=123456"
15+
exit 1
16+
fi
617
- name: gcr.io/google.com/cloudsdktool/cloud-sdk
718
entrypoint: gcloud
819
args:
920
- run
1021
- deploy
1122
- "${_SERVICE_NAME}"
1223
- --image
13-
- "gcr.io/$PROJECT_ID/ons-github-app:$COMMIT_SHA"
24+
- "gcr.io/$PROJECT_ID/${_SERVICE_NAME}:$COMMIT_SHA"
1425
- --region
1526
- "${_REGION}"
1627
- --platform
1728
- managed
18-
- --allow-unauthenticated
29+
- --set-env-vars
30+
- "GITHUB_APP_ID=${_GITHUB_APP_ID},GITHUB_ACCEPTED_EVENTS=pull_request,GITHUB_PRIVATE_KEY_FILE=/var/secrets/github_private_key,GITHUB_WEBHOOK_SECRET_FILE=/var/secrets/github_webhook_secret"
31+
- --secret
32+
- github-app-private-key=/var/secrets/github_private_key
33+
- --secret
34+
- github-webhook-secret=/var/secrets/github_webhook_secret
1935
substitutions:
2036
_SERVICE_NAME: ons-github-app
2137
_REGION: europe-west2
38+
_GITHUB_APP_ID: ""
2239
images:
23-
- "gcr.io/$PROJECT_ID/ons-github-app:$COMMIT_SHA"
40+
- "gcr.io/$PROJECT_ID/${_SERVICE_NAME}:$COMMIT_SHA"

deploy.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ set -euo pipefail
44
: "${PROJECT_ID:?Set PROJECT_ID}"
55
: "${SERVICE_NAME:=ons-github-app}"
66
: "${REGION:=europe-west2}"
7+
: "${GITHUB_APP_ID:?Set GITHUB_APP_ID}"
8+
9+
: "${GITHUB_ACCEPTED_EVENTS:=pull_request}"
710

811
IMAGE="gcr.io/${PROJECT_ID}/${SERVICE_NAME}:latest"
912

@@ -13,4 +16,6 @@ gcloud run deploy "${SERVICE_NAME}" \
1316
--image "${IMAGE}" \
1417
--region "${REGION}" \
1518
--platform managed \
16-
--allow-unauthenticated
19+
--set-env-vars "GITHUB_APP_ID=${GITHUB_APP_ID},GITHUB_ACCEPTED_EVENTS=${GITHUB_ACCEPTED_EVENTS},GITHUB_PRIVATE_KEY_FILE=/var/secrets/github_private_key,GITHUB_WEBHOOK_SECRET_FILE=/var/secrets/github_webhook_secret" \
20+
--secret github-app-private-key=/var/secrets/github_private_key \
21+
--secret github-webhook-secret=/var/secrets/github_webhook_secret

docs/tutorial/01-prereqs.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# 01 — Prerequisites and repo overview
2+
3+
## Prerequisites
4+
5+
You need:
6+
7+
- A GCP project with billing enabled.
8+
- `gcloud` installed and authenticated (`gcloud auth login`).
9+
- Terraform >= 1.6.
10+
- Docker (and ideally Buildx for `linux/amd64` builds).
11+
- A GitHub account that can create and install GitHub Apps on the target org/user.
12+
13+
Recommended:
14+
15+
- `pre-commit` installed.
16+
- Permissions in GCP to create Cloud Run, API Gateway, Artifact Registry, Secret Manager secrets, IAM bindings, and KMS keys.
17+
18+
## Repo overview (what code does what)
19+
20+
### Runtime (FastAPI)
21+
22+
- `src/app.py`
23+
- `POST /webhooks/github` is the GitHub webhook endpoint.
24+
- Verifies the webhook signature.
25+
- Handles `pull_request.opened` by posting a comment back on the PR.
26+
- `GET /healthz` is a health check.
27+
28+
- `src/webhook.py`
29+
- Computes an HMAC SHA-256 digest of the request body using the webhook secret.
30+
- Compares it to the `X-Hub-Signature-256` header.
31+
32+
- `src/github_app.py`
33+
- Creates a GitHub App JWT (signed with the app’s private key).
34+
- Exchanges it for an **installation access token**.
35+
- Uses the installation token to call GitHub’s API (post PR comment).
36+
37+
### Secrets strategy (best practice)
38+
39+
- **Local dev**: you can pass secrets via environment variables (e.g. `docker run --env-file .env ...`).
40+
- **Cloud Run**: secrets should not be plain env vars.
41+
- Store secret values in **Secret Manager**.
42+
- Mount them into the container as **files**.
43+
- The app reads them via:
44+
- `GITHUB_PRIVATE_KEY_FILE`
45+
- `GITHUB_WEBHOOK_SECRET_FILE`
46+
47+
This keeps secret values out of container images, build logs, and Terraform state.
48+
49+
## Install and run repo tooling (optional but recommended)
50+
51+
From the repo root:
52+
53+
```bash
54+
python -m venv .venv
55+
source .venv/bin/activate
56+
pip install -r requirements.txt
57+
pip install pre-commit
58+
pre-commit install
59+
```

docs/tutorial/02-remote-state.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# 02 — Remote state bootstrap (GCS + KMS)
2+
3+
Terraform state should be stored remotely so it’s durable, shareable, and can be locked.
4+
This repo includes an opinionated bootstrap module under `infra/remote-state/`.
5+
6+
## What this step creates
7+
8+
- Enables:
9+
- Cloud Storage API
10+
- Cloud KMS API
11+
- IAM API
12+
- A KMS key ring + crypto key (used to encrypt Terraform state)
13+
- A GCS bucket for Terraform remote state
14+
15+
## Steps
16+
17+
1) Choose your project and principals
18+
19+
You need:
20+
21+
- `PROJECT_ID` — your GCP project
22+
- A principal to **read** state objects (viewer)
23+
- A principal to **write** state objects (admin)
24+
25+
Examples of principals:
26+
27+
- `user:you@example.com`
28+
- `serviceAccount:ci@your-project.iam.gserviceaccount.com`
29+
30+
2) Create a `terraform.tfvars`
31+
32+
In `infra/remote-state/`, create `terraform.tfvars`:
33+
34+
```hcl
35+
project_id = "<your-project-id>"
36+
storage_object_viewer_principal = "user:you@example.com"
37+
storage_object_admin_principal = "user:you@example.com"
38+
```
39+
40+
3) Apply the bootstrap
41+
42+
```bash
43+
cd infra/remote-state
44+
terraform init
45+
terraform apply
46+
```
47+
48+
4) Capture outputs
49+
50+
Terraform will output:
51+
52+
- `state_bucket_name`
53+
- `kms_key_resource_name`
54+
55+
You’ll use these in the next tutorial.
56+
57+
## Why this matters for your workflow
58+
59+
- Remote state enables safe collaboration and reduces the risk of state loss.
60+
- KMS encryption ensures sensitive metadata in state is encrypted at rest.

docs/tutorial/03-github-app.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# 03 — Create and configure the GitHub App
2+
3+
## What you’re setting up
4+
5+
A GitHub App that:
6+
7+
- Receives `pull_request` webhooks
8+
- Uses an app private key to authenticate as the GitHub App
9+
- Uses an **installation token** to comment on PRs
10+
11+
## Steps (GitHub UI)
12+
13+
1) Create a new GitHub App
14+
15+
- GitHub → Settings → Developer settings → GitHub Apps → **New GitHub App**
16+
17+
2) Basic settings
18+
19+
- **GitHub App name**: choose a unique name
20+
- **Homepage URL**: any valid URL (can be placeholder)
21+
22+
3) Webhook settings
23+
24+
- **Webhook URL**: set a placeholder for now (you will update it after Terraform deploy)
25+
- Example placeholder: `https://example.com/webhooks/github`
26+
- **Webhook secret**: generate one and save it (you’ll store it in Secret Manager)
27+
28+
Generate a webhook secret locally:
29+
30+
```bash
31+
openssl rand -hex 32
32+
```
33+
34+
4) Permissions (minimum for “comment on PR opened”)
35+
36+
The app posts PR comments via the Issues Comments API (PRs are Issues underneath), so it needs:
37+
38+
- **Repository permissions**
39+
- Metadata: **Read-only** (required by GitHub)
40+
- Issues: **Read & write** (to create PR comments)
41+
- Pull requests: **Read-only** (webhook payload context)
42+
43+
5) Subscribe to events
44+
45+
- Subscribe to: **Pull request** events
46+
47+
6) Create the App and generate a private key
48+
49+
- Create the app
50+
- In the app settings page, generate a private key
51+
- Download the `.pem` file
52+
53+
7) Install the App
54+
55+
- Install the app on your org/user
56+
- Select the repository you want it to operate on
57+
58+
## Values you will need later
59+
60+
- **App ID** (used as `GITHUB_APP_ID`)
61+
- **Webhook secret** (store in Secret Manager)
62+
- **Private key file** (store in Secret Manager)
63+
- **Installation** is included in webhook payloads (the app uses it automatically)

0 commit comments

Comments
 (0)