Skip to main content

Step 4 — .gitlab-ci.yml and the first image

The pipeline is a handful of include: lines pulling shared components from the oym-k8s-deploy-templates catalog. You don't write jobs; you list components.

The file

Node / SvelteKit project:

---
# OYM k8s deploy components: https://gitlab.com/onyourmarks/k8s/oym-k8s-deploy-templates

include:
- component: "$OYM_COMPONENT_URL/base-variables@1.1.0"

- component: "$OYM_COMPONENT_URL/stage-test-eslint@1.1.0"
- component: "$OYM_COMPONENT_URL/stage-test-prettier@1.1.0"
- component: "$OYM_COMPONENT_URL/stage-test-stylelint@1.1.0"
- component: "$OYM_COMPONENT_URL/stage-test-vitest@1.1.0"

- component: "$OYM_COMPONENT_URL/stage-build-image@1.1.0"
- component: "$OYM_COMPONENT_URL/stage-tag-environment@1.1.0"

variables:
DEPLOY_ENVIRONMENTS: "staging,production"

PHP project:

---
# OYM k8s deploy components: https://gitlab.com/onyourmarks/k8s/oym-k8s-deploy-templates

include:
- component: "$OYM_COMPONENT_URL/base-variables@1.1.0"

- component: "$OYM_COMPONENT_URL/stage-test-phpstan@1.1.0"
- component: "$OYM_COMPONENT_URL/stage-test-phpunit@1.1.0"
# add eslint / prettier / stylelint if the project has a frontend build

- component: "$OYM_COMPONENT_URL/stage-build-image@1.1.0"
- component: "$OYM_COMPONENT_URL/stage-tag-environment@1.1.0"

variables:
DEPLOY_ENVIRONMENTS: "staging,production"

Drop any stage-test-* line whose tool the project doesn't have (no pnpm test script → no vitest, no phpstan.neon → no phpstan). eslint, prettier and stylelint are non-blocking; vitest, phpunit and phpstan fail the pipeline.

If the project only has one environment, set DEPLOY_ENVIRONMENTS: "production" and, in step 5, list only that environment.

Optional overrides in variables:: NODE_VERSION (default 24), PNPM_VERSION (11), PHP_VERSION (8.5).

What the pipeline does

JobStageRunsResult
test:*testautomaticallyLint / test feedback.
build:imagebuildautomatically, every pushregistry.gitlab.com/onyourmarks/k8s/<slug>/app:<sha> from .deploy/docker/build/app.Dockerfile.
tag:image:staging, tag:image:productiontagmanually, you click itRe-tags that image as <sha>-staging / <sha>-production, notifies Kargo, then waits until ArgoCD reports the deploy healthy and records a GitLab Deployment.
The tag job is the deploy

There is no branch-to-environment rule. Any branch, any commit: once build:image is green you can click tag:image:staging. Clicking tag:image:production on the same pipeline promotes the same image, no rebuild. Before step 6 the tag job will still succeed in re-tagging but then time out waiting for ArgoCD, because the app doesn't exist on the cluster yet. That's expected.

The tag jobs live in a child pipeline (trigger:tag:pipeline). In the GitLab UI open the pipeline, click the downstream pipeline, and the manual buttons are there.

Variables you do NOT set

These are inherited from the onyourmarks/k8s group and must not be redefined in the project:

VariablePurpose
OYM_COMPONENT_URLPoints include: at the catalog.
ARGOCD_KARGO_WEBHOOK_URLThe tag job posts {"warehouse": "<slug>-<env>"} here so Kargo refreshes instantly.
ARGOCD_TOKENLets the tag job poll ArgoCD for the deploy result.

That is also why the project must live in that group: elsewhere the variables don't exist and the pipeline fails on the first include.

Traps

warning
  • CI_PROJECT_NAME must equal the slug. The tag job derives the Kargo warehouse name from it. Renaming the GitLab project silently breaks deploys.
  • Don't copy .gitlab-ci.yml from oym-paddock-fe or team-yellowb-tourshirt-2026. They use vendored local: templates from before Kargo existed.
  • DEPLOY_ENVIRONMENTS must match environments in step 5. A tag job for an environment that isn't defined on the cluster produces an image nobody picks up.
  • Legacy CI leftovers. If the old file had SSH_STAGING, stage-deploy, deploy.php or a .gitlab/ folder of scripts, delete all of it. The new file above is complete on its own.

First pipeline

  1. Commit .deploy/ and .gitlab-ci.yml, push a branch.
  2. Wait for build:image to go green. If it fails, the log shows the Docker build error; fix locally with the test from step 3 and push again.
  3. Open the downstream pipeline and click tag:image:staging. It will re-tag the image and then poll ArgoCD; that poll fails now (no app yet). Fine.
  4. Verify the tag exists: GitLab project → Deploy → Container Registryapp → you see <sha> and <sha>-staging. Kargo picks that tag up once step 6 is done.

Let Claude Code do it

Phase 4 of the project prompt from the prompt generator writes the file and, if you ticked "Replaces a legacy VM deploy", removes the old deploy flow.

Done when

  • .gitlab-ci.yml matches the template above, pinned at @1.1.0
  • build:image is green on your branch
  • tag:image:staging was clicked and the registry shows <sha>-staging