|
| 1 | +#!/bin/sh |
| 2 | +# mp2rss-cli installer. |
| 3 | +# |
| 4 | +# curl -fsSL https://mp2rss.com/install.sh | sh |
| 5 | +# curl -fsSL https://mp2rss.com/install.sh | INSTALL_DIR=$HOME/bin sh |
| 6 | +# curl -fsSL https://mp2rss.com/install.sh | VERSION=v0.2.0 NO_VERIFY=1 sh |
| 7 | +# |
| 8 | +# POSIX sh (compatible with bash 3.2, dash, busybox ash). shellcheck clean. |
| 9 | +set -eu |
| 10 | + |
| 11 | +# ----- knobs (env overrides) ----- |
| 12 | +: "${REPO:=areyoubugcoder/mp2rss-cli}" |
| 13 | +: "${BINARY:=mp2rss}" |
| 14 | +: "${INSTALL_DIR:=}" |
| 15 | +: "${VERSION:=}" |
| 16 | +: "${NO_VERIFY:=}" |
| 17 | + |
| 18 | +# ----- pretty output ----- |
| 19 | +red() { printf '\033[31m%s\033[0m\n' "$*" >&2; } |
| 20 | +green() { printf '\033[32m%s\033[0m\n' "$*"; } |
| 21 | +yellow() { printf '\033[33m%s\033[0m\n' "$*"; } |
| 22 | +say() { printf '%s\n' "$*"; } |
| 23 | + |
| 24 | +die() { |
| 25 | + red "✗ $*" |
| 26 | + exit 1 |
| 27 | +} |
| 28 | + |
| 29 | +# ----- platform detection ----- |
| 30 | +detect_os() { |
| 31 | + os=$(uname -s 2>/dev/null || echo unknown) |
| 32 | + case "$os" in |
| 33 | + Darwin) echo "darwin" ;; |
| 34 | + Linux) echo "linux" ;; |
| 35 | + MINGW*|MSYS*|CYGWIN*) echo "windows" ;; |
| 36 | + *) die "暂不支持的操作系统:$os" ;; |
| 37 | + esac |
| 38 | +} |
| 39 | + |
| 40 | +detect_arch() { |
| 41 | + arch=$(uname -m 2>/dev/null || echo unknown) |
| 42 | + case "$arch" in |
| 43 | + x86_64|amd64) echo "amd64" ;; |
| 44 | + aarch64|arm64) echo "arm64" ;; |
| 45 | + *) die "暂不支持的 CPU 架构:$arch" ;; |
| 46 | + esac |
| 47 | +} |
| 48 | + |
| 49 | +# ----- dependency checks ----- |
| 50 | +need() { |
| 51 | + command -v "$1" >/dev/null 2>&1 || die "找不到必需命令:$1(请先安装)" |
| 52 | +} |
| 53 | + |
| 54 | +# need exactly one of (curl, wget); prefer curl |
| 55 | +download() { |
| 56 | + url=$1 |
| 57 | + dst=$2 |
| 58 | + if command -v curl >/dev/null 2>&1; then |
| 59 | + curl -fsSL -o "$dst" "$url" || die "下载失败:$url" |
| 60 | + elif command -v wget >/dev/null 2>&1; then |
| 61 | + wget -q -O "$dst" "$url" || die "下载失败:$url" |
| 62 | + else |
| 63 | + die "需要 curl 或 wget" |
| 64 | + fi |
| 65 | +} |
| 66 | + |
| 67 | +# ----- find a writable install dir ----- |
| 68 | +pick_install_dir() { |
| 69 | + if [ -n "$INSTALL_DIR" ]; then |
| 70 | + echo "$INSTALL_DIR" |
| 71 | + return |
| 72 | + fi |
| 73 | + # prefer ~/.local/bin (no sudo) |
| 74 | + if [ -d "$HOME/.local/bin" ] || mkdir -p "$HOME/.local/bin" 2>/dev/null; then |
| 75 | + if [ -w "$HOME/.local/bin" ]; then |
| 76 | + echo "$HOME/.local/bin" |
| 77 | + return |
| 78 | + fi |
| 79 | + fi |
| 80 | + # fallback ~/bin |
| 81 | + if [ -d "$HOME/bin" ] && [ -w "$HOME/bin" ]; then |
| 82 | + echo "$HOME/bin" |
| 83 | + return |
| 84 | + fi |
| 85 | + # last resort /usr/local/bin (likely needs sudo) |
| 86 | + echo "/usr/local/bin" |
| 87 | +} |
| 88 | + |
| 89 | +# ----- main flow ----- |
| 90 | +goos=$(detect_os) |
| 91 | +goarch=$(detect_arch) |
| 92 | + |
| 93 | +if [ "$goos" = "windows" ]; then |
| 94 | + die "Windows 请使用 npm 包装:npm install -g @mp2rss/cli" |
| 95 | +fi |
| 96 | + |
| 97 | +api="https://api.github.com/repos/$REPO/releases/latest" |
| 98 | + |
| 99 | +if [ -z "$VERSION" ]; then |
| 100 | + need uname |
| 101 | + say "🔎 查询 $REPO 最新版本…" |
| 102 | + tmp_json=$(mktemp) |
| 103 | + trap 'rm -f "$tmp_json"' EXIT INT TERM |
| 104 | + download "$api" "$tmp_json" |
| 105 | + # parse tag_name without jq |
| 106 | + VERSION=$(sed -n 's/^[[:space:]]*"tag_name"[[:space:]]*:[[:space:]]*"\(v[^"]*\)".*/\1/p' "$tmp_json" | head -n 1) |
| 107 | + rm -f "$tmp_json" |
| 108 | + trap - EXIT INT TERM |
| 109 | + [ -n "$VERSION" ] || die "解析 latest release 失败,请检查网络或手动指定 VERSION=v..." |
| 110 | +fi |
| 111 | + |
| 112 | +semver=${VERSION#v} |
| 113 | +asset="mp2rss-cli_${semver}_${goos}_${goarch}.tar.gz" |
| 114 | +url="https://github.com/$REPO/releases/download/$VERSION/$asset" |
| 115 | +checksums_url="https://github.com/$REPO/releases/download/$VERSION/checksums.txt" |
| 116 | + |
| 117 | +say "📦 平台 ${goos}/${goarch},目标版本 ${VERSION}" |
| 118 | + |
| 119 | +# Download into temp dir |
| 120 | +tmpdir=$(mktemp -d) |
| 121 | +trap 'rm -rf "$tmpdir"' EXIT INT TERM |
| 122 | + |
| 123 | +say " ↓ 下载 $asset" |
| 124 | +download "$url" "$tmpdir/$asset" |
| 125 | + |
| 126 | +if [ -z "$NO_VERIFY" ]; then |
| 127 | + say " 🔐 校验 SHA-256" |
| 128 | + download "$checksums_url" "$tmpdir/checksums.txt" |
| 129 | + expected=$(awk -v a="$asset" '{ |
| 130 | + name=$2; sub(/^\*/, "", name); |
| 131 | + if (name == a) { print $1; exit } |
| 132 | + }' "$tmpdir/checksums.txt") |
| 133 | + [ -n "$expected" ] || die "checksums.txt 中找不到 $asset" |
| 134 | + |
| 135 | + if command -v sha256sum >/dev/null 2>&1; then |
| 136 | + actual=$(sha256sum "$tmpdir/$asset" | awk '{print $1}') |
| 137 | + elif command -v shasum >/dev/null 2>&1; then |
| 138 | + actual=$(shasum -a 256 "$tmpdir/$asset" | awk '{print $1}') |
| 139 | + else |
| 140 | + yellow "⚠ 未找到 sha256sum / shasum,跳过校验(设 NO_VERIFY=1 也可显式跳过)" |
| 141 | + actual=$expected |
| 142 | + fi |
| 143 | + [ "$actual" = "$expected" ] || die "checksum 不匹配:expected $expected, got $actual" |
| 144 | +fi |
| 145 | + |
| 146 | +say " 📂 解压" |
| 147 | +need tar |
| 148 | +tar -xzf "$tmpdir/$asset" -C "$tmpdir" "$BINARY" |
| 149 | +[ -f "$tmpdir/$BINARY" ] || die "归档中未找到 $BINARY 可执行文件" |
| 150 | +chmod 755 "$tmpdir/$BINARY" |
| 151 | + |
| 152 | +install_dir=$(pick_install_dir) |
| 153 | +mkdir -p "$install_dir" 2>/dev/null || true |
| 154 | + |
| 155 | +target="$install_dir/$BINARY" |
| 156 | + |
| 157 | +if [ -w "$install_dir" ]; then |
| 158 | + mv "$tmpdir/$BINARY" "$target" |
| 159 | +else |
| 160 | + yellow " ⚠ $install_dir 不可写,使用 sudo 安装。" |
| 161 | + sudo mv "$tmpdir/$BINARY" "$target" || die "sudo 安装失败,请改用 INSTALL_DIR=\$HOME/.local/bin 重试" |
| 162 | +fi |
| 163 | + |
| 164 | +say "" |
| 165 | +green "✓ 已安装到 $target" |
| 166 | + |
| 167 | +# self-check (skip if PATH doesn't include it yet — still surface success) |
| 168 | +if "$target" --version >/dev/null 2>&1; then |
| 169 | + ver=$("$target" --version 2>&1 | head -n 1) |
| 170 | + green " $ver" |
| 171 | +else |
| 172 | + yellow " ⚠ 自检 \"$target --version\" 失败,请手动确认" |
| 173 | +fi |
| 174 | + |
| 175 | +# PATH hint |
| 176 | +case ":$PATH:" in |
| 177 | + *":$install_dir:"*) |
| 178 | + : # already in PATH |
| 179 | + ;; |
| 180 | + *) |
| 181 | + say "" |
| 182 | + yellow "提示:$install_dir 不在 \$PATH 中。请把以下行加入 ~/.bashrc 或 ~/.zshrc:" |
| 183 | + say " export PATH=\"$install_dir:\$PATH\"" |
| 184 | + ;; |
| 185 | +esac |
| 186 | + |
| 187 | +say "" |
| 188 | +say "下一步:mp2rss auth login" |
0 commit comments