Docs / Channels and promotion
Promotion rules
A rule moves channels when something happens on GitHub, such as a release being published. Rules live on the console's channels screen, next to the channels they move.
How rules run
Rules are run by the Screenery GitHub App. GitHub sends the App a signed event, Screenery checks the signature, and every enabled rule in each project bound to that repository gets a chance to act. A rule does not run from your CI job, and needs no secret.
So rules on GitHub events only work for a project bound to a repository through the App. If the App is uninstalled or suspended, the binding still shows on the Access screen, but no event arrives and no rule runs. The rules panel says which of these is stopping it.
You do not need a rule for @latest to follow your default branch. A push from the default branch moves it on its own. See How @latest moves.
Two more events come from Screenery itself rather than GitHub: a build finalizing, and someone reviewing a channel. Rules on those can only post to chat, and they run with or without a bound repository. See Chat notifications.
The default rules
The first time a project is bound to a repository through the App, it gets two rules, enabled. A project that already has rules keeps them, so binding again never brings back a rule you deleted.
| When | Then |
|---|---|
| A release is published | Promote the default branch to @latest, and to a channel named after the release tag. |
| A pull request is closed | Delete its @pr-N channel 30 days later. |
"The default branch" means your repository's: a repository whose default is master gets a rule that promotes from @master.
A promotion copies the whole channel. @latest ends up equal to the source, so names the source no longer has are removed from it. The release-tag channel, such as @v2.3.1, is a frozen copy you can link to. It is never pruned automatically.
Draft releases do not count; publishing one does. A tag that is not a valid channel name, such as one with capital letters, gets no release channel, and the log says why.
If you ship by merging to your default branch and never publish GitHub releases, the release rule never runs. Its log stays empty, and that is correct.
Writing a rule
On the channels screen, an admin adds a rule with + new rule, or edits, pauses or deletes one. A rule is a short document:
{
"on": "release.published",
"filter": { "tag_prefix": "web-v" },
"actions": [
{ "kind": "promote", "from": "main", "to": "latest" },
{ "kind": "promote_release_tag" }
]
}
| Field | Takes |
|---|---|
| on | release.published, pr.closed or branch.push. Also build.finalized and review.submitted, for rules that only post to chat. |
| filter | Optional. { "tag_prefix": … } for releases, { "branch": … } for branch pushes, or { "conditions": … } for builds and reviews (conditions). |
| actions | One to four actions, below. |
| enabled | Optional. false pauses the rule. |
| Action | Does |
|---|---|
| promote | Points to at what from holds. The two must differ. |
| promote_release_tag | Copies the source of the rule's first promote to a channel named after the release tag. |
| prune | Deletes channel after after_days days, 0 to 365. |
| notify_slack | Posts to a chat target; also notify_discord and notify_teams. See Chat notifications. |
Channel names may use the placeholders {pr.number}, {branch} and {tag}, as in pr-{pr.number}. A placeholder the event does not carry skips that action. So does a value that is not a valid channel name. Screenery does not guess a lowercase version.
A rule with an unknown field or action is refused when you save it, rather than stored as something that does less than you wrote.
Monorepos: tag prefixes
Several projects can be bound to one repository. A release on that repository reaches every one of them, so give each project's release rule a tag prefix:
"filter": { "tag_prefix": "web-v" }
With tag_prefix: web-v, an api-v2.1 release does not promote the web project.
The match is a literal prefix. A trailing *, as in web-v*, is accepted and means the same thing. A * anywhere else is refused.
The rule log
Every run of a rule is logged under it on the channels screen, including the runs it skipped. "Why did my release not promote?" is answered there: a skipped run names the tag and the prefix that did not match.
| Result | Means |
|---|---|
| verified | The rule ran and its actions succeeded. |
| degraded | An action failed. |
| not run | A filter did not match, or every action was skipped. Information, not a failure. |
One failed action does not stop the others, and one failed rule does not stop the rest of the project's rules. A paused rule is the only rule that logs nothing. Channels a rule moves show automation as the actor that last moved them.
Pruning pull-request channels
The default prune rule deletes @pr-N 30 days after the pull request closes, merged or not. The deletion is scheduled, and pending deletions are listed on the channels screen with their dates.
- Reopening the pull request cancels its pending deletion.
- If a pull request closes again, the later date wins.
- Release-tag channels are never auto-pruned.
A prune deletes the channel, so its @pr-N URLs answer 404 afterwards. Pausing or deleting the rule does not cancel a deletion it already scheduled.
Pruning is also what keeps a busy project under its channel limit (100 channels on Free, 500 on Pro). Every channel counts, including the branch and @pr-N channels pushes create. At the limit, a push that would create a new channel stores its screenshots and moves existing channels, but skips the new one and warns with channel_limit. A promotion that would create a new channel is refused.
Promoting by hand
npx screenery promote acme/dashboard --from main --to latest
Give exactly one source:
| Flag | Moves |
|---|---|
| --from <channel> | Every asset on that channel. |
| --version <v_…> | One asset. Add --asset <name> if the version id is ambiguous. |
| --rollback <n> | The channel's state n promotions ago. |
It uses the same credential as push: GitHub OIDC when the job has id-token: write, otherwise SCREENERY_TOKEN, a publish token. CLI 0.3.0 needs the publish token. See Credentials.
Every promotion is checked at the edge before the command returns. It exits 1 if the CDN is not serving the new version, so a release nobody can see does not pass as a green CI step. A promotion that changes nothing prints "already points at" and exits 0.
Screenery refuses to promote eval screenshots, or verification receipts, onto @latest.
Rollback
On the channels screen, each channel has a rollback… button. It puts the channel back the way it was before its last promotion. From the CLI, --rollback n goes back n promotions.
A rollback is itself a promotion, recorded in the history, so you can undo it the same way. It also brings back names that a push removed. Promoting and rolling back need the publisher role; editing rules needs admin.
Channel mode
By default a push makes a channel hold exactly what it pushed, and removes the rest. That is snapshot mode. If several jobs feed one project, set the project to additive on the console's Access screen:
| Mode | A push |
|---|---|
| snapshot | Removes the names it did not carry. The default. |
| additive | Only adds and updates. Nothing is removed. |
The mode applies to @latest, branch channels and @pr-N. In additive mode a --from promotion removes nothing either. A change affects the next push only. Old @v_… URLs never stop working.
To mark one push as partial instead (--partial on the CLI, partial: true on the Action) requires CLI 0.4.0.