Configuration
Configuration lives in yaffle.toml at your repository root.
Minimal example
Section titled “Minimal example”version = 1
[[environments]]name = "production"
[[triggers.github.push]]ref = "refs/heads/main"environment = "production"
[[triggers.github.pull_request]]branch_pattern = "*"
[[workspaces]]path = "infra"environments = ["*"]Full example
Section titled “Full example”version = 1
# Named environments[[environments]]name = "production"
[[environments]]name = "staging"
# Push to main → production[[triggers.github.push]]ref = "refs/heads/main"environment = "production"
# Push to staging → staging[[triggers.github.push]]ref = "refs/heads/staging"environment = "staging"
# All PRs create transient previews[[triggers.github.pull_request]]branch_pattern = "*"
# Shared infra runs in both environments[[workspaces]]path = "infra/shared"environments = ["production", "staging"]variables.cloudflare_zone_id = "abc123"
# Production-only workspace[[workspaces]]path = "infra/production"environments = ["production"]
# App infra runs in all environments (including PR previews)[[workspaces]]path = "apps/api/infra"environments = ["*"]variables.domain = "{{ environment }}.yaffle.dev"
# Publish a curated platform API to other repos in the same Yaffle org[[workspaces]]path = "platform/eks"environments = ["production", "staging"]
outputs.cluster_endpoint = { visibility = "public", consumers = ["applications/apps/*"] }outputs.cluster_ca = { visibility = "public", consumers = ["applications/apps/*"] }
# Require approval for production changes[[approvals]]workspaces = ["infra/production", "infra/shared"]environments = ["production"]approvers = [ "github:user:alice", "github:team:acme/platform",]Reference
Section titled “Reference”version
Section titled “version”Always 1.
version = 1[[environments]]
Section titled “[[environments]]”Named environments your workspaces can target.
[[environments]]name = "production"
[[environments]]name = "staging"PR previews are transient environments—they don’t need to be declared.
[[triggers.github.push]]
Section titled “[[triggers.github.push]]”Run workspaces when a ref (branch or tag) is pushed.
| Field | Description |
|---|---|
ref | Full git ref pattern (must start with refs/heads/ or refs/tags/) |
environment | Which environment to target |
# Trigger on branch push[[triggers.github.push]]ref = "refs/heads/main"environment = "production"
[[triggers.github.push]]ref = "refs/heads/staging/*"environment = "staging"
# Trigger on tag push[[triggers.github.push]]ref = "refs/tags/v*"environment = "release"The ref field supports glob patterns. The * wildcard matches any characters except /.
[[triggers.github.pull_request]]
Section titled “[[triggers.github.pull_request]]”Create transient previews for PRs.
| Field | Description |
|---|---|
branch_pattern | Glob for head branch (* = all PRs) |
[[triggers.github.pull_request]]branch_pattern = "*"[[workspaces]]
Section titled “[[workspaces]]”OpenTofu directories Yaffle manages. (Terraform directories work too—OpenTofu is fully compatible.)
| Field | Description |
|---|---|
path | Path to OpenTofu/Terraform directory |
environments | Which environments run this workspace |
variables | Variables to inject |
outputs | Optional per-output visibility rules for cross-repo module consumers |
[[workspaces]]path = "infra/api"environments = ["*"]variables.environment = "{{ environment }}"variables.region = "us-east-1"Environments
Section titled “Environments”["production"]— Only runs in production["production", "staging"]— Runs in both["*"]— Runs in all environments, including PR previews
Variables
Section titled “Variables”Variables are injected as -var flags. Use {{ environment }} to template the current environment name.
variables.environment = "{{ environment }}"variables.domain = "{{ environment }}.example.com"Outputs
Section titled “Outputs”Outputs are internal by default for same-repo consumers. Use outputs.<name> to make specific outputs public for allowlisted cross-repo consumers.
[[workspaces]]path = "platform/database"environments = ["production", "staging"]
outputs.connection_string = { visibility = "public", consumers = ["applications/apps/*"] }outputs.secret_arn = { visibility = "public", consumers = ["applications/apps/*"] }outputs.<name> fields:
| Field | Description |
|---|---|
visibility | internal or public |
consumers | Required for public; allowlist of <repo>/<workspace-pattern> |
Rules:
internaloutputs are only available to downstream workspaces in the same repopublicoutputs are available only to explicitly allowlisted workspaces in other repos in the same Yaffle org- same-repo consumers can still read the full module surface
- cross-org module sharing is not supported
[[approvals]]
Section titled “[[approvals]]”Require approval before applying.
| Field | Description |
|---|---|
workspaces | Paths or globs |
environments | Which environments |
approvers | Who can approve |
[[approvals]]workspaces = ["infra/*"]environments = ["production"]approvers = [ "github:user:alice", "github:user:bob", "github:team:acme/platform-engineering",]Approver syntax
Section titled “Approver syntax”<provider>:<type>:<identifier>| Example | Meaning |
|---|---|
github:user:alice | GitHub user alice |
github:team:acme/platform | GitHub team @acme/platform |
Matching
Section titled “Matching”Multiple approval blocks can match. Approvers are combined. Any single approver can approve.
Variable templating
Section titled “Variable templating”Use {{ }} in variable values. Yaffle uses MiniJinja syntax.
Available variables
Section titled “Available variables”| Template | Description | Example value |
|---|---|---|
{{ environment }} | Environment name | production, pr-42 |
{{ environment_kind }} | named or transient | transient |
{{ org }} | GitHub organization/owner | acme |
{{ repo }} | Repository name | infra |
{{ workspace_path }} | Workspace path | infra/network |
{{ branch }} | Git branch name | main, feature/login |
{{ commit_sha }} | Full commit SHA | a1b2c3d4... |
{{ pr_number }} | PR number (null for named envs) | 42 |
Examples
Section titled “Examples”[[workspaces]]path = "infra"environments = ["*"]variables.env = "{{ environment }}"variables.bucket = "{{ org }}-{{ repo }}-{{ environment }}"variables.commit = "{{ commit_sha }}"Filters
Section titled “Filters”MiniJinja filters are supported:
variables.branch_slug = "{{ branch | replace('/', '-') }}"variables.env_upper = "{{ environment | upper }}"variables.fallback = "{{ pr_number | default('main') }}"Conditionals
Section titled “Conditionals”variables.domain = "{% if environment_kind == 'transient' %}preview-{{ pr_number }}{% else %}{{ environment }}{% endif %}.example.com"Glob patterns
Section titled “Glob patterns”Globs work in ref, branch_pattern, workspaces, and environments:
| Pattern | Matches |
|---|---|
* | Anything (but not /) |
refs/heads/feature/* | refs/heads/feature/login, but not refs/heads/feature/a/b |
refs/tags/v* | refs/tags/v1, refs/tags/v1.0.0 |
infra/* | infra/foo, infra/foo/bar (workspace patterns match across /) |