amend! Record demos with vhs and upload them to GitHub's attachment store

Record demos with vhs and publish them to GitHub's attachment store

The demo gifs in the README start on their own, loop without telling the
reader where a run begins, and give no way to pause, seek or replay. A
video with the browser's own controls fixes all three, but GitHub plays
a video in a README only when it is served from its own attachment
store. If you commit one to the assets branch and link it the way we
link the images, GitHub drops the whole <video> element when it renders
the page.

Replace terminalizer with vhs. vhs records the demo straight to mp4
rather than going through a gif, and it takes the terminal size, font
and colours from demo/settings.tape. Then upload the result from the
endpoint that GitHub's own drag-and-drop upload posts to, and print the
tag to paste into the page.

Uploading alone is not enough. An attachment is readable only by people
who are signed in to GitHub until a posted comment in the repository
refers to it, and a README on a branch does not count. So post each
recording to a collecting issue and wait until the video can be fetched
without a token. Miss that step and the video plays for whoever recorded
it and 404s for every other reader.

Pad the bottom of the frame as well, because the browser draws its
playback controls over the video and they are tall enough to cover the
line where the demos put their captions.

The recording is sharper and smaller than the gif it replaces. It runs
at 1866x1290 and 25 fps for 304K, against 1140x828 and about 5 fps for
461K. It also costs the reader nothing until they press play, whereas
the gifs are fetched every time the README is opened.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-09-22 21:37:45 +02:00
co-authored by Claude Opus 5
parent 84dd4b41d7
commit 8c1852e074
2 changed files with 55 additions and 2 deletions
+42
View File
@@ -8,6 +8,10 @@ TEST=$1
# its own attachment store, and an attachment is tied to one repository.
REPO=jesseduffield/lazygit
# The issue that collects the demo recordings. Posting a comment there is what
# makes an uploaded video readable by people who are not signed in to GitHub.
PUBLISH_ISSUE=
usage() {
echo "Usage: $0 <test path>"
echo "e.g. $0 pkg/integration/tests/demo/nuke_working_tree.go"
@@ -19,6 +23,14 @@ then
usage
fi
if [ -z "$PUBLISH_ISSUE" ]
then
echo "Set PUBLISH_ISSUE at the top of this script to the number of the issue"
echo "that collects demo recordings. Without a comment referring to it, the"
echo "video is only visible to people who are signed in to GitHub."
exit 1
fi
for TOOL in vhs ttyd ffmpeg gh
do
if ! command -v "$TOOL" > /dev/null 2>&1
@@ -136,6 +148,36 @@ then
exit 1
fi
# An attachment stays private until a posted comment somewhere in the
# repository refers to it. Until that happens the video is a 404 for anyone who
# is not signed in, and the README shows a broken player. Referring to it once
# makes it public for good, even if the comment is deleted afterwards, so we
# collect the recordings in one issue and leave the comments in place.
gh api "repos/$REPO/issues/$PUBLISH_ISSUE/comments" \
--raw-field "body=$NAME
$URL" > /dev/null
# Make sure that worked before handing over a URL, because the person recording
# the demo is signed in and will not see the failure.
ATTEMPT=0
while [ "$ATTEMPT" -lt 30 ]
do
if curl --silent --fail --output /dev/null --max-time 20 --range 0-1 "$URL"
then
break
fi
ATTEMPT=$((ATTEMPT + 1))
sleep 2
done
if [ "$ATTEMPT" -eq 30 ]
then
echo "$URL is still not readable without signing in to GitHub."
echo "Embedding it now would give logged-out readers a broken player."
exit 1
fi
echo "Demo recorded to $OUTPUT"
echo
echo "Embed it with:"
+13 -2
View File
@@ -66,8 +66,9 @@ script sources into the tape it generates for the demo.
### Including demos in README/docs
Recording a demo does two things with the mp4: it writes it to your assets
worktree, and it uploads a copy to GitHub's attachment store. The script then
Recording a demo does three things with the mp4: it writes it to your assets
worktree, it uploads a copy to GitHub's attachment store, and it posts that
copy as a comment on the issue named by `PUBLISH_ISSUE` in the script. Then it
prints the tag to embed:
```html
@@ -85,5 +86,15 @@ would for any other asset.
Attachment URLs are opaque and have no path we can predict, so a new recording
of an existing demo means a new URL and an edit to the page that embeds it.
That comment on `PUBLISH_ISSUE` is not bookkeeping; it is what makes the video
watchable. An uploaded attachment is readable only by people signed in to
GitHub until some posted comment in the repository refers to it, and a README
on a branch does not count. Skip that step and the video plays for you and
404s for everyone else, which is easy to miss because you are signed in. The
script waits until the video can be fetched without a token before it prints
the tag. Referring to an attachment once is enough and cannot be undone, so
the comments could be deleted later, but leaving them gives us a dated list of
every recording.
Uploading needs push access to the lazygit repository. If you don't have it,
record the demo, then ask a maintainer to upload the mp4 for you.