Skip to content

Publishing Releases

Changing configuration and making it live are separate steps in ConfigHub. Editing a Unit reaches no cluster; publishing a Release is what makes a change live. A Release bundles the Units in a Space that are assigned to the Space's release Target, captured at a point in time, and serves them as an immutable, read-only OCI artifact for your GitOps operator to pull. See integrating with GitOps operators for how the bundle reaches the cluster.

Releases are published per Space, so you generally release a whole variant at once.

Commands

The cub release subcommands publish, list, get, withdraw, and delete Releases. For the complete flag reference, see the cub release CLI docs.

Publish

cub release publish <space-slug>

Creates a Release of <space-slug>, bundling the Units assigned to the Space's release Target and making it available via the OCI server. By default each Unit is captured at its head Revision; pass --revision to instead pin each Unit to the highest-numbered Revision carrying a Tag (a Unit with no matching tagged Revision falls back to its head Revision).

Each Unit's Revision is selected by Tag, so the value given to --revision has to resolve to one Tag. A Tag itself does; so do the boundaries of a ChangeSet and a ChangeOrder, which is how a whole change is released rather than a revision number that means something different in every Unit:

Form What it selects
<slug> or Tag:<slug> the Revision that Tag marks
ChangeSet:<slug> where a closed ChangeSet ended
Before:ChangeSet:<slug> the state it started from
ChangeOrder:<slug> where the change arrived
Before:ChangeOrder:<slug> the state before it

A revision number, a Revision ID, and the named revisions each pick out a Revision of one Unit, so they are not accepted.

# Bundle each Unit at the Revision tagged "v1.2.0"
cub release publish --revision v1.2.0 my-space

# Bundle each Unit at the Revision a promoted change arrived at
cub release publish --revision ChangeOrder:web-base/release-v452 my-space

# Release the state a promotion started from, to roll it back
cub release publish --revision Before:ChangeOrder:web-base/release-v452 my-space

--label, --annotation, and --delete-gate set the Release's metadata at publish time; cub release update can change it afterwards.

Publishing advances each bundled Unit's LastReleasedRevisionNum to the revision captured in the bundle. UnreleasedChanges compares a Unit's head revision against it, so it tells you what has changed since the last publish.

What blocks a publish

A Unit with an outstanding Validation Error is not ready to be released, and blocks the publish. This is not terminal: the check re-runs the Unit's Triggers, so once the underlying problem is fixed, the publish succeeds on the next attempt. Validation Errors are evaluated against the revision being bundled, so --revision v1.2.0 checks the gates on the v1.2.0 revisions specifically.

A publish with --revision ChangeOrder:<slug> is a Release for that ChangeOrder, and records it in the Release's ChangeOrderID. When the ChangeOrder is governed by a ChangeWorkflow, the publish must also satisfy the ReleasePrerequisites of the Stage the Space belongs to, such as an approval of exactly the revisions being bundled. They are evaluated after the bundle is assembled, over the revisions it contains, and a refusal names the requirement and the revision that falls short, and leaves no Release behind:

Failed: HTTP 422: unable to publish a release of change order 'release-v452' in stage 'prod': requires release-manager: 1 Approval attestation(s) from eligible attesters who did not write the change; web revision 3 has 0 of 1

Recording what is missing, here with cub variant approve, and publishing again is how it clears. Publishing a Release for a ChangeOrder also updates the ChangeOrder's Stage, since Released and Final prerequisites read which Spaces have released the change. Before:ChangeOrder:<slug> names the state before the change, so a Release of it is not a Release for the ChangeOrder.

Withdraw

cub release withdraw <release-id>

Takes a Release out of service: it is no longer published for download, so its OCI endpoint stops serving it. The Release itself is retained — withdrawing is un-publishing, not deleting — so it remains listable and inspectable, and the record of what was published survives.

The Release is located by its globally-unique ID, so --space is not required. If any Unit in the bundle has an outstanding DestroyGate, the withdrawal is blocked until the gate clears.

Delete

cub release delete <release-id>

Removes the Release and its stored bundle, and the release-N Tag it made; a Tag it adopted stays. This is the destructive form; use withdraw when the intent is to stop serving a Release rather than to erase it. The Release is located by its ID, so --space is not required.

A Release marks the Revisions it bundled, and its Tag marks them too, so the delete is refused until --detach removes the marks: cub release delete --detach <release-id>. See deleting referenced entities.

Get

cub release get --space <space-slug> <release-id>

Gets details about a Release. Instead of a release ID, you can identify a Release within a specific --space by OCI reference:

  • --oci-reference latest — the newest Release published for the Space's release Target.
  • --oci-reference sha256:... — a specific manifest digest.
  • --bundle-digest sha256:... — a specific bundle content digest.

List

cub release list --space <space-slug>

Lists the Releases in a Space, or across all Spaces with --space '*'. Supports the standard --where, --filter, and --contains selectors:

# Find a Release by bundle digest across the whole organization
cub release list --space '*' --where "Digest = 'sha256:...'"

Getting started

In order to publish a Release, you must have an OCI ProviderType Target and a Space with Units ready for deployment. This can be done by the following:

cub space create target-space # holds the Target
cub target create --provider OCI --space target-space oci-example # creates the Target
cub space create --release-target target-space/oci-example app-example # creates the Space

cub variant upload --component app-example --variant base --space app-example ./app-example/

The Target lives in its own Space (target-space) to sidestep an ordering problem: a Space's --release-target must already exist when the Space is created, so the Target can't live in the Space it releases. It names no worker; with none, cub target create defaults to OCI/Any. How a GitOps operator authenticates to pull the Release is covered in GitOps.

The cub variant upload step uploads ./app-example/ as Units in the Space app-example (--component and --variant are required). Note the --release-target option on the Space creation: it sets Space.ReleaseTargetID, which acts as the default Target for Units in that Space. Uploaded Units inherit that Target automatically, so they're picked up by the Release without any per-Unit configuration.

Now that we have a Space populated with Units ready for deployment, we can publish a Release. Use the following:

cub release publish app-example

This will bundle all Units and make them available at Space.ReleaseURL, to fetch that value, use the following:

cub space get app-example -o jq='.ReleaseURL'

The created Release will be available at that value plus the OCI tag latest, or its manifest digest, available via the following command:

cub release list --space app-example

Or if you know the ReleaseID of the Release already, you can do:

cub release get --space app-example <ReleaseID>

Together, the ReleaseURL and latest or a manifest digest can be used to configure an OCI client such as ArgoCD or FluxCD synchronizing the image as an application to be applied to a cluster.

Releasing Updates

Once we have an OCI client synchronizing Releases, we can publish updates from edited Units by running publish once again, like so:

cub release publish app-example

This will bundle the Units at their head Revision, so any updates made, and any Units with no updates since the initial Release, will be bundled. If you want to pin a Release to a specific set of Revisions instead, create a Tag and apply it to those Revisions, like so:

cub tag create v0.1.0
cub unit tag --space app-example --where "Slug LIKE '%'" v0.1.0

cub unit tag requires a selection (--unit, --where, or --filter) and tags each selected Unit's head Revision by default. The --where "Slug LIKE '%'" above matches every Unit in the Space; narrow it (for example --unit my-unit or a label match) to tag only a subset. Then publish a Release pinned to that Tag:

cub release publish --revision v0.1.0 app-example

From there, assuming the OCI client is using the latest OCI tag, the configuration can be automatically synchronized, otherwise, the manifest digest will need to be used.

Taking a Release out of service

If a Release was made erroneously or is later found to have issues, withdraw it. It is un-published — no longer served from its OCI endpoint, so no client can pull it — while the Release itself is retained, so what was published stays on the record:

cub release withdraw <ReleaseID>

Withdrawing does not roll anything back. A GitOps operator following the latest OCI tag needs something to converge on, so publish the previous state as a new Release. --revision Before:ChangeOrder:<slug> names the state a promoted change started from, which is how a rollout is rolled back:

cub release publish --revision Before:ChangeOrder:web-base/release-v452 my-space

To remove a Release and its stored bundle entirely, rather than to stop serving it, use cub release delete <ReleaseID>. A Destroy Gate on any Unit in the bundle blocks withdrawal until it clears.

See tracking and promoting changes for undoing a change that has been promoted into several Spaces.