Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Download the archive for your platform from [GitHub Releases](https://github.com
| Variable | Description | Default |
|----------|-------------|---------|
| `FLASHDUTY_VERSION` | Install a specific version (e.g. `v1.5.12`) | latest |
| `FLASHDUTY_INSTALL_DIR` | Install directory | `/usr/local/bin` (shell), `~\.flashduty\bin` (PowerShell) |
| `FLASHDUTY_INSTALL_DIR` | Install directory | shell: the directory of the `flashduty` already on `PATH`, else `/usr/local/bin`; PowerShell: `~\.flashduty\bin` |
| `MIRROR_URL` | Release asset mirror (must be `https://`) | `https://static.flashcat.cloud/flashduty-cli` |

## Quick start
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ irm https://static.flashcat.cloud/flashduty-cli/install.ps1 | iex
| 变量 | 说明 | 默认值 |
|------|------|--------|
| `FLASHDUTY_VERSION` | 安装指定版本(如 `v1.5.12`) | 最新版 |
| `FLASHDUTY_INSTALL_DIR` | 安装目录 | `/usr/local/bin`(shell),`~\.flashduty\bin`(PowerShell) |
| `FLASHDUTY_INSTALL_DIR` | 安装目录 | shell:`PATH` 上已有 `flashduty` 所在目录,没有则 `/usr/local/bin`;PowerShell:`~\.flashduty\bin` |
| `MIRROR_URL` | 下载镜像地址(必须是 `https://`) | `https://static.flashcat.cloud/flashduty-cli` |

## 快速上手
Expand Down
34 changes: 26 additions & 8 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
#
# Environment:
# FLASHDUTY_VERSION Install a specific version (e.g. v0.1.2). Default: latest.
# FLASHDUTY_INSTALL_DIR Install directory. Default: /usr/local/bin.
# FLASHDUTY_INSTALL_DIR Install directory. Default: the directory of the
# flashduty already on PATH, else /usr/local/bin.
# INSTALLED_NAME Installed command name. Default: flashduty.
# MIRROR_URL Fetch release assets from this https mirror prefix.
# Default: https://static.flashcat.cloud/flashduty-cli.
Expand All @@ -18,7 +19,15 @@ set -e
REPO="flashcatcloud/flashduty-cli"
BINARY="flashduty-cli"
INSTALLED_NAME="${INSTALLED_NAME:-flashduty}"
INSTALL_DIR="${FLASHDUTY_INSTALL_DIR:-/usr/local/bin}"
# Default to the directory of the copy the shell already runs, so an upgrade
# replaces it instead of landing behind it on PATH. Fresh installs use
# /usr/local/bin.
existing=$(command -v "${INSTALLED_NAME}" 2>/dev/null || true)
case "${existing}" in
/*) default_dir=$(dirname -- "${existing}") ;;
*) default_dir="/usr/local/bin" ;;
esac
INSTALL_DIR="${FLASHDUTY_INSTALL_DIR:-${default_dir}}"

# By default release downloads are fetched from the Flashcat CDN. Set MIRROR_URL
# to another prefix to override, or to an empty string to force GitHub fallback.
Expand Down Expand Up @@ -230,6 +239,12 @@ EOF
# --- main ---

main() {
# flashduty-runner stages its own bundled copy; replacing it would desync the
# runner from the CLI it was built and verified against.
if [ "${FLASHDUTY_MANAGED_BY_RUNNER:-}" = "1" ]; then
fail "flashduty is managed by flashduty-runner; upgrade the runner instead"
fi

need_cmd uname
need_cmd tar
need_cmd curl
Expand Down Expand Up @@ -316,12 +331,15 @@ main() {

info "Installed to ${INSTALL_DIR}/${INSTALLED_NAME}"

# Warn if install dir is not in PATH
case ":${PATH}:" in
*":${INSTALL_DIR}:"*) ;;
*) info "WARNING: ${INSTALL_DIR} is not in your PATH. Add it with:"
info " export PATH=\"${INSTALL_DIR}:\$PATH\"" ;;
esac
# Warn unless the shell now runs the binary just installed.
active=$(command -v "${INSTALLED_NAME}" 2>/dev/null || true)
if [ -z "${active}" ]; then
info "WARNING: ${INSTALL_DIR} is not in your PATH. Add it with:"
info " export PATH=\"${INSTALL_DIR}:\$PATH\""
elif [ "$(dirname -- "${active}")" != "${INSTALL_DIR%/}" ]; then
info "WARNING: your shell runs ${active}, not this install."
info " Remove that copy, or put ${INSTALL_DIR} ahead of it on PATH."
fi

# Best-effort shell completion; never fail the install over it.
BIN="${INSTALL_DIR}/${INSTALLED_NAME}"
Expand Down
71 changes: 70 additions & 1 deletion tests/install_sh_sudo_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ run_install() {
home_dir="$1"
install_dir="$2"
out_file="$3"
env PATH="${FAKE_BIN}:$PATH" \
env PATH="${FAKE_BIN}:${EXTRA_PATH:+${EXTRA_PATH}:}$PATH" \
HOME="${home_dir}" \
SHELL=/bin/false \
FLASHDUTY_VERSION=v9.9.9 \
Expand Down Expand Up @@ -186,7 +186,76 @@ test_tty_prompts_for_interactive_sudo() {
assert_file_missing "${home_dir}/.local/bin/flashduty"
}

# A stale copy earlier on PATH: with no FLASHDUTY_INSTALL_DIR the upgrade must
# replace it in place rather than install a shadowed copy elsewhere.
make_existing_copy() {
mkdir -p "$1"
printf '#!/bin/sh\necho "old flashduty"\n' > "$1/flashduty"
chmod +x "$1/flashduty"
}

test_replaces_copy_already_on_path() {
SUDO_LOG="${TMP_DIR}/sudo-existing.log"
export SUDO_LOG
: > "${SUDO_LOG}"
home_dir="${TMP_DIR}/home-existing"
existing_dir="${TMP_DIR}/existing-bin"
out_file="${TMP_DIR}/existing.out"
mkdir -p "${home_dir}"
make_existing_copy "${existing_dir}"

# Trailing slash: dash's `command -v` then reports "<dir>//flashduty".
EXTRA_PATH="${existing_dir}/" run_install "${home_dir}" "" "${out_file}"

assert_contains "${out_file}" "Installed to ${existing_dir}/flashduty"
assert_contains "${existing_dir}/flashduty" "fake flashduty"
assert_file_missing "${home_dir}/.local/bin/flashduty"
if grep -Fq "WARNING" "${out_file}"; then
echo "unexpected warning:" >&2
cat "${out_file}" >&2
exit 1
fi
}

test_warns_when_copy_on_path_shadows_install() {
SUDO_LOG="${TMP_DIR}/sudo-shadow.log"
export SUDO_LOG
: > "${SUDO_LOG}"
home_dir="${TMP_DIR}/home-shadow"
existing_dir="${TMP_DIR}/shadow-bin"
out_file="${TMP_DIR}/shadow.out"
mkdir -p "${home_dir}"
make_existing_copy "${existing_dir}"
chmod 555 "${existing_dir}"

EXTRA_PATH="${existing_dir}" run_install "${home_dir}" "" "${out_file}"

assert_file_exists "${home_dir}/.local/bin/flashduty"
assert_contains "${existing_dir}/flashduty" "old flashduty"
assert_contains "${out_file}" "WARNING: your shell runs ${existing_dir}/flashduty, not this install."
}

test_refuses_runner_managed_copy() {
SUDO_LOG="${TMP_DIR}/sudo-runner.log"
export SUDO_LOG
: > "${SUDO_LOG}"
home_dir="${TMP_DIR}/home-runner"
install_dir="${TMP_DIR}/runner-bin"
out_file="${TMP_DIR}/runner.out"
mkdir -p "${home_dir}" "${install_dir}"

if FLASHDUTY_MANAGED_BY_RUNNER=1 run_install "${home_dir}" "${install_dir}" "${out_file}"; then
echo "expected install to fail when managed by flashduty-runner" >&2
exit 1
fi
assert_contains "${out_file}" "flashduty is managed by flashduty-runner; upgrade the runner instead"
assert_file_missing "${install_dir}/flashduty"
}

make_fixtures
make_fake_commands
test_non_tty_falls_back_to_user_bin
test_tty_prompts_for_interactive_sudo
test_replaces_copy_already_on_path
test_warns_when_copy_on_path_shadows_install
test_refuses_runner_managed_copy
Loading