aboutsummaryrefslogtreecommitdiff
path: root/tmuxrdr/scripts
diff options
context:
space:
mode:
authorIgor Soarez <igor@soarez.org>2026-10-05 12:02:45 +0300
committerIgor Soarez <igor@soarez.org>2026-10-05 12:02:45 +0300
commit10fb1ab5ac9091e59957e97c3e75ea6bc288b8c5 (patch)
tree7adca7723f6f5e6d76540df9e9d0a1af3674803e /tmuxrdr/scripts
parent2312d3978b7d123968e834c0e0080ad2ff5559df (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-xtmuxrdr/scripts/build52
-rwxr-xr-xtmuxrdr/scripts/install28
-rwxr-xr-xtmuxrdr/scripts/sync-upstream13
-rwxr-xr-xtmuxrdr/scripts/test-e2e15
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