-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsetup.sh
More file actions
executable file
·357 lines (307 loc) · 10.9 KB
/
setup.sh
File metadata and controls
executable file
·357 lines (307 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#!/usr/bin/env bash
# 一键配置脚本 - 在干净的 Linux 系统上部署完整的开发环境
# 使用方法:git clone 后直接运行 ./setup.sh
#
# 用法:
# ./setup.sh 部署配置 + 安装工具(默认)
# ./setup.sh --deploy 只部署配置文件
# ./setup.sh --install 只安装开发工具(配置已部署时)
set -exo pipefail
# --------- helpers ---------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
function sudo_run() {
if [ "$EUID" -eq 0 ]; then
"$@"
else
sudo "$@"
fi
}
# 通用重试函数
# 用法: retry_fn <max_retries> <description> <function_name> [args...]
retry_fn() {
local max_retries=${1:-5}
local description=$2
local fn=$3
shift 3
local retry=0
while [ $retry -lt $max_retries ]; do
if $fn "$@"; then
return 0
fi
retry=$((retry + 1))
echo "${description}失败,重试 $retry/$max_retries..."
sleep 5
done
echo "错误: ${description}失败,已达最大重试次数"
return 1
}
ensure_python3() {
if command -v python3 >/dev/null 2>&1; then
return 0
fi
if [[ "$(uname -s)" == "Darwin" ]]; then
if command -v brew >/dev/null 2>&1; then
brew install python3
else
echo "Python3 未安装,请先安装 Python3(推荐 Homebrew: brew install python3)"
return 1
fi
else
sudo_run apt-get update
sudo_run apt-get install -y python3
fi
}
download_xdotter() {
local version="${XDOTTER_VERSION:-v0.4.3}"
local arch
arch=$(uname -m)
local os
os=$(uname -s)
local binary_name
if [[ "$os" == "Darwin" ]]; then
binary_name="xd-${arch}-apple-darwin"
else
binary_name="xd-${arch}-unknown-linux-gnu"
fi
local url="https://github.com/cncsmonster/xdotter/releases/download/${version}/${binary_name}"
local dest="/tmp/${binary_name}"
echo "尝试下载 xdotter 预编译二进制: ${binary_name}"
if ! curl -sSL --retry 3 --retry-delay 2 \
--connect-timeout 30 --max-time 120 \
"$url" -o "$dest" 2>/dev/null; then
echo "⚠️ 下载失败,回退到 cargo install"
if ! command -v cargo >/dev/null 2>&1; then
echo "❌ cargo 未安装,无法从源码编译 xdotter"
echo " 推荐:安装 Rust 后重试,或手动下载预编译二进制"
return 1
fi
cargo install --git https://github.com/cncsmonster/xdotter.git --tag "${version}" --locked
return $?
fi
# 检查下载的是否为有效二进制文件(而非 GitHub 404 HTML 页面)
if ! file "$dest" | grep -qE 'ELF|Mach-O|executable'; then
echo "⚠️ 下载的不是有效二进制文件(可能是 404 页面),回退到 cargo install"
rm -f "$dest"
if ! command -v cargo >/dev/null 2>&1; then
echo "❌ cargo 未安装,无法从源码编译 xdotter"
echo " 推荐:安装 Rust 后重试,或手动下载预编译二进制"
return 1
fi
cargo install --git https://github.com/cncsmonster/xdotter.git --tag "${version}" --locked
return $?
fi
# SHA256 校验(仅 Linux x86_64,其他架构暂无)
local EXPECTED_SHA256=""
if [[ "$os" != "Darwin" ]]; then
case "$arch" in
x86_64) EXPECTED_SHA256="6334cdf31d7bf9a0ef35bb358ae425a0de64ca308bcce1cd5ac7af88b4dfb3fc" ;;
*) echo "⚠️ 架构 $arch 无 SHA256 记录,跳过校验";;
esac
fi
if [ -n "$EXPECTED_SHA256" ]; then
local ACTUAL_SHA256
ACTUAL_SHA256=$(sha256sum "$dest" | awk '{print $1}')
if [ "$ACTUAL_SHA256" != "$EXPECTED_SHA256" ]; then
echo "❌ xdotter SHA256 校验失败!"
echo " 期望: $EXPECTED_SHA256"
echo " 实际: $ACTUAL_SHA256"
rm -f "$dest"
return 1
fi
fi
mv "$dest" ~/.cargo/bin/xd
chmod +x ~/.cargo/bin/xd
echo "✅ xdotter 预编译二进制安装成功"
}
deploy_dotfiles(){
mkdir -p ~/.cargo/bin
# 确保 cargo 可用,以便 xdotter 下载失败时能 fallback 到 cargo install
if ! command -v cargo >/dev/null 2>&1; then
echo "🔧 xdotter 需要 Rust 作为后备安装方式,正在安装最小 Rust 工具链..."
if [[ "$(uname -s)" != "Darwin" ]]; then
export DEBIAN_FRONTEND=noninteractive
export TZ=Asia/Shanghai
fi
load_install_functions
setup-rustup
fi
retry_fn 3 "下载 xdotter" download_xdotter
cd "${SCRIPT_DIR}" && ~/.cargo/bin/xd deploy --quiet --force
# 部署字体配置后刷新缓存
install-fonts
# CI 环境自适应:GitHub Actions runner 位于国外,rsproxy.cn 反而慢
# 检测 CI 环境,覆盖 cargo 配置直连 crates.io
apply_cargo_mirror_override
}
# ========== 字体安装函数 ==========
# 安装 wezterm 配置的字体及 Nerd Font 图标支持
install-fonts() {
echo "=========================================="
echo "安装字体..."
echo "=========================================="
if [[ "$(uname -s)" == "Darwin" ]]; then
# macOS: 使用 Homebrew 安装字体
if ! command -v brew >/dev/null 2>&1; then
echo "⚠️ Homebrew 未安装,跳过字体安装"
return 0
fi
echo "使用 Homebrew 安装字体..."
brew install --cask \
font-jetbrains-mono \
font-fira-code \
font-fira-code-nerd-font \
font-noto-sans-cjk \
font-noto-color-emoji \
2>/dev/null || echo "⚠️ 部分字体可能已安装,继续执行..."
echo "✅ 字体安装完成(macOS/Homebrew)"
else
# Linux: 通过 apt 安装 fontconfig(提供 fc-list/fc-cache)和字体包
sudo_run apt-get update
sudo_run apt-get install -y --no-install-recommends \
fontconfig \
fonts-noto-cjk \
fonts-noto-color-emoji \
fonts-jetbrains-mono \
fonts-dejavu-core
# 安装 FiraCode Nerd Font(带图标支持,apt 无此包,从 GitHub 下载)
if [ -x "$(command -v fc-list)" ] && fc-list | grep -qi "FiraCode.*Nerd"; then
echo "FiraCode Nerd Font 已安装,跳过"
else
echo "安装 FiraCode Nerd Font..."
local FIRACODE_VERSION="7.0.0"
local FIRACODE_URL="https://github.com/ryanoasis/nerd-fonts/releases/download/v${FIRACODE_VERSION}/FiraCode.zip"
local FIRACODE_DEST="/tmp/FiraCode-Nerd-Font.zip"
echo "下载 FiraCode Nerd Font..."
if wget --tries=3 --timeout=30 --connect-timeout=15 "$FIRACODE_URL" -O "$FIRACODE_DEST" 2>/dev/null; then
local FIRACODE_DIR="/usr/local/share/fonts/FiraCode-Nerd-Font"
sudo_run mkdir -p "$FIRACODE_DIR"
# 解压到系统字体目录
if command -v unzip >/dev/null 2>&1; then
sudo_run unzip -o "$FIRACODE_DEST" -d "$FIRACODE_DIR"
else
sudo_run apt-get install -y unzip
sudo_run unzip -o "$FIRACODE_DEST" -d "$FIRACODE_DIR"
fi
rm -f "$FIRACODE_DEST"
echo "FiraCode Nerd Font 安装完成"
else
echo "⚠️ FiraCode Nerd Font 下载失败,跳过"
fi
fi
# 刷新字体缓存
if [ -x "$(command -v fc-cache)" ]; then
echo "刷新字体缓存..."
sudo_run fc-cache -f
fi
echo "✅ 字体安装完成"
fi
}
apply_cargo_mirror_override() {
# 检测是否在 GitHub Actions CI 环境
if [ -z "$GITHUB_ACTIONS" ] && [ -z "$CI" ]; then
return 0 # 非 CI 环境,无需覆盖
fi
echo "🌐 检测到 CI 环境,配置 cargo 直连 crates.io(跳过 rsproxy)"
# 生成覆盖配置到 ~/.cargo/config.toml(替换 xdotter 创建的 symlink)
local cargo_config="$HOME/.cargo/config.toml"
local original_target
# macOS readlink 不支持 -f,用 realpath 或手动处理
if command -v realpath >/dev/null 2>&1; then
original_target=$(realpath "$cargo_config" 2>/dev/null || echo "")
else
original_target=$(readlink -f "$cargo_config" 2>/dev/null || echo "")
fi
if [ -n "$original_target" ]; then
# 读取原配置内容,修改 source 部分
# 使用 sed 注释掉 replace-with 行
sed 's/^replace-with = "rsproxy-sparse"/# replace-with = "rsproxy-sparse" # disabled in CI/' \
"$original_target" > "$cargo_config.tmp" && \
mv "$cargo_config.tmp" "$cargo_config"
echo "✅ Cargo 配置已切换为 CI 模式(直连 crates.io)"
else
echo "⚠️ 未找到 ~/.cargo/config.toml,跳过 cargo 配置覆盖"
fi
}
load_install_functions() {
source "${SCRIPT_DIR}/shells/common/env.sh"
source "${SCRIPT_DIR}/shells/common/fn.sh"
source "${SCRIPT_DIR}/shells/common/install-functions.sh"
}
# ========== 第一部分:部署配置 ==========
do_deploy() {
if [[ "$(uname -s)" != "Darwin" ]]; then
export DEBIAN_FRONTEND=noninteractive
export TZ=Asia/Shanghai
fi
echo "=========================================="
echo "部署 dotfiles 配置..."
echo "=========================================="
deploy_dotfiles
echo "✅ 配置部署完成"
}
# ========== 第二部分:安装工具 ==========
do_install() {
if [[ "$(uname -s)" != "Darwin" ]]; then
export DEBIAN_FRONTEND=noninteractive
export TZ=Asia/Shanghai
fi
load_install_functions
echo "=========================================="
echo "安装开发工具..."
echo "=========================================="
install-common-tools
# Rust 工具链(第一公民)
retry_fn 5 "安装 Rust" install-rust stable
# Rust 工具安装
if ! retry_fn 3 "安装 Rust 工具" install-common-rust-tools; then
if [ "${CARGO_INSTALL_STRICT:-0}" = "1" ]; then
echo "❌ 错误:CARGO_INSTALL_STRICT=1,Rust 工具安装失败,终止脚本"
exit 1
fi
echo "⚠️ 警告:Rust 工具安装失败,继续执行后续步骤..."
fi
retry_fn 3 "安装 cargo-fuzz" setup-cargo-fuzz
# 其他开发工具
retry_fn 3 "安装 Neovim" install-neovim
retry_fn 3 "安装 Helix" install-helix
retry_fn 3 "安装 Helix Runtime" install-helix-runtime
retry_fn 3 "安装 marksman" install-marksman
retry_fn 3 "安装 yq" install-yq
# LLVM 仅 Linux(llvmup 使用 apt-get,macOS 用 Homebrew 管理)
if [[ "$(uname -s)" != "Darwin" ]]; then
llvmup default 22
else
echo "macOS: 跳过 llvmup(LLVM 由 Homebrew 提供)"
fi
# 使用 mise 安装 go, zig, node, pnpm 等工具
mise trust && mise install
eval "$(mise hook-env -s $SH)" 2>/dev/null || true
retry_fn 3 "安装 Helix LSP" install-helix-lsp
# 安装 Zellij 终端复用器
retry_fn 3 "安装 Zellij" install-zellij
retry_fn 3 "安装 Yazi 插件" install-yazi-plugins
echo ""
echo "=========================================="
echo "✅ 所有工具安装完成"
echo "=========================================="
}
# ========== 入口 ==========
main() {
if [[ $# -eq 0 ]]; then
do_deploy
do_install
else
case "$1" in
--deploy) do_deploy ;;
--install) do_install ;;
*)
echo "用法: $0 [--deploy|--install]"
echo " 无参数 部署配置 + 安装工具(默认)"
echo " --deploy 只部署配置文件"
echo " --install 只安装开发工具"
exit 1
;;
esac
fi
}
main "$@"