Skip to content

Commit 83ec14b

Browse files
flacatusclaude
andauthored
Add kargo skills to onboard new components and documentation (#463)
* feat(kargo): add onboarding skill and reorganize promotion tasks Move promotion tasks into kargo component directory, add kargo-onboard skill for scaffolding new components into the Kargo promotion pipeline, and add architecture documentation for kargo-infra-common. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(kargo): replace inline YAML examples with file links in onboarding guide Addresses PR review feedback to link to actual source files instead of duplicating YAML that can go stale when the referenced files change. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 32b1f28 commit 83ec14b

10 files changed

Lines changed: 537 additions & 17 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,5 @@ kustomize build <path> | kubectl apply --dry-run=client -f -
7676
- When a CI check fails on a PR, read `skills/ci-troubleshooting.md`
7777
- When working interactively on new features or significant changes,
7878
read `skills/brainstorming-workflow.md` before making changes
79+
- When onboarding a new component to Kargo automated promotion,
80+
read `skills/kargo-onboard.md`
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
# Onboarding a Component to Kargo Promotion
2+
3+
This guide explains how to add a new component to the `kargo-infra-common`
4+
automated promotion pipeline. Kargo watches container registries and Helm chart
5+
repositories for new versions, then opens PRs to promote those versions through
6+
staging and production.
7+
8+
## How Promotion Works
9+
10+
```
11+
Warehouse Stage: staging Stage: production
12+
(watches registries) ──freight──> (auto-promotes) ──soak──> (manual approval)
13+
│ │
14+
PromotionTask PromotionTask
15+
updates staging updates production
16+
config files config files
17+
│ │
18+
Opens PR to main Opens PR to main
19+
Waits for merge Waits for merge
20+
```
21+
22+
**Key concepts:**
23+
24+
- **Warehouse** — watches image registries / Helm repos and creates "freight"
25+
(a versioned bundle of artifacts) when new versions appear.
26+
- **Stage** — represents an environment (staging, production). Each stage
27+
consumes freight and runs a promotion template.
28+
- **PromotionTask** — the reusable logic that updates files in this GitOps
29+
repo. Each component has its own task.
30+
- **Freight origin guard** — each PromotionTask uses
31+
`if: ${{ ctx.targetFreight.origin.name == "<warehouse>" }}` so it only
32+
executes when its own warehouse produces new freight.
33+
34+
## Directory Layout
35+
36+
```
37+
kargo-infra-common/
38+
├── base/ # shared project resources
39+
│ ├── namespace.yaml # namespace with kargo.akuity.io/project label
40+
│ ├── project.yaml # Kargo Project
41+
│ ├── project-config.yaml # promotion policies (auto/manual per stage)
42+
│ ├── rbac/ # RBAC for the konflux-devprod group
43+
│ ├── external-secrets/ # git credentials from Vault
44+
│ ├── stage-ring-1-staging.yaml # staging stage (calls all component tasks)
45+
│ └── stage-ring-2-production.yaml # production stage (calls component tasks)
46+
47+
├── kargo/ # kargo component
48+
│ ├── kustomization.yaml
49+
│ ├── warehouse.yaml # watches kargo + dex images, kargo helm chart
50+
│ └── promotiontasks/
51+
│ ├── kargo-promote-ring-1.yaml # updates staging config
52+
│ └── kargo-promote-ring-2.yaml # updates production config
53+
54+
├── <your-component>/ # add your component here
55+
│ ├── kustomization.yaml
56+
│ ├── warehouse.yaml
57+
│ └── promotiontasks/
58+
│ └── <component>-promote-ring-1.yaml # at minimum, staging
59+
60+
└── kustomization.yaml # references: base, kargo, <your-component>
61+
```
62+
63+
Each component owns its **warehouse** (what to watch) and **promotion tasks**
64+
(what to update). The **stages** in `base/` are shared — they call every
65+
component's promotion task in sequence within a single promotion, which
66+
preserves soak time gating between staging and production.
67+
68+
## Step-by-Step: Onboard a New Component
69+
70+
### 1. Create Your Component Directory
71+
72+
```
73+
kargo-infra-common/<component>/
74+
├── kustomization.yaml
75+
├── warehouse.yaml
76+
└── promotiontasks/
77+
├── kustomization.yaml
78+
└── <component>-promote-ring-1.yaml
79+
```
80+
81+
### 2. Define Your Warehouse
82+
83+
The warehouse tells Kargo what artifacts to watch. It polls every 5 minutes
84+
and creates freight when new versions appear.
85+
86+
```yaml
87+
# <component>/warehouse.yaml
88+
---
89+
apiVersion: kargo.akuity.io/v1alpha1
90+
kind: Warehouse
91+
metadata:
92+
name: <component>
93+
spec:
94+
freightCreationPolicy: Automatic
95+
interval: 5m0s
96+
subscriptions:
97+
# Helm chart (if your component deploys via Helm)
98+
- chart:
99+
repoURL: https://charts.example.com
100+
name: <chart-name>
101+
semverConstraint: ^1.0.0
102+
discoveryLimit: 5
103+
# Container images
104+
- image:
105+
repoURL: quay.io/konflux-ci/<image>
106+
imageSelectionStrategy: NewestBuild
107+
discoveryLimit: 5
108+
allowTags: ^[0-9a-f]{40}$ # SHA tags
109+
# Add more images as needed
110+
```
111+
112+
**`allowTags`** — regex filter for image tags. Common patterns:
113+
- `^[0-9a-f]{40}$` — full 40-char git SHA
114+
- `^1\.10-[0-9a-f]{7}$` — semver prefix + short SHA
115+
- `^v[0-9]+\.[0-9]+\.[0-9]+$` — semver tags
116+
117+
**`imageSelectionStrategy`** — use `NewestBuild` for SHA-tagged images,
118+
`SemVer` for semver-tagged images.
119+
120+
### 3. Create Your Promotion Task
121+
122+
The promotion task defines which files to update and how. It uses
123+
`yaml-update` to patch specific keys in your component's config files.
124+
125+
```yaml
126+
# <component>/promotiontasks/<component>-promote-ring-1.yaml
127+
---
128+
apiVersion: kargo.akuity.io/v1alpha1
129+
kind: PromotionTask
130+
metadata:
131+
name: <component>-promote-ring-1
132+
spec:
133+
vars:
134+
- name: srcPath
135+
steps:
136+
- uses: yaml-update
137+
as: update-<component>
138+
if: ${{ ctx.targetFreight.origin.name == "<component>" }}
139+
config:
140+
path: ${{ vars.srcPath }}/components/<component>/internal-staging/<target-file>
141+
updates:
142+
- key: <yaml.key.to.update>
143+
value: ${{ imageFrom("quay.io/konflux-ci/<image>").Tag }}
144+
```
145+
146+
**What goes in `updates`** depends on how your component is deployed:
147+
148+
| Deployment Method | Target File | Key to Update |
149+
|---|---|---|
150+
| HelmChartInflationGenerator | `kargo-helm-generator.yaml` | `valuesInline.image.tag` |
151+
| Kustomize `helmCharts:` | `helm-values.yaml` | `<image-key>.tag` |
152+
| Kustomize `images:` | `kustomization.yaml` | `images.0.newTag` |
153+
| Helm chart version | generator or kustomization | `version` (use `chartFrom()`) |
154+
| Git resource ref | `kustomization.yaml` | `resources.<index>` |
155+
156+
**Expression functions:**
157+
- `imageFrom("quay.io/repo").Tag` — resolves image tag from freight
158+
- `chartFrom("oci://registry/chart").Version` — resolves chart version from freight
159+
160+
**If your component also deploys to production**, create `<component>-promote-ring-2.yaml`
161+
with the same structure but pointing to the production config path
162+
(`internal-production/` instead of `internal-staging/`).
163+
164+
### 4. Wire Up the Kustomizations
165+
166+
```yaml
167+
# <component>/kustomization.yaml
168+
---
169+
apiVersion: kustomize.config.k8s.io/v1beta1
170+
kind: Kustomization
171+
resources:
172+
- warehouse.yaml
173+
- promotiontasks
174+
```
175+
176+
```yaml
177+
# <component>/promotiontasks/kustomization.yaml
178+
---
179+
apiVersion: kustomize.config.k8s.io/v1beta1
180+
kind: Kustomization
181+
resources:
182+
- <component>-promote-ring-1.yaml
183+
# - <component>-promote-ring-2.yaml # if you have production
184+
```
185+
186+
### 5. Register Your Component in the Top-Level Kustomization
187+
188+
```yaml
189+
# kustomization.yaml
190+
---
191+
apiVersion: kustomize.config.k8s.io/v1beta1
192+
kind: Kustomization
193+
namespace: kargo-infra-common
194+
resources:
195+
- base
196+
- kargo
197+
- <component> # <-- add this line
198+
commonAnnotations:
199+
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
200+
```
201+
202+
### 6. Add Your Warehouse as a Freight Origin in the Stage
203+
204+
Edit `base/stage-ring-1-staging.yaml` — add your warehouse under
205+
`requestedFreight`:
206+
207+
```yaml
208+
spec:
209+
requestedFreight:
210+
- origin:
211+
kind: Warehouse
212+
name: kargo
213+
sources:
214+
direct: true
215+
- origin: # <-- add this block
216+
kind: Warehouse
217+
name: <component>
218+
sources:
219+
direct: true
220+
```
221+
222+
Then add your promotion task call in the `steps` section, after the existing
223+
task calls:
224+
225+
```yaml
226+
steps:
227+
- uses: git-clone
228+
...
229+
# --- kargo ---
230+
- task:
231+
name: kargo-promote-ring-1
232+
as: kargo-promote
233+
vars:
234+
- name: srcPath
235+
value: ./src
236+
# --- <component> --- # <-- add this block
237+
- task:
238+
name: <component>-promote-ring-1
239+
as: <component>-promote
240+
vars:
241+
- name: srcPath
242+
value: ./src
243+
- uses: git-commit
244+
...
245+
```
246+
247+
If your component also promotes to production, make the same changes in
248+
`base/stage-ring-2-production.yaml`.
249+
250+
### 7. Update the Project Config (if needed)
251+
252+
The project config in `base/project-config.yaml` controls auto-promotion.
253+
The current stages already have policies defined — you only need to edit this
254+
if you add new stages:
255+
256+
```yaml
257+
spec:
258+
promotionPolicies:
259+
- stage: ring-1-staging
260+
autoPromotionEnabled: true # staging auto-promotes
261+
- stage: ring-2-production
262+
autoPromotionEnabled: false # production requires manual approval
263+
```
264+
265+
### 8. Validate
266+
267+
```sh
268+
# build the kargo-infra-common project
269+
kustomize build components/kargo/internal-production/projects/kargo-infra-common/
270+
271+
# build the full overlays
272+
kustomize build --enable-helm components/kargo/internal-staging/
273+
kustomize build --enable-helm components/kargo/internal-production/
274+
275+
# apply to a local kind cluster for testing (if available)
276+
kustomize build components/kargo/internal-production/projects/kargo-infra-common/ \
277+
| kubectl apply -f -
278+
```
279+
280+
## Environments
281+
282+
This repo supports four environments across two cluster types:
283+
284+
| Environment | Cluster Type | Kargo Stage | ArgoCD Source Path |
285+
|---|---|---|---|
286+
| `internal-staging` | Internal | `ring-1-staging` | `components/<name>/internal-staging` |
287+
| `internal-production` | Internal | `ring-2-production` | `components/<name>/internal-production` |
288+
| `external-staging` | External | `ring-1-staging` | `components/<name>/external-staging` |
289+
| `external-production` | External | `ring-2-production` | `components/<name>/external-production` |
290+
291+
ArgoCD ApplicationSets use `{{values.environment}}` which is patched per
292+
overlay to the correct environment name. Your component needs a directory
293+
matching each environment it deploys to (e.g., `internal-staging/`,
294+
`internal-production/`).
295+
296+
If your component deploys to **external** clusters, your promotion task must
297+
also update the external config files.
298+
299+
## Example: Kargo Component (Reference)
300+
301+
The [`kargo/`](../kargo/) directory is the reference implementation:
302+
303+
- **Warehouse** — watches the Kargo Helm chart (OCI), Kargo image, and Dex
304+
image: [`kargo/warehouse.yaml`](../kargo/warehouse.yaml)
305+
- **Promotion Task (staging)** — updates the Helm generator with chart version
306+
and image tags: [`kargo/promotiontasks/kargo-promote-ring-1.yaml`](../kargo/promotiontasks/kargo-promote-ring-1.yaml)
307+
- **Promotion Task (production)** — same pattern targeting production config:
308+
[`kargo/promotiontasks/kargo-promote-ring-2.yaml`](../kargo/promotiontasks/kargo-promote-ring-2.yaml)
309+
310+
## Checklist
311+
312+
Before opening your PR:
313+
314+
- [ ] Warehouse created with correct image repos, tag filters, and chart refs
315+
- [ ] PromotionTask created with `if` guard on `ctx.targetFreight.origin.name`
316+
- [ ] PromotionTask `path` points to the correct target file in your component
317+
- [ ] Kustomizations wired up (`<component>/kustomization.yaml` and `promotiontasks/kustomization.yaml`)
318+
- [ ] Top-level `kustomization.yaml` includes your component
319+
- [ ] Stage YAML updated with your freight origin and task call
320+
- [ ] `kustomize build` passes for all overlays
321+
- [ ] Tested on a local kind cluster (if available)

components/kargo/internal-production/projects/kargo-infra-common/base/kustomization.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@ resources:
77
- project-config.yaml
88
- rbac
99
- external-secrets
10-
- promotiontasks
1110
- stage-ring-1-staging.yaml
1211
- stage-ring-2-production.yaml

components/kargo/internal-production/projects/kargo-infra-common/base/stage-ring-1-staging.yaml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,11 @@ spec:
2828
- branch: main
2929
path: ./src
3030
- task:
31-
name: ring-1-promote
32-
as: promote
31+
name: kargo-promote-ring-1
32+
as: kargo-promote
3333
vars:
3434
- name: srcPath
3535
value: ./src
36-
- name: component
37-
value: ${{ vars.component }}
3836
- uses: git-commit
3937
as: commit
4038
config:

components/kargo/internal-production/projects/kargo-infra-common/base/stage-ring-2-production.yaml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,11 @@ spec:
2929
- branch: main
3030
path: ./src
3131
- task:
32-
name: ring-2-promote
33-
as: promote
32+
name: kargo-promote-ring-2
33+
as: kargo-promote
3434
vars:
3535
- name: srcPath
3636
value: ./src
37-
- name: component
38-
value: ${{ vars.component }}
3937
- uses: git-commit
4038
as: commit
4139
config:

components/kargo/internal-production/projects/kargo-infra-common/kargo/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1
33
kind: Kustomization
44
resources:
55
- warehouse.yaml
6+
- promotiontasks

components/kargo/internal-production/projects/kargo-infra-common/base/promotiontasks/ring-1-promote.yaml renamed to components/kargo/internal-production/projects/kargo-infra-common/kargo/promotiontasks/kargo-promote-ring-1.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
apiVersion: kargo.akuity.io/v1alpha1
33
kind: PromotionTask
44
metadata:
5-
name: ring-1-promote
5+
name: kargo-promote-ring-1
66
spec:
77
vars:
88
- name: srcPath
9-
- name: component
109
steps:
1110
- uses: yaml-update
1211
as: update-kargo
13-
if: ${{ vars.component == "kargo" }}
12+
if: ${{ ctx.targetFreight.origin.name == "kargo" }}
1413
config:
1514
path: ${{ vars.srcPath }}/components/kargo/internal-staging/deployment/kargo-helm-generator.yaml
1615
updates:

0 commit comments

Comments
 (0)