diff --git a/.images/get-started/dosbox.png b/.images/get-started/dosbox.png new file mode 100644 index 0000000000..b0bd13f6ee Binary files /dev/null and b/.images/get-started/dosbox.png differ diff --git a/.images/get-started/not-private-verify.png b/.images/get-started/not-private-verify.png new file mode 100644 index 0000000000..beae6c892a Binary files /dev/null and b/.images/get-started/not-private-verify.png differ diff --git a/.images/get-started/not-private.png b/.images/get-started/not-private.png new file mode 100644 index 0000000000..1a3e658c05 Binary files /dev/null and b/.images/get-started/not-private.png differ diff --git a/README.md b/README.md index 7010dfb7d2..2c405c7c34 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Zarf runs on [a bunch of operating systems](./docs/supported-oses.md) and aims t - Coming Soon! + [Read](./examples/game/) diff --git a/docs/asciinema/.gitignore b/docs/asciinema/.gitignore new file mode 100644 index 0000000000..169f8fc36f --- /dev/null +++ b/docs/asciinema/.gitignore @@ -0,0 +1,3 @@ +recordings/ +.config/ +*.log \ No newline at end of file diff --git a/docs/asciinema/README.md b/docs/asciinema/README.md new file mode 100644 index 0000000000..15f2429ff6 --- /dev/null +++ b/docs/asciinema/README.md @@ -0,0 +1,188 @@ +# asciinema + +The Zarf docs include a series of recorded / published terminal sessions which we use to brag to (potential) users about the appeal of our clear, easy-to-use CLI. These recordings let them _experience_ Zarf in a way that simply reading can't, they pass on pro usage tips / context, and they help new users find their footing with the tool—we love them. + +They don't come "for free" though—creating them takes time; keeping them current takes even more. + +To facilitate this process we use a product called [asciinema](https://asciinema.org/) which does two things for us: 1) captures _actual terminal sessions_, and 2) serves as a simple, backing web host for storage & (browser-based) playback of recordings. + +Best of all, because it provides for both of those functions through its CLI, we can _script the creation & update of our terminal session recordings_ for ultra-low cost of maintenance & near total elimination of toil. Yay! + +  + + +## The Flow + +1. [Setup Your Environment](#setup-your-environment) + +1. [Run Scenarios](#run-scenarios) + +1. [Review Recordings](#review-recordings) + +1. [Publish to Asciinema.org](#publish-to-asciinemaorg) + +  + + +## Setup Your Environment + +1. Put a Zarf release into the `/build` folder—it doesn't matter if you download it or build it yourself, just make sure _it's the version you want to generate docs against_. + +1. Log Zarf into Iron Bank—instructions [here](../ironbank.md#2-configure-zarf-the-use-em). + +1. Install the things necessary to run our [./Vagrantfile](./Vagrantfile)—instructions [here](../workstation.md#i-want-a-demoexample-sandbox). + +1. Configure your "asciinema.org install id" such that `asciinema` uploads are tied to Zarf's collection of recordings. Follow [these instructions](#setup---zarf-install-id) then come back & continue. + + +  + + +## Run Scenarios + +Make sure the `expect` scripts you need _exist in the `./scenarios` folder_, then run the utility script: + +```sh +./asciinema.sh run [""|"all"] # run & record ALL scenarios +./asciinema.sh run # run & record a SPECIFIC scenario +``` + +Watch the terminal do its magic & dump your new recordings to the `./recordings` folder. + +  + + +## Review Recordings + +By this point, you should have some terminal session recordings in the `./recordings` folder. They (currently) only exist on _your machine_ though, so this is your chance to check them before they "go live". + +You can use your local terminal _sort-of-like_ a video player & "play" back any of your recordings like so: + +```sh +asciinema play +``` + +Be sure to do a review & verify that what you caught during recording was everything you expected to! + +  + + +## Publish to Asciinema.org + +Once all of your recordings look good it's time to push them out to https://asciinema.org! + +During publishing `asciinema` uses a unique identifier—called an "install id"—to connect an uploaded recording to the appropriate asciinema.org account. It stores this id in a file on your disk: `$HOME/.config/asciinema/install-id`. + +When you run `asciinema` commands, if you do not already have an `install-id` file one is generated for you. This makes for a nice new-user experience and if you only need to upload anonymous, temporary recordings to asciinema.org this is enough. + +If you need recordings to stick around and be listed in a specific group, however—and in this case you do—then you'll need access to an install id that has been registered with Zarf's account on asciinema.org. + +> _**Dig in!**_ +> +> Go ahead and have a look at the asciinema [usage page](https://asciinema.org/docs/usage) (under the "auth" heading) if you're interested in more detail / background on this topic. + +  + +### The Flow + +1. [Publish New Recordings](#publish-new-recordings) + +1. [Update Project Links](#update-project-links) + +1. [Remove Outdated Recordings](#remove-outdated-recordings) + +  + + +### Setup - Zarf install id + +#### Access + +For access to an "install id" that has been authorized to upload recordings to [the Zarf account](./asciinema-org) on asciinema.org, you can: + +- Login to [Zarf's asciinema.org account](./asciinema-org) and copy down a "recorder token" (a.k.a. "install id") from the [account settings](https://asciinema.org/user/edit) page, _**or**_— + +- Generate a new install id locally, then login & associate it to [Zarf's asciinema.org account](./asciinema-org) (as described [under the "auth" heading](https://asciinema.org/docs/usage)), _**or**_— + +- Get someone else with access to do any of the above _for_ you & just send back the install id! + + +  + + +#### Associate + +Once you have access to an authorized install id you need to put it in a location on disk where `asciinema` knows to look for it. + +**If you are configuring your own machine to run asciinema** you can drop your install id into an appropriate file with a couple of commands that look like this: + +```sh +mkdir -p $HOME/.config/asciinema +echo -n "" > $HOME/.config/asciinema/install-id +``` + +**If you plan to use the [./Vagrantfile](./Vagrantfile) to run asciinema instead** you should dump your install id into a `./.config/asciinema/install-id` file (in the same directory as the README.md you're reading right now!) as this will allow your install id to be mounted into the VM on startup: + +```sh +mkdir -p ./.config/asciinema +echo -n "" > ./.config/asciinema/install-id +``` + +  + + +### Publish New Recordings + +After you've configured `asciinema` to use the Zarf install id it's time to publish some recordings! + +Again, using the utility script, you'll run a command that looks something like this: + +```sh +./asciinema.sh pub [""|"all"] # upload ALL recordings +./asciinema.sh pub # upload a SPECIFIC recording +``` + +> _**Take note**_ +> +> Publishing _always creates a brand new link_ at asciinema.org! There is, unfortunately (as of Oct 2021), no "official" way to update a recording using the `asciinema` CLI—ugh. + +Once the recordings have been published you'll see some `.url` files appear in your `recordings` folder—these files contain the **new** asciinema.org urls that you'll use to update the rest of your project links. + +  + + +### Update Project Links + +After you publish recordings to asciinema.org you'll have some project docs' links to update. By-hand updates can be slow, annoying, and error-prone though, so... utility script to the rescue! + +```sh +./asciinema.sh see # see where all the links are (manual use!) +./asciinema.sh sub # substitute old urls for new (full auto!) +``` + +These commands will speed you along no matter which manner of update you prefer (manual or auto). + +> _**Recommendation**_ +> +> Post-modification, pre-commit reviews are less bothersome (and easier to rollback) if you clear your working directory (e.g. `git stash`) before running the `sub` command. + +  + + +### Remove Outdated Recordings + +The last step in the asciinema.org publishing flow is to go and _clean out old recordings_. Regrettably, this step is a manual one because there is no "official" way (as of Oct 2021) to delete recordings via the `asciinema` CLI. + +The task here is pretty simple, thankfully: +1. Point your browser at the asciinema.org account listed in [this file](./asciinema-org). + +1. Log in. + +1. Delete all recordings _other than the most recent upload of **any given title**_ by handing-off to this utility script command: + + ```sh + ./asciinema.sh zap # nukes ALL the expired stuff! + ``` + + Some interaction will be required for this command to complete—you'll have to dig some cookies out of your browser's dev tools & hand them over. While this isn't an ideal situation, it _is_ about the best we can do without "real" support for deletes in `asciinema`. It's still _way_ better than trying to cleanup a bunch of recordings by hand. +  diff --git a/docs/asciinema/Vagrantfile b/docs/asciinema/Vagrantfile new file mode 100644 index 0000000000..79de28a689 --- /dev/null +++ b/docs/asciinema/Vagrantfile @@ -0,0 +1,31 @@ +Vagrant.configure("2") do |config| + + config.vm.provider "virtualbox" do |vb| + vb.check_guest_additions = false + vb.cpus = 4 + vb.memory = 4096 + end + + config.vm.box = "boxomatic/ubuntu-20.04" + config.vm.network "forwarded_port", guest: 80, host: 50080 + config.vm.network "forwarded_port", guest: 443, host: 50443 + + config.vm.hostname = "asciinema" + config.vm.synced_folder '.', '/vagrant', disabled: true + config.vm.synced_folder '../..', '/zarf', SharedFoldersEnableSymlinksCreate: false + config.vm.synced_folder'./.config/asciinema', '/root/.config/asciinema', + SharedFoldersEnableSymlinksCreate: false, + owner: 'root', group: 'root' + config.vm.synced_folder'~/.docker', '/root/.docker', + SharedFoldersEnableSymlinksCreate: false, + owner: 'root', group: 'root' + + config.ssh.insert_key = false + config.ssh.extra_args = [ "-t", "cd /zarf/docs/asciinema; sudo su" ] + + config.vm.provision "shell", inline: <<-'SCRIPT' + # https://serverfault.com/questions/500764/dpkg-reconfigure-unable-to-re-open-stdin-no-file-or-directory + export DEBIAN_FRONTEND=noninteractive + apt-get --yes install asciinema expect + SCRIPT +end diff --git a/docs/asciinema/asciinema-org b/docs/asciinema/asciinema-org new file mode 100644 index 0000000000..5950224295 --- /dev/null +++ b/docs/asciinema/asciinema-org @@ -0,0 +1 @@ +https://asciinema.org/~barrett \ No newline at end of file diff --git a/docs/asciinema/asciinema.sh b/docs/asciinema/asciinema.sh new file mode 100755 index 0000000000..1f01620164 --- /dev/null +++ b/docs/asciinema/asciinema.sh @@ -0,0 +1,317 @@ +#!/bin/bash + +# Assumptions: +# - run as root (for proper zarf permissions) + +# Usage: +# - described in ./README.md + +# env +ME="$( basename "${BASH_SOURCE[0]}" )" +HERE="$( cd "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +SCENARIOS="$HERE/scenarios" +RECORDINGS="$HERE/recordings" +ROOTDIR="$( realpath "$HERE/../.." )" +ZARFDIR="$( realpath "$ROOTDIR/build" )" + +# func defs +function help() { + echo "" + echo "Usage: $ME [args...]" + echo "" + echo " run - run a scripted scenario & record terminal session" + echo " pub - publish recording to asciinema.org" + echo " see - show outdated project asciinema.org links" + echo " sub - substitute newly-uploaded asciinema.org links in project source" + echo " zap - remove all but the MOST CURRENT recordings from asciinema.org" + echo "" +} + +# executes (and records) a given scenario file's terminal session +function run() { + local fname="${1##*/}" + local fpath="$SCENARIOS/$fname" + + if [ ! -f "$fpath" ] ; then + echo "" + echo "Can't find scenario: $fpath" + echo "" + return 1 + fi + + mkdir -p "$RECORDINGS" + local base="${fname/.exp/}" + local recording="$RECORDINGS/$base.rec" + local log="${recording/.rec/.log}" + rm -f "$RECORDINGS/$base".* + + local cols=$(tput cols) + local rows=$(tput lines) + ( + cd "$ZARFDIR" \ + && stty cols 90 rows 30 \ + && expect -d -f "$fpath" "$recording" "$log" + ) + stty cols "$cols" rows "$rows" +} + +# finds scenarios and "run"s them all +function run_all() { + local scenarios=$(find "$SCENARIOS" -name '*.exp' -type f) + + # need to keep access to calling tty so settings can be + # manipulated during recording + local tty="$(tty)" + echo "$scenarios" | while read scenario ; do + run "$(basename "$scenario")" < "$tty" + done +} + +# uploads a given recording file to asciinema.org +function pub() { + local fname="${1##*/}" + local fpath="$RECORDINGS/$fname" + + if [ ! -f "$fpath" ] ; then + echo "" + echo "Can't find recording: $fpath" + echo "" + return 1 + fi + + local uname="$( basename $fname ".rec" ).url" + local upath="$RECORDINGS/$uname" + if [ -f "$upath" ] ; then + echo "URL file found. Skipping: $fname." && return 0 + fi + + asciinema upload "$fpath" \ + | grep -Po 'https://asciinema.org[^ |\n]*' \ + | tr -d '\n' \ + > "$upath" +} + +# finds new recordings and "pub"s them all +function pub_all() { + local recordings=$(find "$RECORDINGS" -name '*.rec' -type f) + + echo "$recordings" | while read recording ; do + pub "$(basename "$recording")" + done +} + +# finds, parses, and outputs locations of all asciinema.org links in project +# - helps creator of new recordings know what needs update +# - intput to "automatic link update" function (sub) below +function see() { + local found="$( + find "$ROOTDIR" -type f -name '*.md' -print0 \ + | xargs -0 grep -Pon '(http|https)://asciinema.org/a/[^ \]\)'"'"'"]*' + )" + if [ -z "$found" ] ; then return 0 ; fi + + echo "$found" | while IFS= read -r f ; do + local file=$(echo "$f" | awk -F ':' '{print $1}' ) + local line=$(echo "$f" | awk -F ':' '{print $2}' ) + local url=$(echo "$f" | cut -d ':' -f3- ) + local proto=$(echo "$url" | cut -d ':' -f1 ) + local host=$(echo "$url" | cut -d '/' -f3 ) + local path=$(echo "$url" \ + | cut -d '/' -f4- \ + | cut -d '#' -f1 \ + | cut -d '?' -f1 \ + | cut -d '.' -f1 + ) + local ext=$(echo "$url" \ + | cut -d '#' -f1 \ + | cut -d '?' -f1 \ + | awk -F '/' '{print $NF}' \ + | grep '\.' \ + | cut -d '.' -f2 + ) + local query=$(echo "$url" | awk -F '?' '{print $2}' | grep -o '^[^#]*' ) + local fragment=$(echo "$url" | awk -F '#' '{print $2}' ) + local scenario=$(echo "$query" | grep -Po 'x-scenario=\K[^&]*' ) + + local sub="no" + local rsu="$RECORDINGS/$scenario.url" + if [ -f "$rsu" ] ; then + local found_base="$proto://$host/$path" + local rsu_url=$( cat "$rsu" ) + if [ "$found_base" != "$rsu_url" ] ; then + local from="$url" + local to="$rsu_url${ext:+.$ext}${query:+?$query}${fragment:+#$fragment}" + sub="$from > $to" + fi + fi + + echo "Found: $f" + echo " file : $file" + echo " line : $line" + echo " protocol : $proto" + echo " host : $host" + echo " path : $path" + echo " ext : $ext" + echo " query : $query" + echo " fragment : $fragment" + echo " scenario : $scenario" + echo " sub : $sub" + echo "" + done +} + +# substitutes newly-published asciinema.org URLs for old values +function sub() { + local lines="$( printf '%.0s- ' {1..12} )" + local found="$( cat - | paste -d '\t' $lines )" # <-- reads stdin! + local clean="$( echo "$found" | tr -s ' ' | sed -E 's/: \t/: -\t/g' )" + + echo "$clean" | while IFS= read -r c ; do + local sub="$( echo "$c" | grep -Po 'sub : \K[^\t]*' )" + if [ "$sub" = "no" ] ; then continue ; fi + + local file="$( echo "$c" | grep -Po 'file : \K[^\t]*' )" + local line="$( echo "$c" | grep -Po 'line : \K[^\t]*' )" + local from="$( echo "$sub" | awk -F '>' '{print $1}' | tr -d ' ' )" + local to="$( echo "$sub" | awk -F '>' '{print $2}' | tr -d ' ' )" + + local to_sed_safe="${to/&/\\&}" + sed -i'' "${line}s|$from|$to_sed_safe|" "$file" + done +} + +# removes any "outdated" links from asciinema.org +function zap() { + # Web scraping, eeewww! + local account="$( cat "$HERE/asciinema-org" )" + + # scrape paging controls + local page_one="$( curl --silent --location "$account" )" + local page_nav="$( echo "$page_one" \ + | grep '