Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,21 +119,32 @@ isn't realistic. The instructor stages an SSD; students load from it.
```

This produces `images-amd64.tar`, `images-arm64.tar`,
`rangerdanger.tgz`, a `.version` marker, and an auto-generated SSD
`rangerdanger.tgz`, a `.version` marker, the one-click launchers
(`START-HERE.cmd` / `start-here.command`), and an auto-generated SSD
README on the volume. Tagged releases additionally bundle
`rangerdanger-wsl2-kernel` + `rangerdanger-wsl2-kernel.sha256` so
Windows students get ICS DPI on Labs 2.3 / 2.3-bonus without a
separate kernel download.

**On the workshop laptop (student):**

Easiest — double-click the launcher on the SSD: **START-HERE.cmd**
(Windows) or **start-here.command** (macOS). It unpacks the lab files to
`~/rangerdanger` and runs setup for you — no path to type, nothing to
extract by hand.

Manual equivalent:

```bash
tar xzf /Volumes/WORKSHOP_SSD/rangerdanger.tgz -C ~ # creates ~/rangerdanger
cd ~/rangerdanger
./setup.sh --from-tarballs /Volumes/WORKSHOP_SSD
# Windows: .\setup.ps1 -FromTarballs D:\WORKSHOP_SSD
```

`setup.sh` detects the host architecture, loads the matching
tarball with `docker load`, then runs `docker compose up -d`.
Do **not** extract `images-*.tar` — `setup` loads it with `docker load`.
`setup` detects the host architecture, loads the matching tarball, then
runs `docker compose up -d`.

The release artifacts (image tarballs) are also attached to each
[GitHub release](https://github.com/tonylturner/rangerdanger/releases)
Expand Down
29 changes: 16 additions & 13 deletions docs/workshop-ssd.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ kernel asset:
|---|---|---|
| `images-amd64.tar` | All Docker images for Intel/AMD64 hosts, saved together | ~6 GB |
| `images-arm64.tar` | All Docker images for Apple Silicon / arm64 hosts. `openplc` is cross-included as the amd64 image (Rosetta 2 on macOS); also bundles `tonistiigi/binfmt` so arm64 **Linux** hosts can register amd64 emulation offline. | ~6 GB |
| `rangerdanger.tgz` | The repo at the staged commit | ~1-2 MB |
| `rangerdanger.tgz` | The repo at the staged commit (extracts to a self-contained `rangerdanger/` folder) | ~1-2 MB |
| `START-HERE.cmd` / `start-here.command` | One-click launchers (Windows / macOS): unpack the repo to `~/rangerdanger` and run setup. Students just double-click — no path to type, nothing to extract by hand. | <1 KB |
| `README.md` | Auto-generated per-stage instructions for the student | ~1 KB |
| `.version` | Plain-text version marker (`vX.Y.Z` or `latest`) | <1 KB |
| `rangerdanger-wsl2-kernel` + `.sha256` | Custom WSL2 kernel for Windows ICS DPI labs - only present when staging from a tagged release whose `build-wsl-kernel.yml` workflow has produced the asset. `setup.ps1 -FromTarballs` picks it up automatically. | ~14 MB |
Expand Down Expand Up @@ -63,11 +64,17 @@ in the working tree at stage time is what students get.
## Student first-run install

Documented short form in [`quickstart.md`](quickstart.md#path-c---offline--ssd-workshops).
Long form for the operator:

Students normally just **double-click `START-HERE.cmd` (Windows) or
`start-here.command` (macOS)** on the SSD — it runs the extract + setup
below for them, with no path to type and no archive to unpack by hand.
Long form for the operator (or to run by hand):

```sh
git clone https://github.com/tonylturner/rangerdanger # OR copy from SSD
cd rangerdanger
# from the SSD (offline):
tar xzf /Volumes/WORKSHOP_SSD/rangerdanger.tgz -C ~
# or with internet: git clone https://github.com/tonylturner/rangerdanger ~/rangerdanger
cd ~/rangerdanger
./setup.sh --from-tarballs /Volumes/WORKSHOP_SSD
```

Expand Down Expand Up @@ -128,10 +135,8 @@ AirDrop, Slack, anything. Student replaces their repo and restarts:
```sh
cd ~/rangerdanger
docker compose down
mkdir -p ~/rangerdanger-new
tar xzf /Volumes/WORKSHOP_SSD/rangerdanger.tgz -C ~/rangerdanger-new
cd ~/rangerdanger-new
./setup.sh --from-tarballs /Volumes/WORKSHOP_SSD # idempotent; reuses loaded images
tar xzf /Volumes/WORKSHOP_SSD/rangerdanger.tgz -C ~ # overwrites ~/rangerdanger in place
./setup.sh --from-tarballs /Volumes/WORKSHOP_SSD # idempotent; reuses loaded images
```

The existing Docker images stay put. No `docker load` re-run is
Expand Down Expand Up @@ -211,11 +216,9 @@ Whatever the delta-README.md says (it's auto-generated per-stage so
the version numbers are correct), but the pattern is:

```sh
# 1. update repo
cd ~
mkdir -p rangerdanger-new
tar xzf /Volumes/WORKSHOP_SSD/delta-v0.1.17/rangerdanger.tgz -C rangerdanger-new
cd rangerdanger-new
# 1. update repo (extract over your existing ~/rangerdanger in place)
cd ~/rangerdanger
tar xzf /Volumes/WORKSHOP_SSD/delta-v0.1.17/rangerdanger.tgz -C ~

# 2. load only the changed images
docker load -i /Volumes/WORKSHOP_SSD/delta-v0.1.17/delta-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar
Expand Down
18 changes: 17 additions & 1 deletion setup.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,23 @@ if ($FromTarballs) {
}

$tarball = Join-Path $FromTarballs "images-$arch.tar"
if (-not (Test-Path $tarball)) { Die "Expected $tarball (this host is $arch). Have you staged the right architecture?" }
if (-not (Test-Path $tarball)) {
# Turn the two common -FromTarballs mistakes into a helpful message
# instead of a bare "not found": pointing at the repo instead of the
# SSD, or pointing at a hand-extracted image archive.
$hint = ""
if ((Test-Path (Join-Path $FromTarballs "setup.ps1")) -or (Test-Path (Join-Path $FromTarballs "docker-compose.release.yml"))) {
$hint = " That path looks like the rangerdanger repo, not the SSD. Point -FromTarballs at the drive that holds images-$arch.tar (e.g. D:\)."
} elseif ((Test-Path (Join-Path $FromTarballs "manifest.json")) -or (Test-Path (Join-Path $FromTarballs "blobs"))) {
$hint = " That path looks like an EXTRACTED image archive -- you don't need to extract images-$arch.tar. Point -FromTarballs at the folder that CONTAINS it; setup runs 'docker load' for you."
} else {
$otherArch = if ($arch -eq 'amd64') { 'arm64' } else { 'amd64' }
if (Test-Path (Join-Path $FromTarballs "images-$otherArch.tar")) {
$hint = " Found images-$otherArch.tar but this host is $arch -- did you stage the right architecture?"
}
}
Die "Expected $tarball (this host is $arch).$hint"
}
$sizeMB = [math]::Round((Get-Item $tarball).Length / 1MB)
Say "Loading $tarball (${sizeMB} MB) - decompressing each image, ~5-15 min on a fast SSD."
Say "Watch the 'Loaded image:' lines below - one per image, 14-19 total."
Expand Down
18 changes: 17 additions & 1 deletion setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,23 @@ if [ -n "$TARBALL_DIR" ]; then
fi

TARBALL="$TARBALL_DIR/images-$ARCH.tar"
[ -f "$TARBALL" ] || die "Expected $TARBALL (this host is $ARCH). Have you staged the right architecture?"
if [ ! -f "$TARBALL" ]; then
# Turn the two common --from-tarballs mistakes into a helpful message
# instead of a bare "not found": pointing at the repo instead of the
# SSD, or pointing at a hand-extracted image archive.
hint=""
if [ -f "$TARBALL_DIR/setup.sh" ] || [ -f "$TARBALL_DIR/docker-compose.release.yml" ]; then
hint=" That path looks like the rangerdanger repo, not the SSD. Point --from-tarballs at the dir that holds images-$ARCH.tar (e.g. /Volumes/WORKSHOP_SSD)."
elif [ -f "$TARBALL_DIR/manifest.json" ] || [ -d "$TARBALL_DIR/blobs" ]; then
hint=" That path looks like an EXTRACTED image archive — you don't need to extract images-$ARCH.tar. Point --from-tarballs at the folder that CONTAINS it; setup runs 'docker load' for you."
else
other=amd64; [ "$ARCH" = "amd64" ] && other=arm64
if [ -f "$TARBALL_DIR/images-$other.tar" ]; then
hint=" Found images-$other.tar but this host is $ARCH — did you stage the right architecture?"
fi
fi
die "Expected $TARBALL (this host is $ARCH).$hint"
fi
SIZE_HUMAN=$(ls -lh "$TARBALL" | awk '{print $5}')
say "Loading $TARBALL ($SIZE_HUMAN) - decompressing each image, ~5-15 min on a fast SSD."
say "Watch the 'Loaded image:' lines below - one per image, 14-19 total."
Expand Down
11 changes: 7 additions & 4 deletions stage-ssd-delta.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,10 @@ if ($changed.Count -gt 0) {

Banner "Stage repo archive -> rangerdanger.tgz"
$tgzPath = Join-Path $OutDir "rangerdanger.tgz"
& git -C $RootDir archive --format=tar.gz -o $tgzPath HEAD
# --prefix=rangerdanger/ so extraction creates a self-contained
# rangerdanger/ folder (see stage-ssd.ps1). The delta apply extracts
# over the student's existing ~/rangerdanger in place.
& git -C $RootDir archive --prefix=rangerdanger/ --format=tar.gz -o $tgzPath HEAD
if ($LASTEXITCODE -ne 0) { Die "git archive failed" }
$tgzSizeMB = [math]::Round((Get-Item $tgzPath).Length / 1MB, 2)
Say "wrote $tgzPath (${tgzSizeMB} MB)"
Expand Down Expand Up @@ -316,10 +319,10 @@ $unchangedList
Run from the student's existing ``~/rangerdanger`` directory:

``````sh
# 1. update the repo (always)
# 1. update the repo (extracts over your existing ~/rangerdanger in place)
cd ~/rangerdanger
docker compose down
tar xzf <delta-dir>/rangerdanger.tgz -C ~/rangerdanger-new
cd ~/rangerdanger-new
tar xzf <delta-dir>/rangerdanger.tgz -C ~

# 2. load only the changed images for this host's arch
ARCH=`$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
Expand Down
12 changes: 8 additions & 4 deletions stage-ssd-delta.sh
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,11 @@ if [ "${#CHANGED[@]}" -gt 0 ]; then
fi

banner "Stage repo archive -> rangerdanger.tgz"
git -C "$ROOT_DIR" archive --format=tar HEAD | gzip > "$OUT/rangerdanger.tgz"
# --prefix=rangerdanger/ so extraction creates a self-contained
# rangerdanger/ folder (see stage-ssd.sh for the full rationale). The
# delta apply instructions below extract over the student's existing
# ~/rangerdanger in place, which relies on this prefix.
git -C "$ROOT_DIR" archive --prefix=rangerdanger/ --format=tar HEAD | gzip > "$OUT/rangerdanger.tgz"
TGZ_SIZE=$(du -h "$OUT/rangerdanger.tgz" | awk '{print $1}')
say "wrote $OUT/rangerdanger.tgz ($TGZ_SIZE)"

Expand Down Expand Up @@ -396,10 +400,10 @@ $([ "${#UNCHANGED[@]}" -gt 0 ] && echo "## Unchanged (kept from prior install)"
Run from the student's existing \`~/rangerdanger\` directory:

\`\`\`sh
# 1. update the repo (always)
# 1. update the repo (extracts over your existing ~/rangerdanger in place)
cd ~/rangerdanger
docker compose down
tar xzf <delta-dir>/rangerdanger.tgz -C ~/rangerdanger-new
cd ~/rangerdanger-new
tar xzf <delta-dir>/rangerdanger.tgz -C ~

# 2. load only the changed images for this host's arch
ARCH=\$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
Expand Down
94 changes: 88 additions & 6 deletions stage-ssd.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,11 @@ Banner "Stage repo archive -> rangerdanger.tgz"
$tgzPath = Join-Path $OutDir "rangerdanger.tgz"
# git can produce gzipped tar directly via --format=tar.gz. Avoids
# needing an external gzip on Windows.
& git -C $RootDir archive --format=tar.gz -o $tgzPath HEAD
# --prefix=rangerdanger/ so extraction creates a rangerdanger/ folder
# instead of scattering repo files into the extract target (which made
# the generated README's `cd ~/rangerdanger` fail). All extract docs
# assume this prefix.
& git -C $RootDir archive --prefix=rangerdanger/ --format=tar.gz -o $tgzPath HEAD
if ($LASTEXITCODE -ne 0) { Die "git archive failed" }
$tgzSizeMB = [math]::Round((Get-Item $tgzPath).Length / 1MB, 2)
Say "wrote $tgzPath (${tgzSizeMB} MB)"
Expand Down Expand Up @@ -215,6 +219,72 @@ try {
Warn " Without the kernel, Windows students on this SSD lose ICS DPI on Labs 2.3 / 2.3-bonus."
}

Banner "Write launchers (one-click student entry points)"
# Self-locating launchers students double-click -- the file lives on the
# SSD so its own dir is the tarball dir (no path to type, no drive letter
# to guess, no docker-load-vs-tar choice). The .cmd also bypasses
# PowerShell's execution policy. Write .cmd CRLF + .command LF explicitly
# (normalized here, not left to the host) so a Windows- or mac-staged SSD
# produces the same files; UTF-8 no-BOM (a BOM breaks cmd.exe's first line).
$noBom = New-Object System.Text.UTF8Encoding($false)
$startCmd = @'
@echo off
setlocal
rem RangerDanger workshop launcher. This file lives on the SSD, so %~dp0
rem is the SSD path -- you never type a path and the drive letter does not
rem matter. Just double-click it.
set "REPO=%USERPROFILE%\rangerdanger"

echo ============================================================
echo RangerDanger workshop launcher
echo Lab files (this SSD): %~dp0
echo Installs to: %REPO%
echo ============================================================
echo.

if not exist "%REPO%\setup.ps1" (
echo [+] Unpacking lab files ^(one-time, ~1 MB^)...
tar -xzf "%~dp0rangerdanger.tgz" -C "%USERPROFILE%"
if errorlevel 1 (
echo [x] Could not unpack rangerdanger.tgz from this SSD.
echo Run this file from the SSD itself, not a copy on the Desktop.
pause
exit /b 1
)
) else (
echo [+] Reusing existing install at %REPO%.
)

echo [+] Starting setup ^(loads images from the SSD, brings the stack up^)...
echo.
powershell -NoProfile -ExecutionPolicy Bypass -File "%REPO%\setup.ps1" -FromTarballs "%~dp0."
echo.
echo [i] When you see "RangerDanger is up", open http://localhost:8088/exercises
pause
'@
$cmdCrlf = (($startCmd -replace "`r`n","`n") -replace "`n","`r`n")
[System.IO.File]::WriteAllText((Join-Path $OutDir "START-HERE.cmd"), $cmdCrlf, $noBom)
Say "wrote $OutDir\START-HERE.cmd"

$startCommand = @'
#!/bin/bash
# RangerDanger workshop launcher (macOS / Linux). Double-click in Finder,
# or run: bash start-here.command
set -e
SSD="$(cd "$(dirname "$0")" && pwd)"
REPO="$HOME/rangerdanger"
echo "Lab files (this SSD): $SSD"
echo "Installs to: $REPO"
if [ ! -f "$REPO/setup.sh" ]; then
echo "[+] Unpacking lab files (one-time)..."
tar xzf "$SSD/rangerdanger.tgz" -C "$HOME"
fi
exec "$REPO/setup.sh" --from-tarballs "$SSD"
'@
$cmdLf = ($startCommand -replace "`r`n","`n")
[System.IO.File]::WriteAllText((Join-Path $OutDir "start-here.command"), $cmdLf, $noBom)
Say "wrote $OutDir\start-here.command"

Banner "Write README"
$shortSha = & git -C $RootDir rev-parse --short HEAD
$lastSubject = & git -C $RootDir log -1 --format=%s
Expand All @@ -231,17 +301,29 @@ Staged $now for version ``$Version``.
- ``rangerdanger.tgz`` -- Repo archive at $shortSha ($($lastSubject.Substring(0, [Math]::Min(80, $lastSubject.Length))))
$kernelReadmeRow

## Use
## Quick start (recommended)

- **Windows:** double-click **START-HERE.cmd** on this SSD.
- **macOS:** double-click **start-here.command** (or run ``bash start-here.command``).

The launcher unpacks the lab files to ``~/rangerdanger`` and runs setup
for you -- no path to type, no drive letter to guess.

You do **not** need to extract ``images-*.tar`` -- setup loads it for you
with ``docker load``.

## Manual (if you prefer)

Copy the four files to the student's laptop, then:
Replace <SSD> with this drive (e.g. /Volumes/WORKSHOP_SSD on macOS, or the
SSD's drive letter such as D: on Windows):

``````sh
tar xzf rangerdanger.tgz -C ~
tar xzf <SSD>/rangerdanger.tgz -C ~ # creates ~/rangerdanger
cd ~/rangerdanger
./setup.sh --from-tarballs <dir-containing-the-tarballs>
./setup.sh --from-tarballs <SSD>
``````

(or ``./setup.ps1 -FromTarballs <dir>`` on Windows.)
(or ``.\setup.ps1 -FromTarballs <SSD>`` on Windows.)

``setup.sh`` / ``setup.ps1`` auto-detects the host architecture and
loads the right ``images-<arch>.tar`` before bringing the stack up.
Expand Down
Loading
Loading