From 91da94de81c38c982b32190de04241b2c1de98eb Mon Sep 17 00:00:00 2001 From: Dom Date: Thu, 3 Sep 2026 19:41:50 -0300 Subject: [PATCH] Offer to link the launchers where sudo can see them sudo replaces PATH with secure_path, which excludes ~/.local/bin and includes /usr/local/bin. Linking the launcher there is what makes 'sudo suplemon' work by name rather than by full path. install.sh prints the command instead of running it: nothing else in the installer needs a password, and asking for one to do something optional would be worse than showing the line. It skips the offer when something that is not ours already holds either name, since a stream editor called 'se' exists and replacing it silently would be rude. Links rather than copies, so re-running the installer keeps them current. uninstall.sh reports them and gives the removal command without ever touching them, for the same no-password reason. Also corrects the README on when upstream stopped. The last change of substance was 11 December 2019; January 2021 was two edits to a README image URL. --- CHANGELOG.md | 23 +++++++++++++++++++++++ README.md | 33 ++++++++++++++++++++++++++------- install.sh | 32 ++++++++++++++++++++++++++++++++ uninstall.sh | 18 ++++++++++++++++++ 4 files changed, 99 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94965e1..d957bb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ Change Log ========== +## Unreleased + +Repository and `curl | sh` installs only; the PyPI package is unaffected. + +**Added** + +- `install.sh` offers a command to link the launchers into + `/usr/local/bin`, which is on sudo's `secure_path` where + `~/.local/bin` is not. It prints the command rather than running it, + since nothing else in the installer needs a password, and it skips the + offer if something that is not ours already holds either name. +- `uninstall.sh` reports those links and gives the command to remove + them. It never touches them itself. +- Both scripts honour `SYS_BIN_DIR` for non-standard prefixes. + +**Documentation** + +- The README describes the `/usr/local/bin` option alongside the + full-path and `sudoedit` approaches. +- Corrected the account of when upstream stopped: the last substantive + change to `master` was 11 December 2019, not January 2021, which was + two README image URL edits. + ## [v0.3.3](https://github.com/leancode/suplemon/tree/0.3.3) (2026-09-03) **Fixed** diff --git a/README.md b/README.md index a56a45d..d2f7da2 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,11 @@ https://github.com/leancode/suplemon This is an actively maintained fork of [richrd/suplemon](https://github.com/richrd/suplemon). The original is a genuinely good editor and none of the design here is ours. -Upstream simply stopped: the last commit to `master` landed in January 2021, -the `dev` branch in June 2020, and 25 issues and 5 pull requests are still -open, some since 2016. Suplemon had also stopped running altogether on -Python 3.12 and newer, which is what prompted the fork. +Upstream simply stopped. The last change of any substance to `master` was on +11 December 2019; the only two commits since were README image URL edits in +January 2021. The `dev` branch last moved in June 2020. 25 issues and 5 pull +requests are still open, some since 2016. Suplemon had also stopped running +altogether on Python 3.12 and newer, which is what prompted the fork. Since then this fork has: @@ -155,13 +156,31 @@ That works because the launcher hard-codes an absolute path to the source tree. `$HOME` is root's under sudo, so a launcher written in terms of `$HOME` would look for the interpreter in the wrong place. -If you do this often, either add `~/.local/bin` to `secure_path` in -`visudo`, or use `sudoedit`, which copies the file to a temporary location, +If you do this often, link the launcher somewhere sudo already looks. +`/usr/local/bin` is on `secure_path` on every system I know of, so this makes +`sudo suplemon` work by name: + + sudo ln -sf ~/.local/bin/suplemon /usr/local/bin/suplemon && sudo ln -sf ~/.local/bin/suplemon /usr/local/bin/se + +`install.sh` prints this command for you, and skips it if something that is +not ours already sits at either name — a stream editor called `se` exists, and +silently replacing it would be rude. + +Links rather than copies, so re-running `install.sh` keeps them current; a +copy would quietly go stale. The launcher holds an absolute path to your own +source tree, so these run your checkout whoever invokes them: fine on a +single-user machine, probably not on a shared one. `uninstall.sh` will not +remove them, since it only touches `~/.local`, but it does tell you they are +there. + +The alternative is `sudoedit`, which copies the file to a temporary location, opens it as you rather than as root, and copies it back: SUDO_EDITOR=~/.local/bin/suplemon sudoedit /etc/hosts -`sudoedit` is the safer of the two: the editor never runs as root. +`sudoedit` is the safest of the three: the editor never runs as root at all, +so nothing it loads — modules, your config, a theme — is running with +privileges. ### Notes diff --git a/install.sh b/install.sh index 133988c..d17e545 100755 --- a/install.sh +++ b/install.sh @@ -11,6 +11,8 @@ set -eu REPO_URL="https://github.com/leancode/suplemon.git" SRC_DIR="$HOME/.local/src/suplemon" BIN_DIR="$HOME/.local/bin" +# Where sudo can see it. Overridable for testing and odd prefixes. +SYS_BIN_DIR="${SYS_BIN_DIR:-/usr/local/bin}" LAUNCHER="$BIN_DIR/suplemon" SHORTCUT="$BIN_DIR/se" PY_MIN="3.8" @@ -221,6 +223,36 @@ LAUNCHER_EOF [ -L "$SHORTCUT" ] && info " or: se [file]" info "Press F1 inside the editor for help, Ctrl+Q to quit." + # Optional, and deliberately not done for you: this script installs + # entirely inside $HOME and never asks for a password. sudo replaces + # PATH with secure_path, which excludes ~/.local/bin but includes + # /usr/local/bin, so a copy there is what makes "sudo suplemon" work + # by name rather than by full path. + # Symlinks rather than copies, so they keep tracking the launcher when + # this script is re-run. Never suggest clobbering an "se" that is not + # ours: a stream editor of that name exists and could be installed. + usrlocal_ok=1 + for f in "$SYS_BIN_DIR/suplemon" "$SYS_BIN_DIR/se"; do + if [ -e "$f" ] || [ -L "$f" ]; then + if ! grep -q 'SUPLEMON_HOME' "$f" 2>/dev/null && + [ "$(readlink "$f" 2>/dev/null)" != "$LAUNCHER" ]; then + warn "$f exists and is not ours; leaving it alone" + usrlocal_ok=0 + fi + fi + done + if [ "$usrlocal_ok" = "1" ] && { [ ! -e "$SYS_BIN_DIR/suplemon" ] || [ ! -e "$SYS_BIN_DIR/se" ]; }; then + printf '\n%sOptional.%s sudo replaces PATH with its own secure_path, which\n' "$B" "$N" + info "excludes $BIN_DIR. To use it with sudo, link the launcher" + info "into $SYS_BIN_DIR. This is the only step that needs a password:" + printf '\n %ssudo ln -sf %s %s/suplemon && sudo ln -sf %s %s/se%s\n' \ + "$B" "$LAUNCHER" "$SYS_BIN_DIR" "$LAUNCHER" "$SYS_BIN_DIR" "$N" + info "Links, not copies, so re-running this script keeps them current." + info "They run your source tree, so keep them to machines you alone use." + elif [ "$usrlocal_ok" = "1" ]; then + ok "Linked into $SYS_BIN_DIR, so sudo can find it" + fi + # Anything the user still has to do goes last, so it is the final thing # on screen rather than something scrolled away by later output. This # script cannot set PATH for you: it runs in its own process, and a diff --git a/uninstall.sh b/uninstall.sh index e6b4022..82f6e21 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -15,6 +15,8 @@ set -eu SRC_DIR="$HOME/.local/src/suplemon" BIN_DIR="$HOME/.local/bin" +# Where the optional sudo-visible links live. Overridable for testing. +SYS_BIN_DIR="${SYS_BIN_DIR:-/usr/local/bin}" LAUNCHER="$BIN_DIR/suplemon" SHORTCUT="$BIN_DIR/se" CONFIG_DIR="$HOME/.config/suplemon" @@ -155,6 +157,22 @@ main() { #################################################################### step "Left in place on purpose" info "$BIN_DIR, because other programs live there" + # The README suggests linking the launchers into /usr/local/bin so sudo + # can find them. Those are root-owned and outside $HOME, and this script + # never asks for a password, so they are reported and left alone. Only + # ours are named: anything else at those paths is someone else's. + found="" + for f in "$SYS_BIN_DIR/suplemon" "$SYS_BIN_DIR/se"; do + if [ -L "$f" ] && [ "$(readlink "$f" 2>/dev/null)" = "$LAUNCHER" ]; then + found="$found $f" + elif [ -f "$f" ] && grep -q 'SUPLEMON_HOME' "$f" 2>/dev/null; then + found="$found $f" + fi + done + if [ -n "$found" ]; then + info "These, which need root to remove. Nothing here touches them:" + printf ' %ssudo rm -f%s%s\n' "$B" "$found" "$N" + fi for f in "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.profile"; do if [ -f "$f" ] && grep -q 'Added by the Suplemon installer' "$f" 2>/dev/null; then info "The PATH block in $f, since $BIN_DIR stays"