# swb assemble-release

Assembles a release that moves an app's `stable` channel to a version. Nothing
moves until the release is approved.

## Synopsis

```
usage: swb assemble-release --name <name> --app <app> --version <version> [--channel stable] [--json]
```

## Arguments and flags

| Flag | Takes | Default |
|---|---|---|
| `--name` | The release's **name**, which people read it by. Required. | — |
| `--app` | The app's **id** — an app the signed-in tenant owns. Required. | — |
| `--version` | The **version** to move the channel to, as the build published it: `1.9.0`. Its release tag, `v1.9.0`, names the same version. It must be a version a channel of the app is or has been at, and only one `beta` is or has been at is approved. Required. | — |
| `--channel` | `stable`, or an `lts-<name>` channel. | `stable` |
| `--json` | Nothing. Prints the release as the API answered it instead of the summary. | off |
| `--help`, `-h` | Prints the command's help and exits. | off |

## What it does

Calls `Mutation.assembleRelease` with one move, the app's channel to the
version, and a client token minted once for the run, so a repeated call is
one release rather than two. The release is recorded in the tenant, reading
`assembling`, owned by you, its move naming the version the channel's head
stands at now as the one it moves from, and whether `beta` is or has been at
the version, which the app's history says. No channel moves: approving the
release with [`swb approve-release`](/command-line/approve-release) does.

A release moves one app. An app has one release assembling at a time: where
one stands unapproved, the new release supersedes it in the same write, and
the older one reads `abandoned`.

## What it prints

The release as the control plane answered it:

```
Closer 1.9 (01JRELEASE): assembling
assembled by Maria Ruiz at 2026-10-20 09:00; not approved
  closer stable 1.8.2 → 1.9.0
```

`--json` prints it as [`swb releases`](/command-line/releases) does.

## The operation and the permission

`Mutation.assembleRelease`. The caller needs `swb.releases.create` on the
tenant, which the `release-manager` role carries.

## The same act in the console

Releases, then Assemble release: choose the app and the version from the
app's builds on `beta`, name the release, and assemble it.

## Refusals and exit status

Exit status is 0 once the release is recorded, and 1 on every refusal below.

| Refusal | When |
|---|---|
| `swb assemble-release needs --<flag> <flag>` | `--name`, `--app` or `--version` was not given. |
| `not signed in or token expired — run swb login` | There is no usable credential. |
| `ValidationFailed: …` naming the channel | `--channel` is `beta` or an `alpha/` channel, which only a build moves, or no channel at all. |
| `ValidationFailed: …` naming the version | No channel of the app is or has been at the version, as its catalog and its history say; where the newest page of its history was not the whole of it, the refusal says older entries were not read. |
| `ValidationFailed: …` naming the app as retired | The app has been retired. |
| `NotFound: App <app> not found` | No such app, or an app the tenant does not own. |
| `NotFound: Version <app>@<version> not found` | An app with no repository has had no upload of that version. |
| `Conflict: …` naming the release that is open | Another release of the app was assembled at the same moment. |
| `NotAuthorized: …` naming `swb.releases.create` | The caller lacks the permission. |
