borg/docs/misc/asciinema/demo.tcl
Thomas Waldmann 21c7c34bdf
new borg2 demo screencast, recorded in a container, fixes #6303
The screencast is still typed by expect, but it now runs in a container
(podman or docker) instead of a vagrant VM, see #8040.

record.sh builds borg from the git tag given by BORG_VERSION, generates
the demo data and records docs/misc/asciinema/borg2-demo.cast.

The demo itself was rewritten for borg 2: repo-create, an archive series
addressed by archive IDs, mount, delete/undelete, prune, compact, check.
The demo data is generated (notes, logs, a database dump) instead of
downloading wallpaper jpegs, so that compression is actually visible.

Removes the borg 1.2 screencast scripts and recordings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 15:21:21 +02:00

209 lines
6.6 KiB
Tcl

# The borg2 demo screencast, see README.rst.
#
# Everything below is "typed" into a shell by expect, so what you see in the
# screencast is what really happened - if borg changes, just record it again.
# Configuration for send -h
# Tries to emulate a human typing
# Tweak this if typing is too fast or too slow
set send_human {.07 .13 1 .02 .28}
set passphrase "correct horse battery staple"
# Colors for the lines we type. The output of the commands is not touched, it
# looks exactly like it looks in your terminal.
set color(reset) "\033\[0m"
set color(prompt) "\033\[1;36m"
set color(comment) "\033\[0;32m"
set color(command) "\033\[1;33m"
set color(option) "\033\[0;35m"
# Type a line like a human would, with a bit of syntax highlighting:
# comments in one color, the command in another one, its options in a third.
proc type_line {line} {
global color
if {[string index $line 0] eq "#"} {
send_user -- $color(comment)
send_user -h -- $line
send_user -- $color(reset)\n
return
}
set tokens [split $line " "]
for {set i 0} {$i < [llength $tokens]} {incr i} {
set token [lindex $tokens $i]
if {$i > 0} {
send_user -h " "
}
# "borg create" and friends: the subcommand belongs to the command
if {$i == 0 || ($i == 1 && [lindex $tokens 0] eq "borg" && ![string match "-*" $token])} {
send_user -- $color(command)
} elseif {[string match "-*" $token]} {
send_user -- $color(option)
}
send_user -h -- $token
send_user -- $color(reset)
}
send_user \n
}
# The screencast uses an archive series (all backups are named "docs"), so
# single archives have to be addressed by their archive ID. Those IDs only
# exist while recording, so we look them up and substitute them into the lines
# we type: __AID1__ is the oldest archive, __AID3__ the most recent one.
set aids {}
proc aid {n} {
global aids passphrase
if {[llength $aids] == 0} {
# Only set these for our own borg call - the shell we spawn below must
# not inherit them, or borg would not ask for the passphrase at all.
set ::env(BORG_REPO) "/media/backup/borgdemo"
set ::env(BORG_PASSPHRASE) $passphrase
foreach id [split [string trim [exec borg repo-list --short]] \n] {
lappend aids [string range [string trim $id] 0 7]
}
unset ::env(BORG_REPO) ::env(BORG_PASSPHRASE)
}
return [lindex $aids [expr {$n - 1}]]
}
set script {
# Hi! This is a quick tour of BorgBackup 2.0 - deduplicating, compressing and encrypting backups.
# Note: made with __BORG_VERSION__, other versions may behave differently.
# This is the data we want to back up - some notes, logs and a project directory:
du -sh ~/Documents
ls ~/Documents
# Backups are stored in a "repository". Let's tell borg where ours is,
# so we do not have to repeat it in every command:
export BORG_REPO=/media/backup/borgdemo
# It could also live on another machine, e.g. rest://user@server/backup.
# Creating the repository - encrypted and authenticated, with the key stored in the repo itself:
borg repo-create --encryption=aes256-ocb --key-location=repokey
# The key is protected by the passphrase we just typed - do not lose either of them!
# Typing that passphrase for every command would be boring here, so:
export BORG_PASSPHRASE='correct horse battery staple'
# For real backups, better use BORG_PASSCOMMAND and your password manager.
# Now our first backup:
borg create --progress docs ./Documents
# That was half a GB of files - but how much of it ended up in the repository?
du -sh /media/backup/borgdemo
# Quite a bit less - borg compressed it on the way in (lz4 by default, zstd packs more).
# Let's add a file...
echo "a new file" > ~/Documents/notes/newfile.txt
# ...and back up again, under the same name:
borg create docs ./Documents
# Much faster. And the repository did not really grow, either:
du -sh /media/backup/borgdemo
# Borg only stored what really changed - the rest was deduplicated.
# What if we move a big directory somewhere else?
mv ~/Documents/projects ~/Documents/projects-archived
borg create docs ./Documents
du -sh /media/backup/borgdemo
# Also almost free: borg deduplicates by content, it does not care about the path.
# So, what do we have in the repository now?
borg repo-list
# Three backups, all named "docs" - that is an "archive series" in borg 2.
# Each of them has its own archive ID, which is how we address a single archive:
borg list aid:__AID3__ | head -5
# What changed between our first two backups?
borg diff aid:__AID1__ aid:__AID2__
# Restoring a single file - extraction is relative to the current directory:
mkdir ~/restore
cd ~/restore
borg extract --noxattrs aid:__AID2__ Documents/notes/newfile.txt
cat Documents/notes/newfile.txt
cd ~
# You can also just browse your backups like a filesystem:
mkdir /tmp/mnt
borg mount /tmp/mnt
ls /tmp/mnt
ls /tmp/mnt/docs-__AID3__/Documents
borg umount /tmp/mnt
# Deleting an archive is not immediate - borg 2 only marks it as deleted...
borg delete aid:__AID1__
borg repo-list
# ...so, if that was a mistake, you can simply undo it:
borg undelete aid:__AID1__
borg repo-list
# Keeping only some archives is what "borg prune" is for (--dry-run shows what it would do):
borg prune --list --dry-run --keep-daily 7 --keep-weekly 4
# Space is only freed when you ask for it - that is what makes undelete possible:
borg compact -v
# And of course you can verify that everything in the repository is still fine:
borg check -v
# That's it! Have a look at https://www.borgbackup.org/ for much more.
}
set script [string trim $script]
set script [string map [list __BORG_VERSION__ [exec borg --version]] $script]
set script [split $script \n]
# Always type an empty line before starting a new comment, so the screencast
# does not look like a wall of text.
set spaced {}
set previous ""
foreach line $script {
if {[string index $line 0] eq "#" && $previous ne "" && [string index $previous 0] ne "#"} {
lappend spaced ""
}
lappend spaced $line
set previous $line
}
set script $spaced
# We echo the commands ourselves (with human-like typing), so switch off the
# echo of the terminal and use a minimal prompt.
set ::env(PS1) "$color(prompt)$ $color(reset)"
set stty_init -echo
set timeout -1
spawn -noecho /bin/sh
expect "$ "
foreach line $script {
while {[regexp {__AID([0-9])__} $line -> n]} {
set line [string map [list __AID${n}__ [aid $n]] $line]
}
type_line $line
send $line\n
expect {
"Enter new passphrase: " {
send -h "$passphrase\n"
exp_continue
}
"Enter same passphrase again: " {
send -h "$passphrase\n"
exp_continue
}
-re "Enter passphrase for key .*: " {
send -h "$passphrase\n"
exp_continue
}
-ex {Do you want your passphrase to be displayed for verification? [yN]: } {
send \n
exp_continue
}
"$ "
}
}