Repository navigation
CI CD
This replaces the interactive source setup.sh / source destroy.sh flow with
two CI workflows so you stop getting charged for OCI compute when you're not
using the app.
-
Deploy (
.github/workflows/deploy.yml) — runs on every push tomain(or manually). Provisions infra with Terraform if it isn't already up, re-bootstraps the DB + secrets, builds/pushes the image, and rolls out the app. Safe to run repeatedly (idempotent). -
Undeploy (
.github/workflows/undeploy.yml) — manual only. Targets only the billed compute (OKE cluster + node pool) and sweeps the leftover Load Balancer. The Always-Free ATP database, its bucket and the VCN are preserved, so your data survives and redeploys are faster.
Why the old
undeploy.shdidn't stop billing: it only deleted the Kubernetes Deployment/Service. The compute you pay for is the OKE node pool (3 ×VM.Standard.E3.Flex, seeterraform/containerengine.tf), which Terraform owns and which keeps running untilterraform destroy.
In the OCI Console: Profile → User settings → API keys → Add API key → Generate API key pair. Download the private key, and click Add. OCI shows a Configuration file preview — copy these values:
-
user→ secretOCI_USER_OCID -
fingerprint→ secretOCI_FINGERPRINT -
tenancy→ secretOCI_TENANCY_OCID -
region→ secretOCI_REGION(e.g.mx-queretaro-1) - the downloaded private key file contents → secret
OCI_PRIVATE_KEY
OCI_PRIVATE_KEYmay be the raw PEM (-----BEGIN PRIVATE KEY----- …) or a base64 blob of it —setup-oci-auth.shaccepts either.
Terraform state must persist between runs (so "deploy if not deployed" and undeploy both know what exists). We store it in an OCI Object Storage bucket via its S3-compatible API.
-
Create a bucket (Console → Object Storage) e.g.
mtdr-tf-statein the same compartment/region → secretTF_STATE_BUCKET. - Get your Object Storage namespace (Console → Object Storage shows it, or
oci os ns get) → secretOCI_NAMESPACE. -
Profile → User settings → Customer Secret Keys → Generate Secret Key.
This gives an Access Key and a Secret Key (S3 credentials):
- Access Key → secret
OCI_S3_ACCESS_KEY - Secret Key → secret
OCI_S3_SECRET_KEY
- Access Key → secret
-
OCI_COMPARTMENT_OCID— the compartment to deploy into. The old script created one automatically; in CI we use an existing one (more reversible). Create/pick a compartment and copy its OCID. -
RUN_NAME— short stable name (1–13 chars, letter first), e.g.mtdrworkshop. -
MTDR_KEY— short stable unique suffix, e.g.mtdr01. Pick once and never change it — resource names (bucket, repo, VCN) derive from it. -
TODO_PDB_NAME— the ATP database name, e.g.mtdrdb01.
If you already have infrastructure running from the old flow and want CI to adopt it instead of creating a parallel copy, set
RUN_NAME/MTDR_KEY/TODO_PDB_NAMEto the existing values andterraform importthe resources into the remote state first. Otherwise just let the first deploy create a fresh set (and destroy the old one from the Console to stop its billing).
| Secret | Used for |
|---|---|
OCI_USERNAME |
OCIR docker login + image-pull secret |
OCI_AUTH_TOKEN |
OCIR docker login (Auth Token, not the API key) |
UI_USERNAME |
UI login username injected into the manifest |
UI_PASSWORD |
UI login password (frontendadmin secret) |
DB_ADMIN_PASSWORD |
ATP ADMIN + TODOUSER password (dbuser secret) |
OPEN_ROUTER_API_KEY |
OpenRouter key for the AI assistant (app-env secret) |
TELEGRAM_BOT_TOKEN |
Telegram bot token (app-env secret) |
TELEGRAM_BOT_NAME |
Telegram bot username, e.g. OCI_TEAM31_BOT (app-env) |
DB_ADMIN_PASSWORD rules: 12–30 chars, ≥1 upper, ≥1 lower, ≥1 digit, no " and
not containing admin.
OPEN_ROUTER_API_KEY, TELEGRAM_BOT_TOKEN and TELEGRAM_BOT_NAME are written
into the app-env Kubernetes secret by bootstrap-app.sh and reach the app via
the manifest's envFrom. JWT_SECRET is not configured — the bootstrap
generates one on the first deploy and reuses it on later deploys (stable across
the 2 replicas and restarts).
OCI_TENANCY_OCID OCI_USER_OCID OCI_FINGERPRINT OCI_PRIVATE_KEY
OCI_REGION OCI_NAMESPACE OCI_COMPARTMENT_OCID
TF_STATE_BUCKET OCI_S3_ACCESS_KEY OCI_S3_SECRET_KEY
RUN_NAME MTDR_KEY TODO_PDB_NAME
OCI_USERNAME OCI_AUTH_TOKEN
UI_USERNAME UI_PASSWORD DB_ADMIN_PASSWORD
OPEN_ROUTER_API_KEY TELEGRAM_BOT_TOKEN TELEGRAM_BOT_NAME
# Auto-generated by the deploy job, do NOT set: JWT_SECRET
Add them under Settings → Environments → prod (the workflows use
environment: prod) or as repo secrets.
KUBECONFIG_B64 is no longer needed — the deploy job now generates the
kubeconfig from the freshly-provisioned cluster.
-
Deploy: push to
main, or Actions → Deploy → Run workflow. -
Undeploy (stop billing): Actions → Undeploy → Run workflow, type
destroyin the confirm box.
After an undeploy, the next deploy rebuilds the OKE cluster and re-creates the
Kubernetes-side resources (wallet secret, dbuser/frontendadmin/etc.). Because
the ATP database is preserved, the schema bootstrap is a no-op against your
existing TODOUSER schema and your data is kept.
The ATP DB here is
is_free_tier = true, so leaving it up costs nothing. If you ever want a true zero-footprint teardown that also drops the DB, runterraform destroywithout the-targetflags (or destroy the DB from the Console) — but you'll lose all data.
- The first deploy after enabling this takes ~15–25 min (OKE + ATP provisioning).
- The DB schema bootstrap uses Oracle Instant Client (
sqlplus) installed in the deploy job. If Oracle changes the Instant Client download URL, update the version indeploy.yml→ Install Oracle Instant Client. - Terraform's S3 backend for OCI needs
skip_s3_checksum = true(already set inutils/ci/terraform-apply.sh); without it uploads fail against OCI. - Local
source setup.sh/source destroy.shstill work and still use local state — the remote backend file is generated only inside CI and is never committed.