CI/CD Pipeline
Proxima Console uses GitLab CI/CD with a multi-stage pipeline. Different jobs run depending on the trigger: merge request, main branch push, or version tag.
The application tier is deployed via GitOps: CI builds and pushes images, then a manual job
bumps image tags in the separate argocd-infra repository, and ArgoCD reconciles the
proxima-production cluster. CI never connects to the cluster or the production database directly.
See Production (Kubernetes) for the runtime architecture.
Pipeline Stages
lint → test → build → integration → e2e → docker → deploy → release
Pipeline Types
1. MR Pipeline (changes-based)
Triggered on merge requests. Jobs run only when relevant files change.
Stages: lint → test → build
| Job | Stage | What It Does |
|---|---|---|
lint:go | lint | Runs go mod verify, govulncheck, and golangci-lint on agent/, backend/, mcp/, and cli/ |
lint:frontend | lint | Runs ESLint + Prettier checks and npm audit --audit-level=high --omit=dev (fails on high-severity production vulnerabilities) |
lint:swagger | lint | Validates swagger spec well-formedness and freshness (regenerates and diffs) |
test:go:unit | test | Runs gotestsum with -race flag, produces JUnit reports |
test:frontend:unit | test | Runs vitest, produces JUnit reports |
migration:check | test | Validates migrations by running up → down → up |
build:agent | build | Compiles the agent with go build |
2. Main Pipeline (all jobs)
Triggered on pushes to main. Runs all MR jobs plus integration, e2e, Docker, and deploy stages.
Additional stages: integration → e2e → docker → deploy
| Job | Stage | What It Does |
|---|---|---|
test:go:integration | integration | Runs Go integration tests against real PostgreSQL and NATS services |
test:e2e | e2e | Full-stack backend smoke tests against all services |
test:e2e:playwright | e2e | Playwright browser tests — uses pre-built frontend (--mode test) and backend artifacts, resets admin MFA, proxies API calls through vite preview |
docker:backend | docker | Builds backend image, scans with Trivy, pushes registry.prxm.uz/proxima/console/backend:$CI_COMMIT_SHA |
docker:frontend | docker | Builds frontend image, scans with Trivy, pushes registry.prxm.uz/proxima/console/frontend:$CI_COMMIT_SHA |
deploy:k8s-gitops | deploy | Manual gate, main only. Clones argocd-infra over an SSH deploy key, bumps every Console image tag under clusters/production/console-system/ to the new SHA, commits and pushes. ArgoCD then syncs and Argo Rollouts performs the blue-green promotion. |
The remaining
docker:*jobs (docker:mcp,docker:docs,docker:telegram-bot,docker:telegram-miniapp) follow the same pattern, building and pushing oneregistry.prxm.uz/proxima/console/<svc>:$CI_COMMIT_SHAimage per service.
GitOps Deployment (how deploy:k8s-gitops works)
The deploy stage no longer touches a server. It is a pure Git operation against the
argocd-infra repo:
docker:*jobs build and pushregistry.prxm.uz/proxima/console/<svc>:$CI_COMMIT_SHAfor every service.deploy:k8s-gitops(manual,mainonly) clonesargocd-infraover an SSH deploy key (DEPLOY_REPO_SSH_PRIVATE_KEY— an SSH key, not an HTTPS token), rewrites every Console image tag underclusters/production/console-system/to the new SHA, then commits and pushes.- ArgoCD notices the new desired state and syncs the
console-systemnamespace. - Database migrations run inside the cluster as an ArgoCD PreSync-hook Job (
console-migrate,golang-migrate migrate up) — not from CI, because CI cannot reach the cluster-internal database. ArgoCD waits for this hook to succeed before rolling out the backend. - Argo Rollouts performs the backend blue-green promotion (preview → active), with automatic rollback if the preview never becomes healthy.
Retired jobs. The old
deploy:dev(SSH to the box,docker-compose down/up, swappingIMAGE_TAGin systemd units) andmigrate:dev(migrations against the dev-server PostgreSQL over SSH) jobs are gone. Migrations are now the responsibility of the ArgoCD PreSync hook, and rollout is the responsibility of ArgoCD + Argo Rollouts.
3. Tag Pipeline (v*)
Triggered when a version tag (e.g., v0.2.0) is pushed. Builds release artifacts and creates a GitLab release.
Stages: docker → release
| Job | Stage | What It Does |
|---|---|---|
docker:backend | docker | Builds and pushes backend image with version tag |
docker:frontend | docker | Builds and pushes frontend image with version tag |
release:agent-binaries | release | Cross-compiles agent for linux/amd64 and linux/arm64, uploads binaries to Cloudflare R2 |
release:cli-binaries | release | Cross-compiles the pc CLI for linux/amd64, linux/arm64, darwin/amd64, and darwin/arm64, uploads binaries (with .sha256 checksums) to Cloudflare R2 |
release:gitlab | release | Creates a GitLab release with notes extracted from CHANGELOG.md; attaches the agent and pc binaries (R2 download links) as release assets |
Versioning
The project follows semver with a v prefix (e.g., v0.2.0).
- Stable tags (e.g.,
v0.2.0): the Docker image is also tagged as:latest. - Pre-release tags (e.g.,
v0.2.0-beta.1): the Docker image is tagged with the version only,:latestis not updated.
CI Variables
| Variable | Type | Purpose |
|---|---|---|
DEPLOY_REPO_SSH_PRIVATE_KEY | File / Masked | SSH deploy key for pushing image-tag bumps to the argocd-infra repo (not an HTTPS token) |
R2_ACCOUNT_ID | Variable | Cloudflare account ID for R2 storage |
R2_ACCESS_KEY_ID | Variable | R2 access key for uploading agent binaries |
R2_SECRET_ACCESS_KEY | Masked | R2 secret access key |
R2_BUCKET | Variable | R2 bucket name for agent binary storage |
R2_PUBLIC_URL | Variable | Public download URL for agent binaries |
Masked variables are hidden in job logs. All secrets should use the "Masked" type to prevent accidental exposure.