Top-level shape
shared
Backends that don’t run inside an engineer’s vCluster. Each in-vCluster service connects out via Tailscale to these endpoints.
The shared block is informational — forge doesn’t push these values into helm values automatically. The component charts are responsible for connecting using the same names (e.g. the in-vCluster
service-config chart has NATS_URL=nats://nats.dev:4222 baked into its env).components
A map: each key is the Helm release name (also used as namespace-DNS-label inside the vCluster, so it must be a valid DNS-1123 label — lowercase, hyphens, no underscores). Each value is a BundleComponent:
BundleComponent fields
Component types
Thetype field is a string for forward-compat, but the platform-test environment understands these values:
Forge uses
type only for default-behavior decisions (today: which DevSpace workflow template to suggest). Adding a new type is a bundle PR, not a forge PR.
Helm value merging
The values forge passes tohelm install for a component are produced by:
1
Start with the component's helm: block
Or
{} if absent.2
Inject image.repository + image.tag
Deep-merge
{ image: { repository: <component.image>, tag: <resolved-tag> } } on top.3
Inject DevSpace pickup annotations (when gradle-local)
Deep-merge a
podAnnotations block:- Plain-object values are merged recursively.
- Arrays are replaced wholesale (no list-merge).
- Primitives are replaced.
Override-File schema
Engineers write an override YAML and pass it togrounds cluster up --bundle X --override ./me.yaml. Schema:
<ComponentOverride> is a discriminated union — exactly one of these shapes:
- image (default)
- gradle-local
- enabled: false
Use the bundle’s image but optionally override the tag.
Pod annotations
The fieldsproject, artifact, reload, and devspace aren’t applied by forge — they’re echoed as Pod annotations for DevSpace to read on the engineer’s laptop. The annotations forge writes:
Resolution algorithm
When forge gets(bundle, override-file):
1
Filter components
Drop everything where
override.enabled === false. Drop optional: true components that aren’t explicitly enabled.2
Resolve per remaining component
- If override is
mode: imagewithtag: replaceimageTagwith the override. - If override is
mode: gradle-local: keep image+tag from bundle; mark mode for annotation injection later. - If no override: use bundle defaults.
3
Compute helm values
Deep-merge per the rules above.
4
Helm install
Best-effort, parallel-friendly. If one component’s chart is missing, the others still go through. Failed components are reported in the response body but don’t fail the call.
Adding a new component
1
Ensure the chart exists
Make sure the component-type’s Helm chart is in
groundsgg/charts and published as an OCI artifact (or co-publish it in the same PR).2
Add a components: entry to bundle.yaml
3
Mark optional if it shouldn't run by default
Add
optional: true and document the opt-in in examples/.4
Open the PR
release-please bumps the bundle version on merge.
Versioning
release-please drives the bundle version:
Engineers pin against bundle versions:
bundle: main always tracks the latest commit on main — useful for daily iteration, not for CI.
See also
- Test Environment overview — engineer onboarding from
grounds loginto a hot-reload loop groundsgg/charts— the Helm charts each component type maps to
