Publish¶
publish: declares what StageFreight does with a build — push image tags, sync project
identity, publish a GitLab component, cut a forge release, publish an archive or package, or
deploy a static site. It's an id → target map; each entry is a discriminated union keyed
by kind — the kind decides which other keys are valid.
Every target shares a when: routing block (all non-empty conditions must match — AND
logic; see Patterns & conditions). The map key is the
target's id.
Registry — push image tags¶
Pushes a build's image to a container registry under one or more tags:.
publish:
dockerhub-stable:
kind: registry
build: myapp # references a builds[] id
registry: dockerhub # references a registries[] id — url/provider/creds/path resolve from it
tags: ["{version}", "{major}.{minor}", "latest"]
when:
git_tags: [stable] # a named pattern from git.tags:
events: [tag]
retention: { keep_last: 10, keep_monthly: 6 }
registry: references a registries[] id (see Identity & Connectivity); pass a
list (registry: [dockerhub, ghcr, harbor]) to fan the target across several registries at
once. tags template expansion and retention are cross-cutting — see Concepts.
Project Metadata — sync identity to registries & forge repos¶
kind: metadata pushes a project's identity — description, long readme/overview, website,
topics, logo — to every registry and forge repo destination that can hold it, from one
source of truth. Each field maps to what a destination supports and is silently skipped where
absent; nothing is truncated, and org/namespace fields are never touched.
publish:
project-meta:
kind: metadata
registry: [dockerhub, harbor] # registry pages
repos: [github-mirror, primary] # forge project pages
description: # length-tiered variants — the engine fits each destination's cap
- "The full, richest tagline your most generous destination can hold…"
- "A trimmer version for tighter caps…"
- "A punchy fallback that fits anywhere"
readme: README.md # long markdown body (registries with an overview field)
website: https://example.com # external site URL (forges that have the field)
topics: [ci-cd, gitops, kubernetes] # discovery tags (forges) — normalized to lowercase-hyphenated
logo: assets/logo.png # project avatar (project-scoped forge avatars)
when: { branches: [main], events: [push, tag] }
What each destination supports¶
| destination | description (short) | readme (long body) | website | topics | logo |
|---|---|---|---|---|---|
| Docker Hub | ✓ ~100 (word-truncated) | ✓ Overview (~25k) | ✕ | ✕ | ✕ (org-scoped) |
| Harbor | — (single field) | ✓ Info (markdown) | ✕ | ✕ | ✕ |
| Quay | — (single field) | ✓ Description (markdown) | ✕ | ✕ | ✕ |
| JFrog | ✓ config description | ✕ (no README API) | ✕ | ✕ | ✕ |
| GHCR / GitLab CR | ✕ (no description API) | ✕ | ✕ | ✕ | ✕ |
| GitHub | ✓ ~350 | ✕ (README is committed) | ✓ | ✓ | ✕ (org-scoped) |
| GitLab | ✓ | ✕ (committed) | ✕ (no field) | ✓ | ✓ project avatar |
| Gitea / Forgejo | ✓ | ✕ (committed) | ✓ | ✓ | ✓ project avatar |
Behavior: single-field registries (Harbor, Quay) take the readme in their one markdown
field (falling back to the short description if no readme); the engine fits the longest
description variant to each destination's cap and warns rather than truncating; logo
syncs idempotently (re-uploads only on change) and is skipped where the avatar is
org-scoped. Each run reports per-destination what was set / skipped / warned.
Release — cut forge releases¶
Creates a release on the detected forge, with rolling git-tag aliases that track it.
publish:
primary-release:
kind: release
aliases: ["{version}", "{major}.{minor}", "latest"]
retention: { keep_last: 10, keep_monthly: 6 }
when: { git_tags: [stable], events: [tag] }
The aliases are rolling git tags resolved with the same template
variables as everything else — {version} → 1.2.3,
{major}.{minor} → 1.2, latest → always the newest.
Cutting the tag & its release notes¶
Releases are cut with stagefreight tag (never raw git tag — that is hook-blocked):
- The
-mmessage is the release's Notable Features section — each bulleted line becomes one item in that list. Write the highlights you want to surface; StageFreight renders them under the heading and assembles the rest of the notes (Image Availability, Downloads + checksums, Security summary, SBOM/signatures) automatically. - Omit
-mand the glossary pipeline generates the highlights from the conventional-commit history since the previous release. Interactive (TTY) runs prompt for the message; non-interactive runs require-mor a non-empty glossary result. --target <ref>tags a specific commit without checking it out (e.g. releaseorigin/mainwhile on another branch); a[skip ci]tip is skipped and its releasable parent is tagged instead.--dry-runpreviews the full plan and the rendered highlights;--pushcreates and pushes the tag, which triggers the release pipeline. Without--pushthe tag stays local.
Bullets with backticks or apostrophes
Passing them straight through a shell lets it interpret them. Keep the notes in a file and pass -m "$(cat NOTES.md)" — command-substitution output is not re-parsed.
CLI
Release authoring (release create, release notes, release prune, release badge)
and its flags live in the CLI Reference. In CI these run as part of
the publish phase; you rarely invoke them by hand.
Other target kinds¶
gitlab-component (GitLab CI/CD component publish), archive / package
distribution (kind: generic-package), and pages (static-site
deploy, e.g. these docs to Cloudflare Pages) are declared the same way — a kind, a build:
or source, and a when:. Their full field sets are in the generated reference below.
Reference¶
The blocks below are generated from the config source — for each kind, exactly the fields it accepts, with each field's meaning, allowed values, and whether it's required.
publish¶
Targets defines distribution targets and side-effects. Declared under the publish: key as an id→target map (execution order preserved).
kind: registry¶
publish:
<id>: # entry key = the unique id
kind: registry # Kind is the target type. Determines which fields are valid. · required
registry: [<string>] # Registry references registries[].id for registry/metadata targets. Accepts a single id (registry…
build: <string> # Build references a BuildConfig.ID. Required for kind: registry.
tags: [<string>] # Tags are tag templates resolved against version info (kind: registry). e.g., ["{version}"…
signing_profile: <string> # SigningProfile references a signing.profiles[].id — the trust profile this target signs under.…
native_scan: false # NativeScan enables post-push vulnerability scanning via the registry's own built-in scanner.…
retention: # Retention controls cleanup of old tags/releases. Structured only in v2 (no scalar shorthand).
keep_last: <int> # keep the N most recent tags per series (0/-1/unset = ∞) · required
keep_daily: <int> # keep one per day for the last N days · required
keep_weekly: <int> # keep one per week for the last N weeks · required
keep_monthly: <int> # keep one per month for the last N months · required
keep_yearly: <int> # keep one per year for the last N years · required
keep_branches: <int> # keep the N most-recent identity groups per template (bounds retired branches; 0/unset = ∞) · required
max_age: <string> # keep items newer than this ("14d", "72h"); additive like keep_* — alone it means "only the recent… · required
identity: [<string>] # extra identity vars beyond the {branch}/{env} defaults — partition tags into independent series · required
protect: [<string>] # tag patterns that are never deleted, an explicit override · required
refs: # scoped overrides: a different policy for items matching a pattern (first match wins; unset fields… · required
- match: <string> # pattern (same syntax as protect/templates) · required
keep_last: <int> # required
keep_daily: <int> # required
keep_weekly: <int> # required
keep_monthly: <int> # required
keep_yearly: <int> # required
max_age: <string> # required
when: # When specifies routing conditions for this target: a single condition-set, or a list of them (OR…
- branches: [<string>] # Branches lists branch filters. Each entry is a policy name or "re:<regex>". Empty = no branch…
git_tags: [<string>] # GitTags lists git tag filters. Each entry is a policy name or "re:<regex>". Empty = no tag…
events: [<string>] # Events lists CI event type filters. Supported: push, tag, release, schedule, manual, pull_request…
forges: [<string>] # Forges restricts this target to specific CI forges by provider name (github, gitlab, gitea…
outcomes: [<string>] # Outcomes gates on the RUN outcome (success | failure | warning) — the notification dimension of…
signals: [<string>] # Signals gates on things the run FOUND, independent of its outcome — a run that succeeded can…
schedules: [<string>] # Schedules restricts this condition to runs fired by one of these schedules (ids from schedules:).…
kind: metadata¶
publish:
<id>: # entry key = the unique id
kind: metadata # Kind is the target type. Determines which fields are valid. · required
registry: [<string>] # Registry references registries[].id for registry/metadata targets. Accepts a single id (registry…
repos: [<string>] # Repos names the destination repos[].id for a kind: release target. The repo with role primary is…
description: [<string>] # Description is the short project description/tagline. A scalar for kind: registry; kind: metadata…
readme: <string> # Readme is the long markdown 'project page' body (registries only: Docker Hub Overview, Harbor Info…
website: <string> # Website is the project's external site URL (forges: GitHub, Gitea/Forgejo).
topics: [<string>] # Topics are discovery tags (forges: GitHub, GitLab, Gitea). Authored freely; the engine normalizes…
logo: <string> # Logo is a path to the project avatar image (project-scoped forges: GitLab, Gitea/Forgejo); synced…
when: # When specifies routing conditions for this target: a single condition-set, or a list of them (OR…
- branches: [<string>] # Branches lists branch filters. Each entry is a policy name or "re:<regex>". Empty = no branch…
git_tags: [<string>] # GitTags lists git tag filters. Each entry is a policy name or "re:<regex>". Empty = no tag…
events: [<string>] # Events lists CI event type filters. Supported: push, tag, release, schedule, manual, pull_request…
forges: [<string>] # Forges restricts this target to specific CI forges by provider name (github, gitlab, gitea…
outcomes: [<string>] # Outcomes gates on the RUN outcome (success | failure | warning) — the notification dimension of…
signals: [<string>] # Signals gates on things the run FOUND, independent of its outcome — a run that succeeded can…
schedules: [<string>] # Schedules restricts this condition to runs fired by one of these schedules (ids from schedules:).…
kind: gitlab-component¶
publish:
<id>: # entry key = the unique id
kind: gitlab-component # Kind is the target type. Determines which fields are valid. · required
spec_files: [<string>] # SpecFiles lists component spec file paths (kind: gitlab-component).
catalog: false # Catalog enables GitLab Catalog registration (kind: gitlab-component).
when: # When specifies routing conditions for this target: a single condition-set, or a list of them (OR…
- branches: [<string>] # Branches lists branch filters. Each entry is a policy name or "re:<regex>". Empty = no branch…
git_tags: [<string>] # GitTags lists git tag filters. Each entry is a policy name or "re:<regex>". Empty = no tag…
events: [<string>] # Events lists CI event type filters. Supported: push, tag, release, schedule, manual, pull_request…
forges: [<string>] # Forges restricts this target to specific CI forges by provider name (github, gitlab, gitea…
outcomes: [<string>] # Outcomes gates on the RUN outcome (success | failure | warning) — the notification dimension of…
signals: [<string>] # Signals gates on things the run FOUND, independent of its outcome — a run that succeeded can…
schedules: [<string>] # Schedules restricts this condition to runs fired by one of these schedules (ids from schedules:).…
kind: release¶
publish:
<id>: # entry key = the unique id
kind: release # Kind is the target type. Determines which fields are valid. · required
aliases: [<string>] # Aliases are rolling git tag aliases (kind: release). e.g., ["{version}", "{major}.{minor}"…
tag: <string> # Tag is the immutable identity pattern for a release channel (kind: release). Distinct from Aliases…
archives: <string> # Archives references a binary-archive target ID (kind: release and generic-package).
prerelease: false # Prerelease marks the forge release as a pre-release (kind: release). DEPRECATED: prefer `type…
signing_profile: <string> # SigningProfile references a signing.profiles[].id — the trust profile this target signs under.…
retention: # Retention controls cleanup of old tags/releases. Structured only in v2 (no scalar shorthand).
keep_last: <int> # keep the N most recent tags per series (0/-1/unset = ∞) · required
keep_daily: <int> # keep one per day for the last N days · required
keep_weekly: <int> # keep one per week for the last N weeks · required
keep_monthly: <int> # keep one per month for the last N months · required
keep_yearly: <int> # keep one per year for the last N years · required
keep_branches: <int> # keep the N most-recent identity groups per template (bounds retired branches; 0/unset = ∞) · required
max_age: <string> # keep items newer than this ("14d", "72h"); additive like keep_* — alone it means "only the recent… · required
identity: [<string>] # extra identity vars beyond the {branch}/{env} defaults — partition tags into independent series · required
protect: [<string>] # tag patterns that are never deleted, an explicit override · required
refs: # scoped overrides: a different policy for items matching a pattern (first match wins; unset fields… · required
- match: <string> # pattern (same syntax as protect/templates) · required
keep_last: <int> # required
keep_daily: <int> # required
keep_weekly: <int> # required
keep_monthly: <int> # required
keep_yearly: <int> # required
max_age: <string> # required
when: # When specifies routing conditions for this target: a single condition-set, or a list of them (OR…
- branches: [<string>] # Branches lists branch filters. Each entry is a policy name or "re:<regex>". Empty = no branch…
git_tags: [<string>] # GitTags lists git tag filters. Each entry is a policy name or "re:<regex>". Empty = no tag…
events: [<string>] # Events lists CI event type filters. Supported: push, tag, release, schedule, manual, pull_request…
forges: [<string>] # Forges restricts this target to specific CI forges by provider name (github, gitlab, gitea…
outcomes: [<string>] # Outcomes gates on the RUN outcome (success | failure | warning) — the notification dimension of…
signals: [<string>] # Signals gates on things the run FOUND, independent of its outcome — a run that succeeded can…
schedules: [<string>] # Schedules restricts this condition to runs fired by one of these schedules (ids from schedules:).…
kind: binary-archive¶
publish:
<id>: # entry key = the unique id
kind: binary-archive # Kind is the target type. Determines which fields are valid. · required
build: <string> # Build references a BuildConfig.ID. Required for kind: registry.
name: <string> # Name is the archive filename template (kind: binary-archive). Supports: {id}, {version}, {os}…
format: <string> # Format is the archive format: "tar.gz", "zip", "auto", or "binary" (kind: binary-archive). "auto"…
binary_name: <string> # BinaryName overrides the binary name inside the archive (kind: binary-archive). Auto-detected from…
include: [<string>] # Include lists extra files to bundle into the archive (kind: binary-archive). e.g., ["README.md"…
checksums: false # Checksums generates a SHA256SUMS file alongside archives (kind: binary-archive).
when: # When specifies routing conditions for this target: a single condition-set, or a list of them (OR…
- branches: [<string>] # Branches lists branch filters. Each entry is a policy name or "re:<regex>". Empty = no branch…
git_tags: [<string>] # GitTags lists git tag filters. Each entry is a policy name or "re:<regex>". Empty = no tag…
events: [<string>] # Events lists CI event type filters. Supported: push, tag, release, schedule, manual, pull_request…
forges: [<string>] # Forges restricts this target to specific CI forges by provider name (github, gitlab, gitea…
outcomes: [<string>] # Outcomes gates on the RUN outcome (success | failure | warning) — the notification dimension of…
signals: [<string>] # Signals gates on things the run FOUND, independent of its outcome — a run that succeeded can…
schedules: [<string>] # Schedules restricts this condition to runs fired by one of these schedules (ids from schedules:).…
kind: generic-package¶
publish:
<id>: # entry key = the unique id
kind: generic-package # Kind is the target type. Determines which fields are valid. · required
repo: <string> # Repo references a repos[].id (kind: generic-package). The forge identity (provider, url, project…
package: <string> # Package is the generic package name (kind: generic-package). Defaults to the repo project's…
version: <string> # Version is the immutable package version pattern (kind: generic-package). Resolved against version…
archives: <string> # Archives references a binary-archive target ID (kind: release and generic-package).
when: # When specifies routing conditions for this target: a single condition-set, or a list of them (OR…
- branches: [<string>] # Branches lists branch filters. Each entry is a policy name or "re:<regex>". Empty = no branch…
git_tags: [<string>] # GitTags lists git tag filters. Each entry is a policy name or "re:<regex>". Empty = no tag…
events: [<string>] # Events lists CI event type filters. Supported: push, tag, release, schedule, manual, pull_request…
forges: [<string>] # Forges restricts this target to specific CI forges by provider name (github, gitlab, gitea…
outcomes: [<string>] # Outcomes gates on the RUN outcome (success | failure | warning) — the notification dimension of…
signals: [<string>] # Signals gates on things the run FOUND, independent of its outcome — a run that succeeded can…
schedules: [<string>] # Schedules restricts this condition to runs fired by one of these schedules (ids from schedules:).…
kind: pages¶
publish:
<id>: # entry key = the unique id
kind: pages # Kind is the target type. Determines which fields are valid. · required
provider: <string> # Provider is the vendor type for auth and API behavior. Registry: docker, ghcr, gitlab, jfrog… · one of: cloudflare, github
build: <string> # Build references a BuildConfig.ID. Required for kind: registry.
dir: <string> # Dir publishes a repo directory directly instead of a build's output tree (kind: pages). Exactly one…
domain: [<string>] # Domain is the custom domain(s) (kind: pages). Accepts a bare scalar or a list:
project: <string> # Project is the Cloudflare Pages project name (provider: cloudflare). Default: the target id.…
base_path: <string> # BasePath is the URL path the site is served under (kind: pages). Inferred per provider (Cloudflare…
exclude: [<string>] # Exclude drops matching paths from the publish workspace before deploy (kind: pages). Globs, applied…
when: # When specifies routing conditions for this target: a single condition-set, or a list of them (OR…
- branches: [<string>] # Branches lists branch filters. Each entry is a policy name or "re:<regex>". Empty = no branch…
git_tags: [<string>] # GitTags lists git tag filters. Each entry is a policy name or "re:<regex>". Empty = no tag…
events: [<string>] # Events lists CI event type filters. Supported: push, tag, release, schedule, manual, pull_request…
forges: [<string>] # Forges restricts this target to specific CI forges by provider name (github, gitlab, gitea…
outcomes: [<string>] # Outcomes gates on the RUN outcome (success | failure | warning) — the notification dimension of…
signals: [<string>] # Signals gates on things the run FOUND, independent of its outcome — a run that succeeded can…
schedules: [<string>] # Schedules restricts this condition to runs fired by one of these schedules (ids from schedules:).…