Skip to main content

Generate one file per selection with matrix

· 4 min read
Jorrit Elfferich
Mission Critical Engineer @ Schuberg Philis

Every file a scaffold template declares renders at most once. when: can skip a file, but it can never multiply one. You could work around that by authoring every combination up front and letting when: prune down to what applies — but that only works if every combination is knowable in advance. It breaks down for environments picked interactively from a longer list, or names typed in by hand that no template author could have enumerated ahead of time. Until now, that meant hand-rolling files outside the template, or maintaining a pile of near-duplicate ones inside it.

The Problem

The number of files a project needs often depends on what gets selected when the project is generated. Pick three environments out of five, and you want three stack files, not five. That output was already possible: declare all five stack files in the template up front, and gate each one with its own when: on whether that specific environment got picked. The real cost was authorship — five nearly-identical files, one per environment the template author had to anticipate, kept in sync by hand as the shared parts drifted. Nothing let a template say "generate one of these per selected value" from a single file; only "generate this specific file" or "skip this specific file."

The Fix

Declaring matrix: on a file entry expands it into one generated file per combination of one or more axes. It reuses the same shape Atmos workflow matrix: steps already use, so the syntax should feel familiar. when: still prunes combinations that don't apply — the same conditional engine that already gates whole files, now scoped to a single combination at a time:

spec:
fields:
- name: environments
type: multiselect
options: [dev, staging, production]
files:
- path: stacks/deploy/environment.yaml
target: "stacks/deploy/{{ .matrix.environment }}.yaml"
matrix:
environment: answers.environments

Selecting dev and staging generates exactly stacks/deploy/dev.yaml and stacks/deploy/staging.yaml. Declaring more than one axis expands their full combination, and each resolved combination is available as .matrix.<axis> in Go-template fields such as the output path and generated content, and as matrix.<axis> in when: conditions — so a file can name itself and branch on its own combination.

Computed axes

Real answers aren't always a flat, pre-selected list. Say environments were a structured answer instead of a multiselect — supplied through --set or a preset value — shaped like this:

environments:
dev:
regions:
us-east-1: {}
production:
regions:
us-east-1: {}
us-west-2: {}

The full list of regions actually used isn't something anyone picked directly — it has to be derived from every environment's own regions. The collectKeys function does that: called with one argument, it returns a map's keys; called with a second argument, it collects that key from every value in the map, flattening and deduplicating across all of them.

files:
- path: deploy.yaml
target: "deploy/{{ .matrix.environment }}/{{ .matrix.region }}.yaml"
matrix:
environment: '{{ collectKeys answers.environments }}'
region: '{{ collectKeys answers.environments "regions" }}'
when: "matrix.region in answers.environments[matrix.environment].regions"

environment resolves to dev and production; region resolves to every region used by any of them (us-east-1 and us-west-2). Their combination fans a single deploy.yaml out into deploy/dev/us-east-1.yaml, deploy/production/us-east-1.yaml, and deploy/production/us-west-2.yamltarget: names each one from .matrix.<axis>, and when: prunes the combination down to each environment's actual regions, so dev never gets a us-west-2 file.

How to Use It

atmos scaffold: one file per selection via matrix
 
00:00.0 / 00:00.0

The scaffolding-matrix example is a minimal, runnable template — one multiselect field driving one matrix axis:

cd examples/scaffolding-matrix
atmos scaffold generate example ./my-project

Answering the environments prompt with dev and staging generates stacks/dev.yaml and stacks/staging.yaml from the template's single environment.yaml file — or skip the prompt entirely with --set environments=dev,staging for scripted, non-interactive use. Add matrix: to any spec.files[] entry alongside target: to do the same in your own templates, using a literal list, a multiselect answer, or a computed expression for each axis.

Get Involved

See the atmos scaffold generate docs for the full reference, or open an issue with feedback.