diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index d00ff67..dc1286b 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -1702,6 +1702,17 @@ fn app_log(level: String, message: String) { #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { + // Headless `--version` / `-V`: print and exit BEFORE building the GUI, so installers and + // users can verify the binary without a window opening. resolve_db_target() ignores args + // starting with `-`, so without this the flag would be silently dropped and the app would + // launch normally instead. + if std::env::args() + .skip(1) + .any(|a| a == "--version" || a == "-V") + { + println!("red-request {}", env!("CARGO_PKG_VERSION")); + return; + } tauri::Builder::default() .plugin( tauri_plugin_log::Builder::new() diff --git a/install.sh b/install.sh index 43bfa65..a11f260 100755 --- a/install.sh +++ b/install.sh @@ -166,17 +166,18 @@ clear_appimage_shadow() { have update-desktop-database && update-desktop-database "$DESKTOP_DIR" >/dev/null 2>&1 || true } -# The .deb ships red-request/red/red-request-engine in /usr/bin but no `rr` -# alias (it was historically AppImage-only). Always (re)create it pointing at -# the system binary, and ensure $INSTALL_DIR is on PATH, so `rr` works the same -# for both build forms — including a fresh .deb install after an uninstall. +# The .deb ships red-request/red/red-request-engine in /usr/bin but no `rr` alias +# (historically AppImage-only). (Re)create it as a symlink in /usr/local/bin — that dir is +# on PATH for every shell (bash/zsh/fish) out of the box, so `rr` just works with no rc-file +# edits, unlike ~/.local/bin. Points at the system binary so `rr` tracks upgrades. ensure_deb_shortcut() { - local deb_bin; deb_bin="$(deb_binary_path)" + local deb_bin sudo="" dst="/usr/local/bin/$SHORTCUT" + deb_bin="$(deb_binary_path)" [[ -n "$deb_bin" ]] || { warn "could not locate the installed $BIN_NAME binary; skipping $SHORTCUT alias"; return 0; } - mkdir -p "$INSTALL_DIR" - ln -sf "$deb_bin" "$INSTALL_DIR/$SHORTCUT" - ok "linked $SHORTCUT → $deb_bin" - ensure_path + [[ $EUID -ne 0 ]] && have sudo && sudo="sudo" + $sudo mkdir -p /usr/local/bin && $sudo ln -sf "$deb_bin" "$dst" \ + && ok "linked $SHORTCUT → $deb_bin ($dst)" \ + || warn "could not create the $SHORTCUT symlink in /usr/local/bin" } install_deb() { @@ -211,6 +212,7 @@ install_deb() { ok "${cur:+upgraded to }Red Request $tag installed (.deb)" clear_appimage_shadow ensure_deb_shortcut + verify_install "$(deb_binary_path)" say "launch it from your app menu, or run: $BIN_NAME (or $SHORTCUT . to open the current folder)" } @@ -221,6 +223,7 @@ rc_file() { case "${SHELL##*/}" in zsh) printf '%s' "${ZDOTDIR:-$HOME}/.zshrc" ;; bash) [[ -f "$HOME/.bashrc" ]] && printf '%s' "$HOME/.bashrc" || printf '%s' "$HOME/.profile" ;; + fish) printf '%s' "${XDG_CONFIG_HOME:-$HOME/.config}/fish/config.fish" ;; *) printf '%s' "$HOME/.profile" ;; esac } @@ -232,12 +235,23 @@ ensure_path() { return 0 fi local rc; rc="$(rc_file)" + mkdir -p "$(dirname "$rc")" if [[ -f "$rc" ]] && grep -q '# >>> red-request >>>' "$rc"; then return 0; fi - { - printf '\n# >>> red-request >>>\n' - printf 'export PATH="%s:$PATH"\n' "$INSTALL_DIR" - printf '# <<< red-request <<<\n' - } >> "$rc" + # fish doesn't read .profile and uses its own PATH syntax — write the right one for the + # user's shell so the line actually takes effect (a bash `export` in config.fish is inert). + if [[ "${SHELL##*/}" == "fish" ]]; then + { + printf '\n# >>> red-request >>>\n' + printf 'fish_add_path %s\n' "$INSTALL_DIR" + printf '# <<< red-request <<<\n' + } >> "$rc" + else + { + printf '\n# >>> red-request >>>\n' + printf 'export PATH="%s:$PATH"\n' "$INSTALL_DIR" + printf '# <<< red-request <<<\n' + } >> "$rc" + fi ok "added $INSTALL_DIR to PATH in $rc — open a new shell or: source $rc" } @@ -292,6 +306,7 @@ install_appimage() { ensure_path ok "${cur:+upgraded to }Red Request $tag → $INSTALL_DIR/$BIN_NAME" + verify_install "$INSTALL_DIR/$BIN_NAME" say "run it: $BIN_NAME (or $SHORTCUT . to open the current folder as a project)" } @@ -308,6 +323,32 @@ install_gui() { return 0 } +# Post-install sanity check: print the binary versions so a broken sidecar (e.g. a glibc- +# incompatible `red`) surfaces here, at install time, instead of as a blank window on first +# launch. $1 is the red-request binary path (it may not be on PATH in this shell yet). +verify_install() { # red_request_path + local rr="$1" red v + say "verifying…" + if [[ -x "$rr" ]]; then + # --version is headless on current builds; `timeout` guards older builds that ignore the + # flag (it would otherwise launch the GUI and hang the installer). + v="$(timeout 10 "$rr" --version 2>/dev/null | head -n1 || true)" + [[ -n "$v" ]] && ok "$v" || warn "$BIN_NAME --version produced no output (build older than this installer expects)" + else + warn "$BIN_NAME not found at $rr" + fi + # The .deb installs a standalone /usr/bin/red; the AppImage bundles it (nothing on disk). + red="$(command -v red 2>/dev/null || true)" + if [[ -n "$red" ]]; then + if v="$("$red" --version 2>&1)"; then + ok "$v" + else + warn "the embedded reddb sidecar (red) failed to run: $v" + warn "the app would open to a blank screen — re-run with --force, or report: $red --version" + fi + fi +} + main() { detect_platform case "$OS" in diff --git a/uninstall.sh b/uninstall.sh index 6ecc973..8eae98d 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -19,25 +19,30 @@ DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/red-request" DESKTOP_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/applications" removed=0 +SUDO=""; [[ $EUID -ne 0 ]] && command -v sudo >/dev/null 2>&1 && SUDO="sudo" rm_path() { [[ -e "$1" || -L "$1" ]] && rm -rf "$1" && { echo "✓ removed $1"; removed=1; }; return 0; } # The `rr` shortcut — only if it points at our binary (don't clobber an unrelated `rr`). -# Match both forms install.sh creates: the AppImage's relative `red-request` and the -# absolute `/usr/bin/red-request` the .deb path re-points it to via clear_appimage_shadow. -if [[ -L "$INSTALL_DIR/$SHORTCUT" ]]; then - rr_tgt="$(readlink "$INSTALL_DIR/$SHORTCUT")" - if [[ "$rr_tgt" == "$BIN_NAME" || "${rr_tgt##*/}" == "$BIN_NAME" ]]; then - rm_path "$INSTALL_DIR/$SHORTCUT" +# install.sh creates it in ~/.local/bin (AppImage) or /usr/local/bin (.deb); the target is +# the AppImage's relative `red-request` or the absolute `/usr/bin/red-request` (.deb). +for sc in "$INSTALL_DIR/$SHORTCUT" "/usr/local/bin/$SHORTCUT"; do + [[ -L "$sc" ]] || continue + rr_tgt="$(readlink "$sc")" + [[ "$rr_tgt" == "$BIN_NAME" || "${rr_tgt##*/}" == "$BIN_NAME" ]] || continue + if [[ "$sc" == /usr/local/* ]]; then + $SUDO rm -f "$sc" && { echo "✓ removed $sc"; removed=1; } + else + rm_path "$sc" fi -fi +done rm_path "$INSTALL_DIR/$BIN_NAME" rm_path "$INSTALL_DIR/$BIN_NAME.new" # stray temp from an interrupted upgrade rm_path "$DATA_DIR" rm_path "$DESKTOP_DIR/red-request.desktop" command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$DESKTOP_DIR" >/dev/null 2>&1 || true -# Strip the PATH block install.sh appended, from whichever rc has it. -for rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.profile"; do +# Strip the PATH block install.sh appended, from whichever rc has it (incl. fish). +for rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.profile" "${XDG_CONFIG_HOME:-$HOME/.config}/fish/config.fish"; do [[ -f "$rc" ]] || continue if grep -q '# >>> red-request >>>' "$rc"; then tmp="$(mktemp)" @@ -51,8 +56,7 @@ done # deps (libwebkit2gtk-4.1-0, libgtk-3-0, …) other apps may rely on. We only remove # the red-request package itself; its dependencies stay installed. if command -v dpkg >/dev/null 2>&1 && dpkg -s "$BIN_NAME" >/dev/null 2>&1; then - sudo=""; [[ $EUID -ne 0 ]] && command -v sudo >/dev/null 2>&1 && sudo="sudo" - if $sudo apt-get purge -y "$BIN_NAME"; then echo "✓ removed apt package $BIN_NAME"; removed=1; fi + if $SUDO apt-get purge -y "$BIN_NAME"; then echo "✓ removed apt package $BIN_NAME"; removed=1; fi fi if [[ "$removed" == "1" ]]; then