03 / Develop · Shell · Ship

Ship a change ​

Getting a merged change onto dev, then promoting it to UAT and prod. The Shell runs its own pipeline — .github/workflows/ci-cd.yml in veni-shell — on top of the platform's GitOps model. Where this page and the platform page differ, the workflow file is what runs.

The one thing to know first: merging to develop builds but does not deploy. Deploying to dev is a deliberate second step.

What each push does ​

You pushCI doesDeploys?
developQuality gate, build + push dev-<sha>, Trivy scanNo
release/devRewrites newTag in the dev overlay, commits it backDev, via ArgoCD
v*.*.*-rc.* tagRetags dev-<sha> → rc tagUAT, if gated on
v*.*.* tagRetags dev-<sha> → release tagProd, if gated on
(manual) workflow_dispatchDeploys an existing tagDev

The quality gate runs typecheck, lint and test for both api and ui, and all of them block. Build and deploy jobs depend on it, so a failure stops everything downstream.

Deploy to dev ​

  1. Merge your PR into develop. CI builds and pushes registry.venizia.ai/shell-api:dev-<sha> and shell-ui:dev-<sha>, then scans them. No cluster is touched.

  2. Release it, either way:

    bash
    git push origin develop:release/dev   # CI bumps the overlay from origin/develop's sha

    Or Actions → CI/CD → Run workflow, optionally pinning a tag like dev-abc1234 (empty = latest develop build).

CI rewrites newTag in deploy/k8s/overlays/dev/kustomization.yaml and commits it to release/dev as chore(k8s/dev): shell → dev-<sha> [skip ci]. ArgoCD app shell-dev (auto-sync, tracks release/dev) reconciles it.

CI never runs kubectl against dev by design — an imperative change would be reverted by Argo self-heal.

Migrations ride along: deploy/k8s/base/api/migrate-job.yaml is an ArgoCD PreSync hook, so it completes before the new pods roll, and a failing migration fails the sync.

Lands on veni.dev.venizia.ai (API: shell-api.dev.venizia.ai).

Promote to UAT and prod ​

Both are promote, not rebuild — the exact dev-<sha> bytes validated in dev get retagged, so what ships is what was tested.

bash
git tag v1.2.0-rc.1 <sha> && git push origin v1.2.0-rc.1   # → UAT
git tag v1.2.0 <sha>      && git push origin v1.2.0        # → prod

The tagged commit must already have been built on develop — its dev-<sha> image has to exist, or the promote job fails with an explicit error.

Both deploys are gated: the repo variable DEPLOY_UAT_ENABLED / DEPLOY_PROD_ENABLED must be "true", and the KUBECONFIG_NONPROD / KUBECONFIG_PROD secret must exist. Otherwise the job is inert. Unlike dev, these use kubectl set image directly, wait for the rollout, then smoke-test the homepage.

UAT: shell.uat.venizia.ai · Prod: app.veni.ai

Roll back dev ​

Re-run the deploy with a known-good tag — Actions → CI/CD → Run workflow, tag dev-<older-sha> — or edit newTag in the dev overlay on release/dev and push.

A rollback does not undo a migration; the PreSync hook only rolls forward. If the bad change carried a migration, write a new one that reverses it.

Change an environment variable ​

Three places, and missing one is the usual cause of "works locally, broken in dev":

WhereFile
Local stackdocker-compose.yml
Documented defaultapi/.env.example (Configure)
Clusterdeploy/k8s/overlays/<env>/api-env-patch.yaml (or ui-env-patch.yaml)

Secrets never go in git. They come from Vault through the External Secrets Operator — deploy/k8s/overlays/<env>/external-secrets.yaml. Dev extracts secret/veni/dev/shell/api via the shared vault-backend ClusterSecretStore; add the key in Vault and it syncs on the refresh interval. Don't reference a per-app vault-shell store — it isn't provisioned, and doing so yields SecretSyncedError.

Ship a docs change ​

These pages live in VENIZIA-AI/veni-docs, not in veni-shell. Shell pages are develop/shell/*.md.

PR into dev. The validate workflow runs npm run validate and npm run build on every PR — a dead page link fails it. On merge, it builds and pushes veni-docs-ui:latest, which the dev overlay pins, and the site picks it up on the next reconcile.

What goes where: the veni-shell README is orientation plus the commands that live in that repo; anything conceptual or reference-level belongs here. See Documenting a project in the docs repo.

Common snags ​

SymptomFix
Merged to develop, nothing deployedWorking as designed — develop only builds. Push release/dev or run the workflow manually.
Promote job: "Source image … not found"The tagged commit was never built on develop. Tag a commit that has a dev-<sha> image.
Tag pushed, UAT/prod didn't moveThe deploy is gated — check repo vars DEPLOY_UAT_ENABLED / DEPLOY_PROD_ENABLED and that the KUBECONFIG_* secret exists.
Argo sync stuckUsually the PreSync migrate Job failing — read that Job's logs first.
SecretSyncedError on an ExternalSecretThe Vault key is missing, or the store reference is wrong (see above).
A new image didn't roll outNothing auto-detects a tag — the overlay's newTag must change and be committed.
Rolled back, still brokenA rollback doesn't revert a migration. Write a forward migration that undoes it.

Check what's deployed ​

newTag in deploy/k8s/overlays/<env>/kustomization.yaml on the release/dev branch is what dev is meant to be running. The tag is dev-<sha>, where the sha is the develop commit it was built from.

See also ​

  • Run — run it locally before you ship it.
  • Deployment — the platform-wide GitOps model, environments and promotion.