Screenery

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.

WhenThen
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" }
  ]
}
FieldTakes
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.
ActionDoes
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.

ResultMeans
verifiedThe rule ran and its actions succeeded.
degradedAn action failed.
not runA 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.

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:

FlagMoves
--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:

ModeA push
snapshotRemoves the names it did not carry. The default.
additiveOnly 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.