Skip to content

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:

  1. Testgo test ./internal/... -v -count=1 All domain packages are tested using in-memory stores (no database required).

  2. Buildgo build ./cmd/server + go vet ./... Produces the platform-tasks-api binary. Fails fast on any vet warning.

  3. Image build — Docker images are built for both services and tagged with the commit SHA.

  4. 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-dev Kustomization
  • A rolling update is performed — old pods are not terminated until new pods pass their readiness probe
  • The health endpoint GET /api/health is 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 main image tag and reconciles tasks-prod
  • Rolling update with maxUnavailable: 0 — zero downtime
  • PostgreSQL schema migrations run automatically via golang-migrate at 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