Taskbase — Deployment Guide¶
Overview¶
Taskbase consists of two services deployed to Kubernetes via Flux CD:
- platform-tasks-api — Go REST API (
harbor.prod.skatzi.com/skatzi/platform-tasks-api) - platform-tasks-ui — React SPA served by nginx (
harbor.prod.skatzi.com/skatzi/platform-tasks-ui)
There are two environments: dev (tasks-dev namespace) and prod (tasks-prod namespace).
Branch strategy¶
All development happens on feature branches. A feature branch is not merged to main until it has passed every validation gate. This keeps main always deployable and prevents intermediate or broken states from reaching the version history.
| Branch | Lifecycle |
|---|---|
feat/*, fix/*, docs/* |
Development; CI runs on every push |
main |
Only receives merges that have passed all gates; always reflects prod-ready code |
The full flow is: feature branch → CI → dev deploy → validation gates → merge to main → prod deploy.
Environments¶
| Dev | Prod | |
|---|---|---|
| Namespace | tasks-dev |
tasks-prod |
| Trigger | Automatic when feature branch passes CI | Automatic when feature branch merges to main |
| Purpose | Integration testing, team verification | Live production traffic |
| Rollback | Team alert, manual investigation | Automatic rollback + on-call alert |
Deployment Flow¶
flowchart LR
A([👨💻 Feature branch\npush / PR])
subgraph CI ["⚙️ CI — Gitea Actions"]
direction TB
CI1["go test ./internal/..."]
CI2["go build + vet"]
CI3["Build & tag images\ncommit-SHA"]
CI4["Push to Harbor"]
CI_FAIL["❌ Notify developer"]
CI1 --> CI_OK{Pass?}
CI_OK -- No --> CI_FAIL
CI_OK -- Yes --> CI2 --> CI3 --> CI4
end
subgraph DEV ["🔵 Dev tasks-dev"]
direction TB
D1["Flux reconciles\nnew image tag"]
D2["Rolling update"]
D3{Healthy?}
D5(["✅ Dev ready"])
D6["🔄 Rollback\nAlert team"]
D1 --> D2 --> D3
D3 -- Yes --> D5
D3 -- No --> D6
end
subgraph GATES ["🔒 Production Gates"]
direction TB
G1["🧪 Integration tests\nvs tasks-dev"]
G2["🛡️ Security scan\nHarbor / Trivy"]
G3["👤 Manual approval\nGitea Actions"]
G_BLOCK["❌ Blocked"]
G1 --> G1OK{Pass?}
G1OK -- No --> G_BLOCK
G1OK -- Yes --> G2
G2 --> G2OK{Clean?}
G2OK -- No --> G_BLOCK
G2OK -- Yes --> G3
G3 --> G3OK{Approved?}
G3OK -- No --> G_BLOCK
end
subgraph MERGE ["🟡 Merge"]
direction TB
M1["Merge feature branch\n→ main"]
end
subgraph PROD ["🟢 Prod tasks-prod"]
direction TB
P1["Flux reconciles\nnew image tag"]
P2["Rolling update\nmaxUnavailable: 0"]
P3{Healthy?}
P5(["✅ Production\ndeployed"])
P6["🔄 Auto-rollback\nAlert on-call"]
P1 --> P2 --> P3
P3 -- Yes --> P5
P3 -- No --> P6
end
A --> CI
CI4 --> D1
D5 --> G1
G3OK -- Approved --> M1
M1 --> P1
style CI fill:#FFF9E6,stroke:#E6AC00
style DEV fill:#EBF5FB,stroke:#2E86C1
style GATES fill:#FDEDEC,stroke:#C0392B
style MERGE fill:#F5F0FF,stroke:#7D3C98
style PROD fill:#EAFAF1,stroke:#1E8449
CI Pipeline Detail¶
The CI pipeline runs on every push to any branch via Gitea Actions (.gitea/workflows/ci.yaml).
Steps:
-
Test —
go test ./internal/... -v -count=1All domain packages are tested using in-memory stores (no database required). -
Build —
go build ./cmd/server+go vet ./...Produces theplatform-tasks-apibinary. Fails fast on any vet warning. -
Image build — Docker images are built for both services and tagged with the commit SHA.
-
Push to Harbor — Images are pushed to
harbor.prod.skatzi.com/skatzi/.
If any step fails, the pipeline stops and the developer is notified. No deployment occurs.
Dev Deployment (Automatic on CI Pass)¶
When CI passes on a feature branch, Flux CD automatically reconciles the new image tags in the tasks-dev namespace. This gives the team a live environment to validate against before any code lands on main.
What happens:
- Flux detects the new image tag in Harbor and updates the
tasks-devKustomization - A rolling update is performed — old pods are not terminated until new pods pass their readiness probe
- The health endpoint
GET /api/healthis polled until it returns{"status":"ok"} - If the rollout does not complete within the timeout, Flux rolls back and alerts the team
Production Gates¶
All three gates must pass before the feature branch is allowed to merge to main. This is enforced via branch protection rules on the main branch.
Gate 1 — Integration Tests¶
A dedicated test suite runs against the live tasks-dev environment, exercising:
- Project and task CRUD via the REST API
- Task group assignment and movement between groups
- Authentication via Keycloak JWT
Gate 2 — Security Scan¶
The images tagged for promotion are scanned for known CVEs using Harbor's built-in Trivy scanner.
- Critical or High severity CVEs → gate fails, vulnerability report attached
- Medium / Low → reported but do not block
Gate 3 — Manual Approval¶
A team member reviews the integration test results, the security scan report, and the diff. Approval is granted via the Gitea Actions approval step; the approver and timestamp are recorded in the deployment audit log.
Merge to Main → Prod Deploy¶
Once all three gates pass, the feature branch is merged to main. The merge itself triggers the prod deployment:
- Flux detects the updated
mainimage tag and reconcilestasks-prod - Rolling update with
maxUnavailable: 0— zero downtime - PostgreSQL schema migrations run automatically via
golang-migrateat startup - If health checks fail within the deadline, Flux automatically rolls back and pages the on-call engineer
Main always reflects what is running in production.
Rollback¶
| Scenario | Action |
|---|---|
| Dev deployment unhealthy | Flux rolls back dev; team alerted |
| Prod deployment unhealthy | Flux rolls back prod automatically; on-call paged |
| Rollback to specific version | Update the image tag in components/tasks-prod/ and merge via PR |
| Emergency rollback | Manually patch the Kustomization image tag in-cluster |
Related¶
- Taskbase component overview
- API architecture
- ADR 0003 — Task Management App Architecture
- Kubernetes manifests:
components/tasks/in this repository