diff options
| author | Igor Soarez <igor@soarez.org> | 2026-10-05 12:02:45 +0300 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-10-05 12:02:45 +0300 |
| commit | 10fb1ab5ac9091e59957e97c3e75ea6bc288b8c5 (patch) | |
| tree | 7adca7723f6f5e6d76540df9e9d0a1af3674803e /tmuxrdr/scripts | |
| parent | 2312d3978b7d123968e834c0e0080ad2ff5559df (diff) | |
tmuxrdr: build and run as its own binary beside stock herdr
scripts/build builds the checkout with the pinned toolchains -- Homebrew's
keg-only rustup, Zig 0.16.0 from ~/.local/opt, crates through sfw, Zig's
hash-pinned packages prefetched by Zig since sfw's proxy breaks them -- and
installs it as ~/.local/bin/tmuxrdr. install now sets up tmuxrdr only, in
~/.config/tmuxrdr, and refuses a binary that reads its config elsewhere.
test-e2e tests tmuxrdr and starts with a config check. The config turns off
herdr's update check, whose update would install stock herdr over the fork.
Diffstat (limited to 'tmuxrdr/scripts')
| -rwxr-xr-x | tmuxrdr/scripts/build | 52 | ||||
| -rwxr-xr-x | tmuxrdr/scripts/install | 28 | ||||
| -rwxr-xr-x | tmuxrdr/scripts/sync-upstream | 13 | ||||
| -rwxr-xr-x | tmuxrdr/scripts/test-e2e | 15 |
4 files changed, 86 insertions, 22 deletions
diff --git a/tmuxrdr/scripts/build b/tmuxrdr/scripts/build new file mode 100755 index 00000000..2305ef73 --- /dev/null +++ b/tmuxrdr/scripts/build @@ -0,0 +1,52 @@ +#!/bin/bash + +# Build tmuxrdr from this checkout and install it as ~/.local/bin/tmuxrdr +# (or $TMUXRDR_BIN). cargo's output is herdr's binary, target/release/herdr; +# the name it is installed under is all the renaming there is -- it starts its +# server from its own path, and src/config/io.rs gives it its own directories. +# +# Toolchains, found without PATH or shell-profile changes: +# Rust the version rust-toolchain.toml pins, through rustup; Homebrew's +# keg-only rustup (/opt/homebrew/opt/rustup/bin) is used when no +# cargo is on PATH +# Zig exactly 0.16.0, which herdr's vendored libghostty-vt requires: +# $ZIG, else ~/.local/opt/zig-0.16.0/zig +# sfw Socket Firewall, which every crate fetch goes through (Zig's +# packages are fetched first, by Zig, hash-checked) +# +# The binary is replaced by a rename, so a running tmuxrdr server keeps the +# old one until it is restarted. + +set -euo pipefail + +repo="$(cd "$(dirname "$0")/../.." && pwd -P)" +dest="${TMUXRDR_BIN:-$HOME/.local/bin/tmuxrdr}" + +die() { >&2 echo "ERROR: $*"; exit 1; } + +if ! command -v cargo >/dev/null && [[ -x /opt/homebrew/opt/rustup/bin/cargo ]]; then + PATH="/opt/homebrew/opt/rustup/bin:$PATH" +fi +command -v cargo >/dev/null || die "no cargo: brew install rustup" +command -v sfw >/dev/null || die "no sfw: crates are fetched through Socket Firewall" + +export ZIG="${ZIG:-$HOME/.local/opt/zig-0.16.0/zig}" +[[ -x "$ZIG" ]] || die "no Zig at $ZIG (needs exactly 0.16.0: ziglang.org/download, signature-checked)" +[[ "$("$ZIG" version)" == "0.16.0" ]] || die "$ZIG is Zig $("$ZIG" version), not 0.16.0" + +cd "$repo" +# Zig's packages are fetched by Zig itself, each checked against the hash +# build.zig.zon pins -- not through sfw, whose proxy breaks Zig's downloads +# (400 Bad Request). With them cached, the build itself needs only crates. +(cd vendor/libghostty-vt && "$ZIG" build --fetch=all) +sfw cargo build --release --locked + +mkdir -p "$(dirname "$dest")" +cp target/release/herdr "$dest.new" +mv -f "$dest.new" "$dest" +echo "installed: $dest ($("$dest" --version))" + +if "$dest" status server 2>/dev/null | grep -q 'status: running'; then + echo "A tmuxrdr server is running the previous build; it switches on its next" + echo "start (tmuxrdr server stop ends it, and its panes)." +fi diff --git a/tmuxrdr/scripts/install b/tmuxrdr/scripts/install index f4c0f1ea..e125a29e 100755 --- a/tmuxrdr/scripts/install +++ b/tmuxrdr/scripts/install @@ -1,27 +1,30 @@ #!/bin/bash -# Put this machine on tmuxrdr's config and plugin. Dry run by default; pass -# `yes` to apply -- the same interface as dot's link.sh. +# Put this machine's tmuxrdr on the checkout's config and plugin. Dry run by +# default; pass `yes` to apply -- the same interface as dot's link.sh. Only +# tmuxrdr is touched: stock herdr keeps its own config and plugins in +# ~/.config/herdr. # # ~/.local/share/tmuxrdr/plugin -> <repo>/tmuxrdr/plugin # the stable path config.toml's popups run from, whatever the checkout -# is called; registered with `herdr plugin link` -# ~/.config/herdr/config.toml -> <repo>/tmuxrdr/config/config.toml +# is called; registered with `tmuxrdr plugin link` +# ~/.config/tmuxrdr/config.toml -> <repo>/tmuxrdr/config/config.toml # herdr saves its settings with an in-place write (src/config/write.rs), # so the link survives and every change lands here, under git. A real # file already there is kept beside it as config.toml.pre-tmuxrdr. # -# Run it again after moving the checkout. Uses whichever herdr is first on -# PATH, or $HERDR. +# Run it again after moving the checkout. Needs the tmuxrdr binary +# (scripts/build), or $HERDR pointing at another tmuxrdr build; it refuses a +# binary that reads its config from anywhere else, such as stock herdr. set -euo pipefail repo="$(cd "$(dirname "$0")/../.." && pwd -P)" -herdr="${HERDR:-herdr}" +herdr="${HERDR:-tmuxrdr}" apply="${1:-}" plugin_id="tmuxrdr.tmux-mode" plugin_link="${XDG_DATA_HOME:-$HOME/.local/share}/tmuxrdr/plugin" -config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/herdr" +config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/tmuxrdr" config_link="$config_dir/config.toml" run() { @@ -54,7 +57,12 @@ link() { return 0 } -command -v "$herdr" >/dev/null || { >&2 echo "ERROR: no $herdr on PATH"; exit 1; } +command -v "$herdr" >/dev/null || { >&2 echo "ERROR: no $herdr; build it: tmuxrdr/scripts/build"; exit 1; } +reads="$("$herdr" --help 2>/dev/null | sed -n 's/^Config: *//p')" +[[ "$reads" == "$config_link" ]] || { + >&2 echo "ERROR: $herdr reads its config from ${reads:-?}, not $config_link: not a tmuxrdr build" + exit 1 +} command -v jq >/dev/null || { >&2 echo "ERROR: the plugin needs jq"; exit 1; } link "$repo/tmuxrdr/plugin" "$plugin_link" @@ -78,7 +86,7 @@ fi if [[ "$apply" == "yes" ]]; then "$herdr" config check - echo "Running herdr picks this up on prefix+r." + echo "A running tmuxrdr picks this up on prefix+r." else echo "Execute with: $0 yes" fi diff --git a/tmuxrdr/scripts/sync-upstream b/tmuxrdr/scripts/sync-upstream index 44c1b010..d73b05df 100755 --- a/tmuxrdr/scripts/sync-upstream +++ b/tmuxrdr/scripts/sync-upstream @@ -7,7 +7,8 @@ # tmuxrdr/scripts/sync-upstream v0.9.4 that release # # Releases, not upstream's master: a release is what Homebrew ships, so a -# tmuxrdr build speaks the same protocol as any stock herdr server around it. +# tmuxrdr build and the stock herdr beside it stay on the same version, and +# their CLIs can talk to each other's servers. # The base is the newest release tag behind HEAD; the tmuxrdr commits are # everything after it. The branch as it was is kept as tmuxrdr-on-<old tag>, # so a bad rebase costs nothing: `git switch -C tmuxrdr tmuxrdr-on-<old tag>`. @@ -62,11 +63,5 @@ fi echo echo "$branch is on $target; the old one is kept as $backup." -installed="$("${HERDR:-herdr}" --version 2>/dev/null | awk '{print "v" $2}')" -if [[ "$installed" != "$target" ]]; then - echo "herdr on PATH is ${installed:-missing}, not $target: test against $target" - echo "(upgrade it, or build this checkout -- README.md) before trusting test-e2e." -fi -HERDR_CONFIG_PATH="$PWD/tmuxrdr/config/config.toml" "${HERDR:-herdr}" config check \ - || echo "config check found issues against the herdr on PATH; see above" -echo "Next: tmuxrdr/scripts/test-e2e" +echo "Next: tmuxrdr/scripts/build, then tmuxrdr/scripts/test-e2e." +echo "(The installed tmuxrdr is still the $old build until build replaces it.)" diff --git a/tmuxrdr/scripts/test-e2e b/tmuxrdr/scripts/test-e2e index 5913afa8..7c82086e 100755 --- a/tmuxrdr/scripts/test-e2e +++ b/tmuxrdr/scripts/test-e2e @@ -13,7 +13,8 @@ # the throwaway pane -- printed, never run -- and is skipped when the # clipboard holds no text. # -# HERDR=path/to/herdr test another binary, e.g. a tmuxrdr build +# HERDR=path/to/binary test another tmuxrdr build than the installed +# tmuxrdr, e.g. target/release/herdr # TMUXRDR_E2E_DELAY=1.2 seconds after each key (default 0.8); raise it on # a loaded machine before suspecting a failure # @@ -22,7 +23,7 @@ set -uo pipefail repo="$(cd "$(dirname "$0")/../.." && pwd -P)" -herdr="${HERDR:-herdr}" +herdr="${HERDR:-tmuxrdr}" delay="${TMUXRDR_E2E_DELAY:-0.8}" session="tmuxrdr-e2e-$$" T=(tmux -L "tmuxrdr-e2e-$$" -f /dev/null) @@ -30,7 +31,7 @@ H=("$herdr" --session "$session") plugin_id="tmuxrdr.tmux-mode" work="$(mktemp -d)" cfg="$work/config.toml" -state="${XDG_STATE_HOME:-$HOME/.local/state}/herdr/plugins/$plugin_id/$session" +state="" # set once the binary's directory name is known, below pass=0 fail=0 skip=0 @@ -49,6 +50,11 @@ die() { >&2 echo "ERROR: $*"; exit 2; } for tool in tmux jq "$herdr"; do command -v "$tool" >/dev/null || die "$tool not found" done +# The binary's own directory name -- tmuxrdr for a tmuxrdr build -- from the +# config path it reports; plugin state lives under the same name. +app="$(basename "$(dirname "$("$herdr" --help | sed -n 's/^Config: *//p')")")" +[[ "$app" == tmuxrdr ]] || die "$herdr keeps its files under '$app', not tmuxrdr: not a tmuxrdr build" +state="${XDG_STATE_HOME:-$HOME/.local/state}/$app/plugins/$plugin_id/$session" "$herdr" plugin list --json 2>/dev/null \ | jq -e --arg id "$plugin_id" --arg root "$repo/tmuxrdr/plugin" \ '[.. | objects | select(.plugin_id? == $id and .plugin_root? == $root)] | length > 0' >/dev/null \ @@ -107,6 +113,9 @@ done [[ "$(focus 2>/dev/null)" == w1:p1 ]] || die "herdr client did not come up" echo "herdr $("$herdr" --version | awk '{print $2}'), session $session, config copy of tmuxrdr/config/config.toml" +check 'config check: no unknown keys or bad bindings' \ + "$(HERDR_CONFIG_PATH="$cfg" "$herdr" config check 2>&1 | head -n 1)" "config: ok" + # --- panes ------------------------------------------------------------------- k C-b %; check 'prefix+% split side by side' "$(tab_panes)" 2 |
