Skip to main content
A demo video goes stale when the app changes. Keep it, and CI films it again after each merge and delivers what changed.

Keep a video

In the repo, with a video your agent recorded:
This writes .demoman/videos/<name>.json, everything a re-film needs, and exports the video to each --to path. Commit both. --gif adds a GIF. Keep refuses, writing nothing, a video whose current version has Studio edits a re-film does not carry (a mask, a trim, a cut, a split, a hold, a gap, a placed zoom), typed text the recording never read, or a site you signed in to by hand with signin rather than as a demo account. The refusal names which. Record it again without them. When your agent later runs a script into a kept video, the kept file follows the script.

Refresh

Films every kept video again, with no agent. It prints a line per video: Refresh compares the screen before each step, and the final screen when the video rests at the end, in the video it judges: the first MP4 that is not 9:16, else the first file that is not 9:16, else the first MP4. Where the kept file recorded a still screen, the new film is read at the same step. If the picture changed, every file of the video is replaced, with the kept file’s record of its screens. Videos are not compared byte for byte, so an unchanged app makes no commit. On a compared screen, a change as small as one digit of body text is seen, and so is a colour change alone: grey text a few shades lighter, a button turned another colour of the same lightness. Five gaps are accepted:
  • A change seen only in motion or mid-scroll, or something that appears and goes within one step, is not seen. It is seen once it shows on a screen that is compared.
  • A change too small to tell from the video’s own compression is not seen: a single character in very light text, or a slight shade change over a small area, such as a thin border or a small icon.
  • An animation always in view, such as a spinner, a clock or a carousel, can read as changed on some refreshes: it shows a different frame on a compared screen (#10).
  • A slower app that tips the camera to pull back, where it held before (or the reverse), reads as changed. The camera decides from the gap between two clicks, and a gap near that line can fall either side of it.
  • A video kept on macOS is filmed again on Linux by the first refresh in CI. Linux’s fonts differ, so that refresh replaces it once. After that, refresh in CI only: a refresh on a Mac and one in CI replace each other’s files.
A kept file with no recorded screens is not refreshed. Its line says to run demoman keep for it again.

Run it on every merge

Writes .github/workflows/demoman.yml at the repo root. Asked, or given as flags:
  • --trigger push runs on each push to the default branch. Fill in the workflow’s “Start your app” step: refresh films the app at its address. --trigger <environment> runs after each successful deployment to that environment, and films the deployed app.
  • --route pr opens a pull request with the changed files. --route main commits them to the branch.
A deployment trigger cannot use --route main: the deployed commit is not a branch. Setup refuses the pair and names pr. The workflow checks out the full history (fetch-depth: 0) with any Git LFS files (lfs: true, so a video in LFS is compared, not a pointer), installs Chromium and fonts on Ubuntu, runs demoman refresh --deliver, and takes one run at a time (concurrency: demoman-refresh). Add a DEMOMAN_SECRET_<NAME> and its _ORIGIN for each demo account the kept scripts sign in with.

The pull request route

Turn on Allow GitHub Actions to create and approve pull requests under Settings → Actions → General. While it is off, refresh pushes the branch, opens no pull request, says so, and exits 1. Refresh force-pushes demoman/refresh and opens one pull request titled “Refresh demo videos”, or updates the open one. The description lists each replaced video and its files, and any video that failed. A workflow token cannot attach a video, so open the files under “Files changed” to play them. Commits that were on demoman/refresh before the run are replaced, and the description says so.

The main route

One “Refresh demo videos” commit on the checked-out branch, pushed. Nothing is reviewed first.

No loop

The bot’s commit carries [skip demoman], the refresh pull request’s merge commit names demoman/refresh, and its squash merge is titled “Refresh demo videos (#N)”. The workflow reads the triggering commit’s message and does nothing when it finds any of them. A rebase merge keeps the bot’s commit message. If you rename the pull request before squashing it, one more refresh runs; it replaces only a video whose picture changed. When no video was replaced, nothing is committed. On the pull request route, when every kept video was filmed and came back unchanged, an open refresh pull request is then closed with a comment: its videos are out of date. When a video failed, or only named videos were refreshed, the pull request stays open.