# Releases and promote

Each build moves across your environments through the release steps in parity.yaml: promote, block, wait, run and group.

A release is one build moving across your [environments](/manifest/environments). Each push to the project's branch makes a build and starts a release, which runs the steps under `release:` in order.

```yaml title="parity.yaml"
environments:
  staging: {}
  production: { services: { web: { replicas: 3 } } }
  acme:   { labels: { fleet: dedicated } }
  globex: { labels: { fleet: dedicated } }

release:
  steps:
    - promote: staging
    - run: e2e
      in: staging
    - block: Ship to production
      prompt: Check the release notes and the on-call calendar.
    - promote: production
    - wait: 1h
      label: Soak
    - promote: { labels: { fleet: dedicated } }
      max_parallel: 1
```

Inside one environment, a promote runs that environment's [rollout](/concepts/builds#rollout-stages): its deploy [steps](/concepts/steps), health checks and traffic switch.

## Step types [#step-types]

| Step | Fields | Does |
| --- | --- | --- |
| `promote: <environment>` | A name, a list, or `{ labels: { … } }`; `max_parallel` | Rolls the build out to each environment and waits for every rollout. |
| `block: <label>` | `prompt`, `approvals` (1), `allow_author` (true), `timeout` (24h) | Waits for people with `environments.deploy` to approve. A rejection ends the release. |
| `wait: <duration>` | Or `{ until: <cron>, time_zone }` | A soak, or the next time a schedule names, such as a weekly release train. |
| `run: <job>` | `in: <environment>`, `retry`, `timeout` (1h) | Runs a job from `jobs:` against that environment, with its settings and secrets. The environment must be promoted by an earlier step. |
| `group: [steps]` | | Runs its steps in parallel, and passes when all of them pass. |

Every step also takes `label` (what people see) and `key` (what the CLI and API name it by). Keys default to `promote-<environment>`, `run-<job>`, `wait` or the block's label, made lowercase.

Without `release:`, a project with environments promotes each one in the order declared, with a block before every environment after the first.

## Rules [#rules]

- **One image per build.** The first rollout builds the images. Every later promote of the release runs those images, never a rebuild. Services a push didn't change keep the image the environment already runs, when it runs the build just before; an environment that missed builds rebuilds every service.
- **Newer builds win.** When a newer build reaches the step where an older release waits (a block, a wait, a held promote), the older release is superseded. A promote whose rollouts started finishes. A release never rolls an environment back to an older build.
- **Freezes hold.** A promote into a [frozen](#freezes) environment shows as held until the freeze lifts.
- **Approvals.** `approvals: 2` needs two different people. `allow_author: false` doesn't count the approval of the person who pushed the commit; for a GitHub push that means approving from the console, or with a token made there, so Parity knows who you are. A block can't be approved after its timeout.
- **Skip.** Anyone with `environments.deploy` can skip a wait, run or promote that hasn't finished, with a reason that stays in the release's record. Blocks are approved or rejected, never skipped, and a promote whose rollouts started finishes.
- **Agents read, people decide.** Agents see releases through `release_status`, and can't approve, reject, skip or promote.

```sh title="Terminal"
parity releases                      # newest first, with what each waits for
parity releases 42                   # one release's steps
parity approve ship-to-production    # the newest release waiting there
parity reject ship-to-production -m "error rate up on staging"
parity skip soak -m "hotfix for the checkout outage" --release 42
```

## Promote by hand [#promote-by-hand]

`parity promote <environment>` ships a release's build into an environment, outside the release's steps: by default the newest release whose build rolled out somewhere and that didn't fail or get rejected; `--release <n>` picks another. Use it for a hotfix that skips a step or a customer held on a specific build. It is refused while the environment is frozen.

`parity promote` without an environment makes your local `parity.yaml` the project's current manifest, the one new builds use. A merge to the default branch does the same.

## Rollback [#rollback]

`parity rollback <environment>` returns the environment to the build it ran before (or `--to <commit>`, among its last five). It skips deploy jobs and is never blocked by a freeze.

A rollback pins the environment: releases hold their promotes into it until someone promotes it by hand. The previous revision stays warm for 10 minutes after a switch, so a rollback inside that window is a traffic change.

## Freezes [#freezes]

A freeze holds deploys to an environment, with a note saying why.

```sh title="Terminal"
parity freeze production -m "Black Friday: no deploys until Monday" --for 72h
parity freeze                  # list freezes
parity unfreeze production
```

- Releases hold their promotes into a frozen environment. A person's promote fails with `deploy_frozen`, the note, who froze it and when.
- `--for` or `--until` lifts it automatically. Freezing again replaces the note.
- Rollback still works.
- Setting and lifting a freeze needs `environments.deploy` and a project token. A sandbox token can't set or lift one.
