mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-05 21:46:49 -04:00
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. Two things about the frame. Pad the bottom, 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. And cut the end: vhs records until the marker reaches the screen, by which time lazygit has exited and the shell has painted its prompt back over the demo. A browser holds the last frame of a video once it has played to the end, so leaving those frames in would end every demo on a terminal prompt and leave it there. Pass --no-upload while you are still working on how a demo looks. That writes the video to demo/output and stops, so trying out a colour or a font costs nothing but the recording itself. The recording is sharper and smaller than the gif it replaces, at 1866x1230 and 25 fps against 1140x828 and about 5 fps. 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:
co-authored by
Claude Opus 5
parent
0026f853c7
commit
d24bb08384
-112
@@ -1,112 +0,0 @@
|
||||
# Specify a command to be executed
|
||||
# like `/bin/bash -l`, `ls`, or any other commands
|
||||
# the default is bash for Linux
|
||||
# or powershell.exe for Windows
|
||||
command: echo "YOU NEED TO SPECIFY YOUR OWN COMMAND WITH THE -d ARG"
|
||||
|
||||
# Specify the current working directory path
|
||||
# the default is the current working directory path
|
||||
cwd: null
|
||||
|
||||
# Export additional ENV variables
|
||||
env:
|
||||
recording: true
|
||||
|
||||
# Explicitly set the number of columns
|
||||
# or use `auto` to take the current
|
||||
# number of columns of your shell
|
||||
cols: 120 # 100
|
||||
|
||||
# Explicitly set the number of rows
|
||||
# or use `auto` to take the current
|
||||
# number of rows of your shell
|
||||
rows: 35 # 30
|
||||
|
||||
# Amount of times to repeat GIF
|
||||
# If value is -1, play once
|
||||
# If value is 0, loop indefinitely
|
||||
# If value is a positive number, loop n times
|
||||
repeat: 0
|
||||
|
||||
# Quality
|
||||
# 1 - 100
|
||||
# Higher quality seems to make no difference, but running it through
|
||||
# gifsicle ends up with a much better compressed version.
|
||||
quality: 100
|
||||
|
||||
# Delay between frames in ms
|
||||
# If the value is `auto` use the actual recording delays
|
||||
frameDelay: auto
|
||||
|
||||
# Maximum delay between frames in ms
|
||||
# Ignored if the `frameDelay` isn't set to `auto`
|
||||
# Set to `auto` to prevent limiting the max idle time
|
||||
maxIdleTime: 2000
|
||||
|
||||
# The surrounding frame box
|
||||
# The `type` can be null, window, floating, or solid`
|
||||
# To hide the title use the value null
|
||||
# Don't forget to add a backgroundColor style with a null as type
|
||||
frameBox:
|
||||
type: floating
|
||||
title: Lazygit
|
||||
style:
|
||||
border: 0px black solid
|
||||
backgroundColor: "#1d1d1d"
|
||||
margin: -5px
|
||||
|
||||
# Add a watermark image to the rendered gif
|
||||
# You need to specify an absolute path for
|
||||
# the image on your machine or a URL, and you can also
|
||||
# add your own CSS styles
|
||||
watermark:
|
||||
imagePath: null
|
||||
style:
|
||||
position: absolute
|
||||
right: 15px
|
||||
bottom: 15px
|
||||
width: 100px
|
||||
opacity: 0.9
|
||||
|
||||
# Cursor style can be one of
|
||||
# `block`, `underline`, or `bar`
|
||||
cursorStyle: block
|
||||
|
||||
# Font family
|
||||
# You can use any font that is installed on your machine
|
||||
# in CSS-like syntax
|
||||
# Download from:
|
||||
# https://github.com/ryanoasis/nerd-fonts/releases/download/v3.0.2/DejaVuSansMono.zip
|
||||
# Not using the mono font because it makes icons too small.
|
||||
fontFamily: "DejaVuSansM Nerd Font"
|
||||
|
||||
# The size of the font
|
||||
fontSize: 8
|
||||
|
||||
# The height of lines
|
||||
lineHeight: 1
|
||||
|
||||
# The spacing between letters
|
||||
letterSpacing: 0
|
||||
|
||||
# Theme
|
||||
theme:
|
||||
background: "transparent"
|
||||
foreground: "#dddad6"
|
||||
cursor: "#c7c7c7"
|
||||
black: "#7a7a7a"
|
||||
red: "#fc4384"
|
||||
green: "#b3e33b"
|
||||
yellow: "#ffa727"
|
||||
blue: "#102895"
|
||||
magenta: "#c930c7"
|
||||
cyan: "#00c5c7"
|
||||
white: "#c7c7c7"
|
||||
brightBlack: "#676767"
|
||||
brightRed: "#ff7fac"
|
||||
brightGreen: "#c8ed71"
|
||||
brightYellow: "#ebdf86"
|
||||
brightBlue: "#6871ff"
|
||||
brightMagenta: "#ff76ff"
|
||||
brightCyan: "#5ffdff"
|
||||
brightWhite: "#fffefe"
|
||||
+223
-46
@@ -2,55 +2,72 @@
|
||||
|
||||
set -e
|
||||
|
||||
TYPE=$1
|
||||
TEST=$2
|
||||
# The repository the demo is uploaded to. GitHub only plays videos that live in
|
||||
# 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.
|
||||
# It can stay closed; commenting on a closed issue publishes the video just as
|
||||
# well, and does not reopen it.
|
||||
PUBLISH_ISSUE=6051
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [gif|mp4] <test path>"
|
||||
echo "e.g. using full path: $0 gif pkg/integration/tests/demo/nuke_working_tree.go"
|
||||
echo "Usage: $0 [--no-upload] <test path>"
|
||||
echo "e.g. $0 pkg/integration/tests/demo/nuke_working_tree.go"
|
||||
echo
|
||||
echo "--no-upload leaves the video in demo/output and stops there, for"
|
||||
echo "checking how a change to demo/settings.tape turns out."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ "$#" -ne 2 ]
|
||||
UPLOAD=true
|
||||
|
||||
if [ "$1" = "--no-upload" ]
|
||||
then
|
||||
UPLOAD=false
|
||||
shift
|
||||
fi
|
||||
|
||||
TEST=$1
|
||||
|
||||
if [ "$#" -ne 1 ]
|
||||
then
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ "$TYPE" != "gif" ] && [ "$TYPE" != "mp4" ]
|
||||
TOOLS="vhs ttyd ffmpeg"
|
||||
|
||||
if [ "$UPLOAD" = true ]
|
||||
then
|
||||
usage
|
||||
exit 1
|
||||
TOOLS="$TOOLS gh"
|
||||
fi
|
||||
|
||||
if [ -z "$TEST" ]
|
||||
for TOOL in $TOOLS
|
||||
do
|
||||
if ! command -v "$TOOL" > /dev/null 2>&1
|
||||
then
|
||||
echo "$TOOL could not be found"
|
||||
echo "Install it with: brew install $TOOL"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$UPLOAD" = true ]
|
||||
then
|
||||
usage
|
||||
fi
|
||||
WORKTREE_PATH=$(git worktree list | grep assets | awk '{print $1}')
|
||||
|
||||
WORKTREE_PATH=$(git worktree list | grep assets | awk '{print $1}')
|
||||
if [ -z "$WORKTREE_PATH" ]
|
||||
then
|
||||
echo "Could not find assets worktree. You'll need to create a worktree for the assets branch using the following command:"
|
||||
echo "git worktree add .worktrees/assets assets"
|
||||
echo "The assets branch has no shared history with the main branch: it exists to store assets which are too large to store in the main branch."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$WORKTREE_PATH" ]
|
||||
then
|
||||
echo "Could not find assets worktree. You'll need to create a worktree for the assets branch using the following command:"
|
||||
echo "git worktree add .worktrees/assets assets"
|
||||
echo "The assets branch has no shared history with the main branch: it exists to store assets which are too large to store in the main branch."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OUTPUT_DIR="$WORKTREE_PATH/demo"
|
||||
|
||||
if ! command -v terminalizer &> /dev/null
|
||||
then
|
||||
echo "terminalizer could not be found"
|
||||
echo "Install it with: npm install -g terminalizer"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v "gifsicle" &> /dev/null
|
||||
then
|
||||
echo "gifsicle could not be found"
|
||||
echo "Install it with: npm install -g gifsicle"
|
||||
exit 1
|
||||
OUTPUT_DIR="$WORKTREE_PATH/demo"
|
||||
else
|
||||
OUTPUT_DIR=demo/output
|
||||
fi
|
||||
|
||||
# Get last part of the test path and set that as the output name
|
||||
@@ -63,19 +80,179 @@ go generate pkg/integration/tests/tests.go
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# First we record the demo into a yaml representation
|
||||
terminalizer -c demo/config.yml record --skip-sharing -d "go run cmd/integration_test/main.go cli --slow $TEST" "$OUTPUT_DIR/$NAME"
|
||||
# Then we render it into a gif
|
||||
terminalizer render "$OUTPUT_DIR/$NAME" -o "$OUTPUT_DIR/$NAME.gif"
|
||||
SCRATCH=$(mktemp -d)
|
||||
trap 'rm -rf "$SCRATCH"' EXIT
|
||||
|
||||
# Then we convert it to either an mp4 or gif based on the command line argument
|
||||
if [ "$TYPE" = "mp4" ]
|
||||
TAPE="$SCRATCH/$NAME.tape"
|
||||
RECORDING="$SCRATCH/$NAME.mp4"
|
||||
OUTPUT="$OUTPUT_DIR/$NAME.mp4"
|
||||
|
||||
# Start recording once lazygit has drawn the top left corner of a view frame.
|
||||
# Demos run in whichever screen mode they ask for, so no particular panel is
|
||||
# on screen for all of them, but every view is drawn with a frame. This is the
|
||||
# corner that `border: rounded` draws; a demo config that turns borders off
|
||||
# would need a different signal.
|
||||
#
|
||||
# The two quotes in the end marker keep the literal VHSDONE out of the command
|
||||
# line, so that waiting for the marker cannot match the command that prints it.
|
||||
#
|
||||
# The screen is cleared before lazygit is started, so that the screen the
|
||||
# terminal restores when lazygit exits is a blank one rather than the typed
|
||||
# command line. The frames vhs records after lazygit has exited are then blank
|
||||
# apart from the marker, which is what the trimming below looks for.
|
||||
cat > "$TAPE" <<EOF
|
||||
Output "$RECORDING"
|
||||
|
||||
Source demo/settings.tape
|
||||
|
||||
# The command is typed while the recording is hidden, so there is nothing to
|
||||
# gain from animating it.
|
||||
Set TypingSpeed 0ms
|
||||
|
||||
Hide
|
||||
Type "clear; go run cmd/integration_test/main.go cli --slow $TEST; echo VHS''DONE"
|
||||
Enter
|
||||
Wait+Screen@180s /╭/
|
||||
Show
|
||||
Wait+Screen@600s /VHSDONE/
|
||||
Hide
|
||||
EOF
|
||||
|
||||
vhs "$TAPE"
|
||||
|
||||
if [ ! -f "$RECORDING" ]
|
||||
then
|
||||
COMPRESSED_PATH="$OUTPUT_DIR/$NAME.mp4"
|
||||
ffmpeg -y -i "$OUTPUT_DIR/$NAME.gif" -movflags faststart -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" "$COMPRESSED_PATH"
|
||||
else
|
||||
COMPRESSED_PATH="$OUTPUT_DIR/$NAME-compressed.gif"
|
||||
gifsicle --colors 256 --use-col=web -O3 < "$OUTPUT_DIR/$NAME.gif" > "$COMPRESSED_PATH"
|
||||
echo "vhs recorded the demo but wrote no video."
|
||||
echo "vhs 0.12.0 does this; see https://github.com/charmbracelet/vhs/issues/787."
|
||||
echo "Install a working version with: go install github.com/charmbracelet/vhs@v0.11.0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Demo recorded to $COMPRESSED_PATH"
|
||||
# The browser draws its playback controls over the bottom of the video, and
|
||||
# they are tall enough to hide lazygit's caption line. Pad the frame so that
|
||||
# the caption sits above them. Chrome draws the tallest bar of the three, and
|
||||
# at the width a README gives the video its buttons start to overlap the
|
||||
# caption below about 90px, so there is not much room to trim here. Measure it
|
||||
# again if demo/settings.tape changes the size of the recording.
|
||||
CAPTION_CLEARANCE=90
|
||||
|
||||
BACKGROUND=$(sed -n 's/.*"background": *"\(#[0-9a-fA-F]*\)".*/\1/p' demo/settings.tape)
|
||||
|
||||
if [ -z "$BACKGROUND" ]
|
||||
then
|
||||
echo "Could not read the background colour from demo/settings.tape"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# vhs keeps recording until the marker reaches the screen, and by then lazygit
|
||||
# has exited and the terminal has been cleared for the marker. Find the first
|
||||
# of those blank frames so we can cut them off. Measure the share of pixels
|
||||
# that are not background: a cleared terminal reads around 0.03, while the
|
||||
# emptiest thing lazygit draws reads around 1.2, an empty panel still being
|
||||
# drawn with a frame around it. Demos whose config turns borders off would
|
||||
# need a different signal, as the start marker above would too.
|
||||
BLANK_INK=0.5
|
||||
|
||||
CUT=$(ffmpeg -v error -i "$RECORDING" \
|
||||
-vf "format=gray,lutyuv=y='if(gt(val\,60)\,255\,0)',signalstats,metadata=print:key=lavfi.signalstats.YAVG:file=-" \
|
||||
-an -f null - 2>/dev/null |
|
||||
# ffmpeg writes these numbers with a decimal point, so keep awk in a
|
||||
# locale that reads them back that way. The + 0 turns them from strings
|
||||
# into numbers, without which awk compares them as text.
|
||||
LC_ALL=C awk -v blank="$BLANK_INK" '
|
||||
/pts_time:/ { split($3, a, ":"); time = a[2] }
|
||||
/YAVG=/ { split($0, b, "="); n++; at[n] = time + 0; ink[n] = b[2] + 0 }
|
||||
END {
|
||||
# Walk back over the blank frames at the end. Starting from the
|
||||
# end leaves a blank frame that the demo painted over again where
|
||||
# it is, so only the tail after lazygit is cut.
|
||||
k = n + 1
|
||||
while (k > 1 && ink[k - 1] < blank) {
|
||||
k--
|
||||
}
|
||||
if (k <= n) {
|
||||
printf "%.3f\n", at[k]
|
||||
}
|
||||
}')
|
||||
|
||||
if [ -n "$CUT" ]
|
||||
then
|
||||
TRIM="-t $CUT"
|
||||
else
|
||||
TRIM=
|
||||
fi
|
||||
|
||||
# The video ends on the last frame of the demo. A browser goes on showing that
|
||||
# frame once it has played to the end, so it needs no padding out. Move the
|
||||
# moov atom to the front so that playback can start before the whole file has
|
||||
# downloaded.
|
||||
# shellcheck disable=SC2086
|
||||
ffmpeg -y -loglevel error -i "$RECORDING" $TRIM \
|
||||
-vf "pad=iw:ih+$CAPTION_CLEARANCE:0:0:color=$BACKGROUND" \
|
||||
-c:v libx264 -crf 23 -preset slow -pix_fmt yuv420p \
|
||||
-movflags +faststart -an "$OUTPUT"
|
||||
|
||||
if [ "$UPLOAD" = false ]
|
||||
then
|
||||
echo "Demo recorded to $OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# GitHub's web editor posts to this endpoint when you drag a file into a
|
||||
# comment box. It is undocumented, but it accepts an ordinary token, so we can
|
||||
# upload from here. You need push access to $REPO for it to work. If the
|
||||
# endpoint ever goes away, drag the video into a comment box on github.com
|
||||
# instead and copy the URL that GitHub inserts.
|
||||
REPOSITORY_ID=$(gh api "repos/$REPO" --jq .id)
|
||||
|
||||
RESPONSE=$(curl --silent --show-error --fail \
|
||||
--request POST \
|
||||
--header "Authorization: Bearer $(gh auth token)" \
|
||||
--header "Accept: application/json" \
|
||||
--header "Content-Type: video/mp4" \
|
||||
--data-binary "@$OUTPUT" \
|
||||
"https://uploads.github.com/user-attachments/assets?name=$NAME.mp4&content_type=video%2Fmp4&repository_id=$REPOSITORY_ID")
|
||||
|
||||
URL=$(echo "$RESPONSE" | sed -e 's/.*"url":"//' -e 's/".*//')
|
||||
|
||||
if [ -z "$URL" ]
|
||||
then
|
||||
echo "Could not read an attachment URL out of GitHub's response:"
|
||||
echo "$RESPONSE"
|
||||
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:"
|
||||
echo "<video src=\"$URL\" controls></video>"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# Terminal settings shared by all demo recordings. The tape that
|
||||
# demo/record_demo.sh generates for a demo sources this file.
|
||||
|
||||
# Use the non-Mono Nerd Font variant; the Mono variant renders the icons too
|
||||
# small.
|
||||
Set FontFamily "SauceCodePro NF"
|
||||
Set FontSize 24
|
||||
Set LineHeight 1.0
|
||||
Set Padding 20
|
||||
|
||||
# vhs sizes the terminal in pixels rather than in cells, so these two numbers
|
||||
# are what produce a 120x35 grid at the font settings above. If you change the
|
||||
# font, the font size or the padding, record a tape that runs `stty size` and
|
||||
# adjust them until the grid is 120x35 again.
|
||||
Set Width 1866
|
||||
Set Height 1140
|
||||
|
||||
# There is no frame rate setting here because vhs ignores `Set Framerate` when
|
||||
# it writes a video; it captures at 25 fps either way.
|
||||
|
||||
# The frame around the focused view is bold green, and xterm.js draws bold text
|
||||
# in the bright variant of a colour, so brightGreen is the one those frames end
|
||||
# up using.
|
||||
|
||||
Set Theme {
|
||||
"background": "#1d1d1d",
|
||||
"foreground": "#dddad6",
|
||||
"cursor": "#c7c7c7",
|
||||
"selection": "#44475a",
|
||||
"black": "#7a7a7a",
|
||||
"red": "#fc4384",
|
||||
"green": "#3fb950",
|
||||
"yellow": "#ffa727",
|
||||
"blue": "#102895",
|
||||
"magenta": "#c930c7",
|
||||
"cyan": "#00c5c7",
|
||||
"white": "#c7c7c7",
|
||||
"brightBlack": "#676767",
|
||||
"brightRed": "#ff7fac",
|
||||
"brightGreen": "#56d364",
|
||||
"brightYellow": "#ebdf86",
|
||||
"brightBlue": "#6871ff",
|
||||
"brightMagenta": "#ff76ff",
|
||||
"brightCyan": "#5ffdff",
|
||||
"brightWhite": "#fffefe"
|
||||
}
|
||||
@@ -8,17 +8,18 @@ You'll want to familiarise yourself with how integration tests are written: see
|
||||
|
||||
Ideally we'd run this whole thing through docker but we haven't got that working. So you will need:
|
||||
```
|
||||
# for recording
|
||||
npm i -g terminalizer
|
||||
# for gif compression
|
||||
npm i -g gifsicle
|
||||
# for mp4 conversion
|
||||
brew install ffmpeg
|
||||
# for recording; vhs drives ttyd and ffmpeg under the hood
|
||||
brew install ttyd ffmpeg
|
||||
|
||||
# vhs 0.12.0 runs the tape, reports success and writes no video at all
|
||||
# (https://github.com/charmbracelet/vhs/issues/787), so pin the release
|
||||
# before it
|
||||
go install github.com/charmbracelet/vhs@v0.11.0
|
||||
|
||||
# font with icons
|
||||
wget https://github.com/ryanoasis/nerd-fonts/releases/download/v3.0.2/DejaVuSansMono.tar.xz && \
|
||||
tar -xf DejaVuSansMono.tar.xz -C /usr/local/share/fonts && \
|
||||
rm DejaVuSansMono.tar.xz
|
||||
wget https://github.com/ryanoasis/nerd-fonts/releases/download/v3.0.2/SourceCodePro.tar.xz && \
|
||||
tar -xf SourceCodePro.tar.xz -C ~/Library/Fonts && \
|
||||
rm SourceCodePro.tar.xz
|
||||
```
|
||||
|
||||
## Creating a demo
|
||||
@@ -49,34 +50,59 @@ The scripts and demo definitions live in the code branches but the output lives
|
||||
git worktree add .worktrees/assets assets
|
||||
```
|
||||
|
||||
Outputs will be stored in `.worktrees/assets/demos/`. We'll store three separate things:
|
||||
* the yaml of the recording
|
||||
* the original gif
|
||||
* either the compressed gif or the mp4 depending on the output you chose (see below)
|
||||
The mp4 of the recording will be stored in `.worktrees/assets/demo/`.
|
||||
|
||||
### Recording the demo
|
||||
|
||||
Once you're happy with your demo you can record it using:
|
||||
```sh
|
||||
scripts/record_demo.sh [gif|mp4] <path>
|
||||
scripts/record_demo.sh <path>
|
||||
# e.g.
|
||||
scripts/record_demo.sh gif pkg/integration/tests/demo/interactive_rebase.go
|
||||
scripts/record_demo.sh pkg/integration/tests/demo/interactive_rebase.go
|
||||
```
|
||||
|
||||
~~The gif format is for use in the first video of the readme (it has a larger size but has auto-play and looping)~~
|
||||
~~The mp4 format is for everything else (no looping, requires clicking, but smaller size).~~
|
||||
The terminal size, font and colours live in `demo/settings.tape`, which the
|
||||
script sources into the tape it generates for the demo.
|
||||
|
||||
Turns out that you can't store mp4s in a repo and link them from a README so we're gonna just use gifs across the board for now.
|
||||
While you are still working on how a demo looks, pass `--no-upload`. That
|
||||
leaves the video in `demo/output` (which is git-ignored) and stops there, so
|
||||
you can watch it without uploading anything or touching the assets worktree:
|
||||
|
||||
```sh
|
||||
scripts/record_demo.sh --no-upload pkg/integration/tests/demo/interactive_rebase.go
|
||||
```
|
||||
|
||||
### Including demos in README/docs
|
||||
|
||||
If you've followed the above steps you'll end up with your output in your assets worktree.
|
||||
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:
|
||||
|
||||
Within that worktree, stage all three output files and raise a PR against the assets branch.
|
||||
|
||||
Then back in the code branch, in the doc, you can embed the recording like so:
|
||||
```md
|
||||

|
||||
```html
|
||||
<video src="https://github.com/user-attachments/assets/<uuid>" controls></video>
|
||||
```
|
||||
|
||||
This means we can update assets without needing to update the docs that embed them.
|
||||
GitHub plays a video in a README only when it is served from its own attachment
|
||||
store. If you commit a video to the assets branch and link it the way we link
|
||||
the images, GitHub drops the whole `<video>` element when it renders the page.
|
||||
So the README reads the uploaded copy rather than the one in the assets
|
||||
worktree. Keep that one anyway, so that we still have the file if we ever need
|
||||
to upload it again. Stage it and raise a PR against the assets branch as you
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user