Deployment
CI/CD

CI/CD

lk-wiz uses GitLab CI for building and deploying application artifacts and Atlantis for Terraform plan/apply.

Atlantis (infrastructure)

atlantis.yaml defines one project per stack per stage (e.g. app-dev, frontend-prod, docs-dev). Autoplan is disabled; a push triggers a plan via webhook, and a human applies by commenting atlantis apply -p <stack>-<stage> after review. depends_on enforces the deploy order — for the docs stack, docs-dev depends on frontend-dev, and docs-prod depends on both docs-dev and frontend-prod.

GitLab CI (application)

The pipeline builds container images (MCP, backend) and pushes them to ECR tagged with the commit SHA. After the Tofu apply creates the buckets and distributions, manual deploy jobs publish the static sites:

  • deploy-frontend-<stage> builds the Next.js app and syncs it to the frontend bucket.
  • deploy-docs-<stage> builds this docs site and syncs it to the docs bucket.

Both read their target bucket and CloudFront distribution ID from the Tofu state JSON in S3 (no Tofu binary needed) and then:

# immutable, fingerprinted assets
aws s3 sync out/_next/static/ s3://$BUCKET/_next/static/ \
  --cache-control "public,max-age=31536000,immutable"
# everything else, always revalidated
aws s3 sync out/ s3://$BUCKET/ \
  --cache-control "no-cache,no-store,must-revalidate" \
  --delete --exclude "_next/static/*"
# bust the edge cache
aws cloudfront create-invalidation --distribution-id "$CF_ID" --paths "/*"

Deploy jobs are manual because they need resources that only exist after the matching atlantis apply — running them pre-apply would hit missing buckets / 403s.

Hybrid path

scripts/deploy_hybrid.sh [dev|prod] runs the whole flow locally: push, await the CI build, tofu apply every stack in order (account_prep → data → app → mcp → frontend → docs), then trigger and await the manual deploy jobs (deploy-frontend-*, deploy-docs-*). It bypasses Atlantis for the apply phase — use with care on prod.