Skip to content

Git Push Deploy

PaaS Runtime turns every git push into a full build-and-deploy cycle — like Heroku, but on your own cluster. No CI tooling to wire up, no deploy scripts to maintain.

Workflow

sequenceDiagram
  participant Dev as Developer
  participant Forgejo
  participant CP as Control Plane
  participant Tekton
  participant Harbor
  participant K8s

  Dev->>Forgejo: git push paas main
  Forgejo->>CP: POST /v1/webhooks/forgejo (HMAC)
  CP->>CP: verify HMAC + idempotency
  CP->>CP: lookup app by git_url
  CP->>Tekton: create PipelineRun
  Tekton->>Harbor: docker build + push
  Tekton->>CP: build complete callback
  CP->>K8s: update Deployment image tag
  K8s->>Dev: app live at https://app.runtime.di2amp.com

Prerequisites

1. Add an SSH key

PaaS uses SSH-authenticated git over the Forgejo cluster service. Add your public key once:

Via the dashboard (recommended):

  1. Open https://ma30.di2amp.com/runtime/dashboard/account
  2. Click + Add SSH key in the SSH Keys section
  3. Paste the contents of ~/.ssh/id_ed25519.pub and give it a memorable name

Via the API:

TOKEN=$(curl -s -X POST https://runtime.di2amp.com/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","password":"..."}' | jq -r '.data.access_token')

curl -X POST https://runtime.di2amp.com/api/v1/auth/ssh-keys \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d "{\"name\":\"laptop\",\"public_key\":\"$(cat ~/.ssh/id_ed25519.pub)\"}"

The response includes a SHA256:... fingerprint — keep it for verification.

2. Create the app

paas apps create my-app

PaaS provisions:

  • A K8s namespace + NetworkPolicy + Service
  • A Forgejo repo at https://forgejo.runtime.di2amp.com/<your-username>/my-app.git
  • A subdomain at https://my-app.runtime.di2amp.com
  • An auto-issued TLS cert (cert-manager + Let's Encrypt)

The CLI auto-adds the paas git remote in your local clone.

First push walkthrough

# 1. Add the remote (auto-done by `paas apps create`, but if you cloned later)
git remote add paas https://forgejo.runtime.di2amp.com/<your-username>/my-app.git

# 2. Push
git push paas main

The webhook handler in the control plane:

  1. Verifies HMAC signature from Forgejo (PAAS_WEBHOOK_SECRET shared secret)
  2. Records the delivery in webhook_deliveries (provider, delivery_id) for idempotency — replays return 200 fast without re-triggering a build
  3. Looks up the app by git_url (canonical, with .git suffix, or without — all three match)
  4. Creates a Tekton PipelineRun that runs Paketo buildpacks + pushes to Harbor
  5. On build success, updates the K8s Deployment.spec.template.spec.containers[0].image to the new tag
  6. Argo Rollouts picks up the change and runs the canary (10% → 25% → 50% → 100%)

Watch a deploy live

# CLI logs (combined build + runtime)
paas logs --tail

# K8s pipeline runs
kubectl get pipelinerun -n paas-system -w

# Verify when live
curl -i https://my-app.runtime.di2amp.com/

Configure the default branch

The webhook handler only triggers builds on the configured branch (default main). To switch to production or release:

Via the dashboard: Settings → Git Source → Default branch → save.

Via the API:

curl -X PUT https://runtime.di2amp.com/v1/apps/<app-id>/git \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://forgejo.runtime.di2amp.com/<user>/my-app.git","branch":"production"}'

Pushes to other branches still create preview environments — they don't replace the production deploy.

Troubleshooting

"no app matches repo"

The webhook reaches the control plane but no app's git_url matches the repo URL. Common causes:

  • The app was created under a different Forgejo org/user than your push
  • git_url is unset on the app — verify with paas apps:show my-app
  • Trailing-slash mismatch — fixed in the lookup, but double-check via paas logs

"webhook signature mismatch"

Forgejo and the control plane share PAAS_WEBHOOK_SECRET (mounted from the forgejo-webhook-secret K8s Secret). If you re-deployed Forgejo or rotated the secret, re-register the webhook.

Build fails

Watch the Tekton run:

kubectl logs -n paas-system pipelinerun/<id> -c step-build --follow

Common build failures: missing Procfile, language not auto-detected (no package.json / requirements.txt / go.mod), or Paketo buildpack missing a system package.

Migration drift policy

When the control plane starts, sqlx verifies every applied migration's SHA-384 checksum against the _sqlx_migrations table. Never rewrite a migration file that has already been applied in any environment — doing so causes a checksum mismatch and CrashLoopBackOff at startup.

The correct doctrine:

  1. NEVER edit an applied migration (e.g. 024_alerts.sql) in-place.
  2. ALWAYS create a new corrective migration (029_fix_alerts_constraints.sql) with idempotent guards (IF NOT EXISTS, DO $$ ... IF NOT EXISTS ... END $$).
  3. The corrective migration handles cleanup (e.g. DELETE FROM … WHERE col IS NULL) before adding NOT NULL constraints that would otherwise fail on existing rows.

Example from AA/06 cycle 5:

  • 024_alerts.sql was rewritten post-apply → DB checksum f493f7d8… ≠ file checksum → crash
  • Fix: restore 024_alerts.sql to its 868-byte original + create 029_fix_alerts_constraints.sql
  • Image paas-control-plane:v_aa06_c5 was the first clean startup after the fix