Skip to content

Commit 73f1582

Browse files
authored
fix(install): add shell uv fallback for cn installer (#136)
Add a mainland China shell fallback for uv in the zh installers and cover the new path with script tests. This keeps the bash installer aligned with the localized install flow without including unrelated lockfile changes.
1 parent a282673 commit 73f1582

File tree

4 files changed

+130
-1
lines changed

4 files changed

+130
-1
lines changed

install_zh.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Flocks 中国用户一键安装脚本。
4242
然后转交 scripts/install_zh.sh 继续安装。默认会在当前工作目录下创建 "flocks" 子目录。
4343
4444
默认会为 install_zh 注入国内软件源与 uv 安装镜像。
45+
uv 备用源: https://uv.agentsmirror.com/install-cn.sh
4546
4647
远程使用:
4748
curl -fsSL $RAW_INSTALL_ZH_SH_URL | bash
@@ -130,6 +131,7 @@ resolve_project_dir() {
130131
configure_cn_environment() {
131132
export FLOCKS_INSTALL_LANGUAGE="${FLOCKS_INSTALL_LANGUAGE:-zh-CN}"
132133
export FLOCKS_UV_INSTALL_SH_URL="${FLOCKS_UV_INSTALL_SH_URL:-https://astral.org.cn/uv/install.sh}"
134+
export FLOCKS_UV_INSTALL_SH_FALLBACK_URL="${FLOCKS_UV_INSTALL_SH_FALLBACK_URL:-https://uv.agentsmirror.com/install-cn.sh}"
133135
export FLOCKS_UV_INSTALL_PS1_URL="${FLOCKS_UV_INSTALL_PS1_URL:-https://astral.org.cn/uv/install.ps1}"
134136
export PUPPETEER_CHROME_DOWNLOAD_BASE_URL="${PUPPETEER_CHROME_DOWNLOAD_BASE_URL:-https://cdn.npmmirror.com/binaries/chrome-for-testing}"
135137
}

scripts/install.sh

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ PATH_REFRESH_HINT_REQUIRED=0
1717
INSTALL_LANGUAGE="${FLOCKS_INSTALL_LANGUAGE:-en}"
1818
UV_DEFAULT_INDEX="${FLOCKS_UV_DEFAULT_INDEX:-https://pypi.org/simple}"
1919
UV_INSTALL_SH_URL="${FLOCKS_UV_INSTALL_SH_URL:-https://astral.sh/uv/install.sh}"
20+
UV_INSTALL_SH_FALLBACK_URL="${FLOCKS_UV_INSTALL_SH_FALLBACK_URL:-}"
2021
NPM_REGISTRY="${FLOCKS_NPM_REGISTRY:-https://registry.npmjs.org/}"
2122
NODEJS_MANUAL_DOWNLOAD_URL="${FLOCKS_NODEJS_MANUAL_DOWNLOAD_URL:-https://nodejs.org/en/download}"
2223
NVM_INSTALL_SCRIPT_URL="${FLOCKS_NVM_INSTALL_SCRIPT_URL:-https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh}"
@@ -59,6 +60,9 @@ select_install_sources() {
5960
info "使用 PyPI 源: $UV_DEFAULT_INDEX"
6061
info "使用 npm 源: $NPM_REGISTRY"
6162
info "使用 uv 安装脚本: $UV_INSTALL_SH_URL"
63+
if [[ -n "$UV_INSTALL_SH_FALLBACK_URL" ]]; then
64+
info "使用 uv 备用安装脚本: $UV_INSTALL_SH_FALLBACK_URL"
65+
fi
6266
else
6367
info "Using PyPI index: $UV_DEFAULT_INDEX"
6468
info "Using npm registry: $NPM_REGISTRY"
@@ -517,6 +521,7 @@ ensure_npm_global_prefix_writable() {
517521
}
518522

519523
install_uv() {
524+
local primary_install_failed=0
520525
if has_cmd uv; then
521526
return
522527
fi
@@ -528,9 +533,30 @@ install_uv() {
528533
has_cmd curl || fail "curl is required to install uv automatically."
529534
info "uv was not found. Installing it automatically..."
530535
fi
531-
curl -LsSf "$UV_INSTALL_SH_URL" | sh
536+
if ! curl -LsSf "$UV_INSTALL_SH_URL" | sh; then
537+
primary_install_failed=1
538+
fi
532539
refresh_path
533540
ensure_path_persisted "$HOME/.local/bin"
541+
542+
if has_cmd uv; then
543+
return
544+
fi
545+
546+
if is_zh_install && [[ -n "$UV_INSTALL_SH_FALLBACK_URL" ]]; then
547+
if [[ "$primary_install_failed" -eq 1 ]]; then
548+
info "默认 uv 安装脚本失败,正在尝试中国大陆备用源..."
549+
else
550+
info "默认 uv 安装脚本执行后仍未检测到 uv,正在尝试中国大陆备用源..."
551+
fi
552+
553+
if ! curl -LsSf "$UV_INSTALL_SH_FALLBACK_URL" | sh; then
554+
fail "默认 uv 安装脚本和中国大陆备用源都执行失败。请检查网络连通性或 PATH 后重试。"
555+
fi
556+
refresh_path
557+
ensure_path_persisted "$HOME/.local/bin"
558+
fi
559+
534560
if is_zh_install; then
535561
has_cmd uv || fail "uv 安装已完成,但当前仍无法找到 uv。请检查 PATH 后重试。"
536562
else

scripts/install_zh.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Flocks 中国用户源码安装脚本。
1919
PyPI: https://mirrors.aliyun.com/pypi/simple
2020
npm : https://registry.npmmirror.com/
2121
uv : https://astral.org.cn/uv/install.sh
22+
uv 备用源: https://uv.agentsmirror.com/install-cn.sh
2223
2324
一键安装入口:
2425
curl -fsSL $RAW_INSTALL_ZH_SH_URL | bash
@@ -38,6 +39,7 @@ configure_cn_environment() {
3839
export FLOCKS_RAW_INSTALL_PS1_URL="${FLOCKS_RAW_INSTALL_PS1_URL:-$RAW_INSTALL_ZH_PS1_URL}"
3940
export FLOCKS_UV_DEFAULT_INDEX="${FLOCKS_UV_DEFAULT_INDEX:-https://mirrors.aliyun.com/pypi/simple}"
4041
export FLOCKS_UV_INSTALL_SH_URL="${FLOCKS_UV_INSTALL_SH_URL:-https://astral.org.cn/uv/install.sh}"
42+
export FLOCKS_UV_INSTALL_SH_FALLBACK_URL="${FLOCKS_UV_INSTALL_SH_FALLBACK_URL:-https://uv.agentsmirror.com/install-cn.sh}"
4143
export FLOCKS_UV_INSTALL_PS1_URL="${FLOCKS_UV_INSTALL_PS1_URL:-https://astral.org.cn/uv/install.ps1}"
4244
export FLOCKS_NPM_REGISTRY="${FLOCKS_NPM_REGISTRY:-https://registry.npmmirror.com/}"
4345
export PUPPETEER_CHROME_DOWNLOAD_BASE_URL="${PUPPETEER_CHROME_DOWNLOAD_BASE_URL:-https://cdn.npmmirror.com/binaries/chrome-for-testing}"

tests/scripts/test_install_script_sources.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ def test_install_zh_bash_bootstrap_uses_gitee_archive_and_delegates_to_zh_worksp
2323
assert 'zh-CN' in script
2424
assert 'FLOCKS_UV_INSTALL_SH_URL' in script
2525
assert 'https://astral.org.cn/uv/install.sh' in script
26+
assert 'FLOCKS_UV_INSTALL_SH_FALLBACK_URL' in script
27+
assert 'https://uv.agentsmirror.com/install-cn.sh' in script
2628
assert 'FLOCKS_UV_INSTALL_PS1_URL' in script
2729
assert 'https://astral.org.cn/uv/install.ps1' in script
2830
assert 'PUPPETEER_CHROME_DOWNLOAD_BASE_URL' in script
@@ -110,10 +112,13 @@ def test_main_bash_installer_uses_configured_default_sources_without_probing() -
110112
assert 'FLOCKS_UV_DEFAULT_INDEX' in script
111113
assert 'FLOCKS_UV_INSTALL_SH_URL' in script
112114
assert 'https://astral.sh/uv/install.sh' in script
115+
assert 'FLOCKS_UV_INSTALL_SH_FALLBACK_URL' in script
113116
assert 'FLOCKS_NPM_REGISTRY' in script
114117
assert 'Using PyPI index: $UV_DEFAULT_INDEX' in script
115118
assert 'Using npm registry: $NPM_REGISTRY' in script
116119
assert 'Using uv install script: $UV_INSTALL_SH_URL' in script
120+
assert 'Using uv fallback script' not in script
121+
assert '使用 uv 备用安装脚本: $UV_INSTALL_SH_FALLBACK_URL' in script
117122
assert 'pick_fastest_url' not in script
118123
assert 'Probing PyPI and npm registries to choose the faster source' not in script
119124
assert 'npm_config_registry="$NPM_REGISTRY" npm install' in script
@@ -126,6 +131,7 @@ def test_main_bash_installer_uses_configured_default_sources_without_probing() -
126131
assert "load_nvm()" in script
127132
assert 'curl -o- "$NVM_INSTALL_SCRIPT_URL" | bash' in script
128133
assert 'curl -LsSf "$UV_INSTALL_SH_URL" | sh' in script
134+
assert 'curl -LsSf "$UV_INSTALL_SH_FALLBACK_URL" | sh' in script
129135
assert 'nvm install "$MIN_NODE_MAJOR"' in script
130136
assert 'nvm use "$MIN_NODE_MAJOR" >/dev/null' in script
131137
assert "Homebrew was not found. Trying to install nvm..." in script
@@ -354,6 +360,99 @@ def test_main_bash_installer_checks_node_modules_dir_before_accepting_global_pre
354360
assert result.returncode == 0, output
355361

356362

363+
def test_main_bash_installer_uses_cn_uv_fallback_when_primary_script_fails() -> None:
364+
script = (SCRIPT_DIR / "install.sh").read_text(encoding="utf-8")
365+
script_without_main = re.sub(r'\nmain "\$@"\s*$', "\n", script)
366+
test_script = script_without_main + textwrap.dedent(
367+
r"""
368+
369+
export HOME="$(mktemp -d)"
370+
export FLOCKS_INSTALL_LANGUAGE="zh-CN"
371+
export FLOCKS_UV_INSTALL_SH_URL="https://primary.example/install.sh"
372+
export FLOCKS_UV_INSTALL_SH_FALLBACK_URL="https://uv.agentsmirror.com/install-cn.sh"
373+
export TEST_LOG="$HOME/install-uv.log"
374+
INSTALL_LANGUAGE="$FLOCKS_INSTALL_LANGUAGE"
375+
UV_INSTALL_SH_URL="$FLOCKS_UV_INSTALL_SH_URL"
376+
UV_INSTALL_SH_FALLBACK_URL="$FLOCKS_UV_INSTALL_SH_FALLBACK_URL"
377+
378+
has_cmd() {
379+
case "$1" in
380+
curl)
381+
return 0
382+
;;
383+
uv)
384+
[[ -f "$HOME/uv-installed" ]]
385+
return $?
386+
;;
387+
*)
388+
command -v "$1" >/dev/null 2>&1
389+
;;
390+
esac
391+
}
392+
393+
info() {
394+
printf '%s\n' "$1" >> "$TEST_LOG"
395+
}
396+
397+
fail() {
398+
printf 'FAIL:%s\n' "$1" >&2
399+
exit 1
400+
}
401+
402+
refresh_path() {
403+
:
404+
}
405+
406+
ensure_path_persisted() {
407+
:
408+
}
409+
410+
curl() {
411+
printf '%s\n' "$*" >> "$HOME/curl-commands.log"
412+
if [[ "$*" == *"primary.example"* ]]; then
413+
return 22
414+
fi
415+
416+
cat <<'EOF'
417+
touch "$HOME/uv-installed"
418+
EOF
419+
}
420+
421+
install_uv
422+
423+
curl_commands="$(<"$HOME/curl-commands.log")"
424+
install_log="$(<"$TEST_LOG")"
425+
426+
[[ -f "$HOME/uv-installed" ]] || {
427+
printf 'uv was not installed by fallback script\n' >&2
428+
exit 1
429+
}
430+
[[ "$curl_commands" == *"https://primary.example/install.sh"* ]] || {
431+
printf 'primary uv script was not attempted: %s\n' "$curl_commands" >&2
432+
exit 1
433+
}
434+
[[ "$curl_commands" == *"https://uv.agentsmirror.com/install-cn.sh"* ]] || {
435+
printf 'fallback uv script was not attempted: %s\n' "$curl_commands" >&2
436+
exit 1
437+
}
438+
[[ "$install_log" == *"默认 uv 安装脚本失败,正在尝试中国大陆备用源"* ]] || {
439+
printf 'fallback log missing: %s\n' "$install_log" >&2
440+
exit 1
441+
}
442+
"""
443+
)
444+
445+
result = subprocess.run(
446+
["bash", "-c", test_script],
447+
check=False,
448+
capture_output=True,
449+
text=True,
450+
)
451+
452+
output = f"{result.stdout}\n{result.stderr}"
453+
assert result.returncode == 0, output
454+
455+
357456
def test_main_powershell_installer_uses_configured_default_sources_without_probing() -> None:
358457
script = (SCRIPT_DIR / "install.ps1").read_text(encoding="utf-8-sig")
359458

0 commit comments

Comments
 (0)