Skip to content

Commit 2423eca

Browse files
committed
docs(ci): move runtime keys to Secret Manager, update README
1 parent 333b058 commit 2423eca

File tree

2 files changed

+106
-83
lines changed

2 files changed

+106
-83
lines changed

.github/workflows/deploy.yml

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,47 @@
1-
# GitHub Actions workflow
2-
# ──────────────────────────────────────────────────────────────────────────
3-
# • Builds the application with Cloud Build (Buildpacks, from source).
4-
# • Deploys the resulting container as a new revision of the existing
5-
# Cloud Run service “wasteassistant”.
6-
# • Passes runtime secrets (OpenAI key + Flask secret) via env-vars.
7-
# • Triggers on every push to main and via the manual “Run workflow” button.
8-
# ──────────────────────────────────────────────────────────────────────────
1+
# .github/workflows/deploy.yml
2+
# ------------------------------------------------------------------
3+
# Build the app from source (Buildpacks) and deploy to Cloud Run.
4+
# Secrets flow:
5+
# • GitHub : GCP_SA_KEY (JSON), GCP_PROJECT_ID, GCP_REGION
6+
# • SecretMgr : OPENAI_API_KEY , FLASK_SECRET ← mounted at runtime
7+
# ------------------------------------------------------------------
98

109
name: Build & Deploy to Cloud Run
1110

12-
# ─── Triggers ────────────────────────────────────────────────────────────
11+
# ───────────────────────────── triggers ──────────────────────────────
1312
on:
14-
push: # automatic deploy on each push to main
13+
push: # every push to main
1514
branches: ["main"]
16-
workflow_dispatch: # enable manual runs from the UI
15+
workflow_dispatch: # manual trigger
1716

18-
# ─── Job definition ──────────────────────────────────────────────────────
17+
# ───────────────────────────── job ───────────────────────────────────
1918
jobs:
2019
build-and-deploy:
21-
runs-on: ubuntu-latest # GitHub-hosted runner
20+
runs-on: ubuntu-latest
2221

2322
steps:
24-
# 1️⃣ Check out repository contents at the current commit
23+
# 1️⃣ Checkout repo
2524
- name: Checkout source
2625
uses: actions/checkout@v4
2726

28-
# 2️⃣ Authenticate to Google Cloud using a service-account key
29-
# stored in the secret GCP_SA_KEY (JSON key with the required roles).
27+
# 2️⃣ Authenticate to GCP with the service-account JSON key
3028
- name: Authenticate to Google Cloud
3129
uses: google-github-actions/auth@v1
3230
with:
3331
credentials_json: ${{ secrets.GCP_SA_KEY }}
3432

35-
# 3️⃣ Build from source and deploy in a single command.
36-
# The Buildpacks flow (source: .) invokes Cloud Build automatically,
37-
# stores the image in Artifact Registry and creates a new revision.
38-
# env_vars injects the runtime secrets into the container.
33+
# 3️⃣ Build + deploy; mount secrets from Secret Manager
3934
- name: Deploy to Cloud Run
4035
uses: google-github-actions/deploy-cloudrun@v2
4136
with:
42-
service: wasteassistant # existing Cloud Run service
43-
source: . # buildpacks build from repo root
37+
service: wasteassistant # pre-created Cloud Run service
38+
source: . # Buildpacks build from repo root
4439
project_id: ${{ secrets.GCP_PROJECT_ID }}
4540
region: ${{ secrets.GCP_REGION }}
46-
env_vars: | # runtime secrets
47-
api_key=${{ secrets.OPENAI_API_KEY }}
48-
secret_key=${{ secrets.FLASK_SECRET }}
49-
flags: --allow-unauthenticated # keep the public URL open
41+
42+
# ↳ map Secret Manager versions → env-vars at runtime
43+
secrets: |
44+
OPENAI_API_KEY=projects/${{ secrets.GCP_PROJECT_ID }}/secrets/OPENAI_API_KEY:latest
45+
FLASK_SECRET=projects/${{ secrets.GCP_PROJECT_ID }}/secrets/FLASK_SECRET:latest
46+
47+
flags: --allow-unauthenticated # keep public endpoint

README.md

Lines changed: 82 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,132 @@
11
# Waste Assistant Chatbot for Jyväskylä ♻️
22

3-
**Waste Assistant** is a friendly Flask + OpenAI bot that helps newcomers—and anyone else—figure out where every item belongs in the city’s recycling system.
3+
**Waste Assistant** is a friendly Flask + OpenAI bot that helps newcomers—and anyone else—figure out where every item belongs in the city’s recycling system.
44

5-
> "Where should this go in Jyväskylä?"
5+
> Where should this go in Jyväskylä?\
66
> Ask in English or Finnish, or snap a photo—the bot knows the answer.
77
8-
<p align="center">
9-
<img src="docs/demo.png" alt="Demo screenshot" width="300">
10-
</p>
11-
12-
138
---
149

1510
## ✨ Features
1611

17-
* **Local expertise** Tailored to Jyväskylä’s official sorting rules.
18-
* **Text *or* photo** Type a question *or* upload an image of the item.
19-
* **Bilingual UI** Instantly switch between **English** and **Finnish**.
20-
* **Smart image compression** 500 × 500 JPEG (quality 75) before sending to OpenAI → fewer tokens & lower cost.
21-
* **GPT-4o-mini vision + chat** One model for language *and* image reasoning.
22-
* **Serverless host** Runs on Google Cloud Run; scales to zero when idle.
12+
- **Local expertise** — tailored to Jyväskylä’s official sorting rules.
13+
- **Text *****or***** photo** — type a question *or* upload an image of the item.
14+
- **Bilingual UI** — instantly switch between **English** and **Finnish**.
15+
- **Smart image compression** — 500 × 500 JPEG (quality 75) → fewer OpenAI tokens & lower cost.
16+
- **GPT‑4o‑mini vision + chat** — one model for language *and* image reasoning.
17+
- **Serverless host** — runs on Google Cloud Run; scales to zero when idle.
2318

2419
---
2520

2621
## 🔧 Tech stack
2722

28-
| Layer | Tool / Service |
29-
| ------------- | ------------------------------ |
30-
| Language | Python 3.10 |
31-
| Web framework | Flask |
32-
| AI / Vision | OpenAI API (gpt-4o-mini) |
33-
| Container | Docker |
34-
| CI / CD | GitHub ActionsCloud Build |
35-
| Runtime | Google Cloud Run |
23+
| Layer | Tool / Service |
24+
| ------------- | ---------------------------- |
25+
| Language | Python 3.10 |
26+
| Web framework | Flask |
27+
| AI / Vision | OpenAI API (gpt‑4o‑mini) |
28+
| Container | Docker |
29+
| CI / CD | GitHub Actions → Cloud Build |
30+
| Runtime | Google Cloud Run |
3631

3732
---
3833

39-
## 🚀 Quick start (local)
34+
## 🚀 Quick start (local)
4035

4136
```bash
42-
# 1 Clone & enter repo
43-
git clone https://github.com/<your-user>/waste-assistant.git
37+
# 1 Clone & enter repo
38+
git clone https://github.com/<youruser>/waste-assistant.git
4439
cd waste-assistant
4540

46-
# 2 Virtual env
41+
# 2 Virtual env
4742
python -m venv venv
4843
source venv/bin/activate # Windows: venv\Scripts\activate
4944

50-
# 3 Install deps
45+
# 3 Install deps
5146
pip install -r requirements.txt
5247

53-
# 4 Create .env
48+
# 4 Create .env (local only)
5449
cat > .env <<EOF
5550
api_key=YOUR_OPENAI_API_KEY
5651
secret_key=YOUR_FLASK_SECRET_KEY
5752
EOF
5853

59-
# 5 Run
54+
# 5 Run
6055
python app.py # → http://localhost:8086
6156
```
6257

6358
Open the URL, ask “Where do I throw plastic bags?” or upload a picture, and get an instant answer.
6459

6560
---
6661

67-
## 🖥️ Usage tips
62+
## 🖥️ Usage tips
6863

69-
* **Ask anything**"Cardboard pizza box?", "Where do batteries go?" …
70-
The bot replies with the right bin, plus nuances (e.g. rinse / remove labels).
71-
* **Photo mode** Drag-and-drop or tap the camera icon on mobile.
72-
The compressed image is analysed by GPT-4o-mini vision.
73-
* **Session memory** Context survives for 30 minutes so follow-ups are fluid.
64+
- **Ask anything** — "Cardboard pizza box?", "Where do batteries go?" …\
65+
The bot replies with the right bin, plus nuances (e.g. rinse / remove labels).
66+
- **Photo mode** — drag‑anddrop or tap the camera icon on mobile.\
67+
The compressed image is analysed by GPT‑4o‑mini vision.
68+
- **Session memory** — context survives for 30 minutes so followups are fluid.
7469

7570
---
7671

7772
## 🗂️ Code overview
7873

79-
* **`main.py`**Flask routes (`/`, `/ask`, `/reset`) + OpenAI calls.
80-
* **Prompts** System messages live in `model_instructions/` (EN & FI).
81-
* **Image pipeline**`compress_image()` resizes & recompresses uploads to save tokens.
82-
* **Session**Flask-Session stores chat history on disk (`./flask_session`).
74+
- `` — Flask routes (`/`, `/ask`, `/reset`) + OpenAI calls.
75+
- **Prompts** — system messages live in `model_instructions/` (EN & FI).
76+
- **Image pipeline** — `compress_image()` resizes & recompresses uploads.
77+
- **Session** — FlaskSession stores chat history on disk (`./flask_session`).
8378

8479
---
8580

86-
## ☁️ Continuous deployment (Cloud Run)
81+
## ☁️ Continuous deployment (Cloud Run)
82+
83+
The workflow `.github/workflows/deploy.yml` builds from source with Cloud Build and deploys a new revision on every push to `main`.
84+
85+
1. **Checkout** the repo.
86+
2. **Authenticate** using the service‑account key in `GCP_SA_KEY`.
87+
3. `gcloud run deploy --source .` — Buildpacks build & push the image.
88+
4. Cloud Run rolls out the revision and keeps the public URL unchanged.
89+
90+
> Public URL format: `https://wasteassistant‑xxxxx.a.run.app`
91+
92+
### GitHub Secrets required by the workflow
8793

88-
A single GitHub Actions workflow (`.github/workflows/deploy.yml`) handles CI/CD:
94+
| Name | Purpose |
95+
| -------------------- | -------------------------------------------------------- |
96+
| **GCP\_SA\_KEY** | JSON key with roles: Artifact Registry, Cloud Build, Run |
97+
| **GCP\_PROJECT\_ID** | e.g. `gpt-models-436109` |
98+
| **GCP\_REGION** | e.g. `europe-north1` |
8999

90-
1. **Checkout** code.
91-
2. **Authenticate** with a service-account key (`GCP_SA_KEY`).
92-
3. `gcloud run deploy --source .` – Buildpacks build & push the image.
93-
4. Cloud Run rolls out a new revision of the `wasteassistant` service.
100+
### Runtime secrets (Secret Manager)
94101

95-
> Public URL format: `https://wasteassistant-xxxxx.a.run.app`
102+
`OPENAI_API_KEY` and `FLASK_SECRET_KEY` are **not stored in GitHub**. They live in **Google Cloud Secret Manager** and are injected by Cloud Run as environment variables.
96103

97-
Required secrets:
104+
```bash
105+
# Create the secrets
106+
echo -n "sk-…" | gcloud secrets create OPENAI_API_KEY --data-file=-
107+
openssl rand -hex 32 | gcloud secrets create FLASK_SECRET_KEY --data-file=-
108+
109+
# Allow Cloud Run’s runtime SA to read them
110+
PROJECT_ID="gpt-models-436109"
111+
PROJECT_NUM=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
112+
RUN_SA="${PROJECT_NUM}-compute@developer.gserviceaccount.com"
113+
114+
for SEC in OPENAI_API_KEY FLASK_SECRET_KEY; do
115+
gcloud secrets add-iam-policy-binding $SEC \
116+
--member="serviceAccount:${RUN_SA}" \
117+
--role="roles/secretmanager.secretAccessor"
118+
done
119+
```
120+
121+
The workflow maps the latest secret versions to env‑vars:
98122

99-
| Secret name | Purpose / example value |
100-
| ---------------- | ------------------------------------------------------ |
101-
| GCP\_SA\_KEY | JSON key (Artifact Registry + Cloud Build + Run roles) |
102-
| GCP\_PROJECT\_ID | e.g. `gpt-models-436109` |
103-
| GCP\_REGION | e.g. `europe-north1` |
104-
| OPENAI\_API\_KEY | `sk-…` |
105-
| FLASK\_SECRET | `openssl rand -hex 32` (any long random string) |
123+
```yaml
124+
secrets: |
125+
OPENAI_API_KEY=projects/${{ secrets.GCP_PROJECT_ID }}/secrets/OPENAI_API_KEY:latest
126+
FLASK_SECRET=projects/${{ secrets.GCP_PROJECT_ID }}/secrets/FLASK_SECRET_KEY:latest
127+
```
128+
129+
The app then reads them with `os.getenv("api_key")` and `os.getenv("secret_key")`.
106130

107131
---
108132

@@ -111,13 +135,14 @@ Required secrets:
111135
Pull requests are welcome!
112136

113137
1. Fork → branch → commit with clear messages.
114-
2. Push and open a PR against `main`.
115-
3. CI will build & deploy the preview automatically.
138+
2. Push and open a PR against ``.
139+
3. CI will build & deploy the preview automatically.
116140

117-
Found a bug or want to suggest a new feature? Open an issue.
141+
Found a bug or have an idea? Open an issue.
118142

119143
---
120144

121145
## 📄 License
122146

123-
Apache-2.0. See `LICENSE` for full text
147+
Apache‑2.0 — see `LICENSE` for full text.
148+

0 commit comments

Comments
 (0)