#!/bin/sh set -u SCRIPT_DIR=$(CDPATH=; cd "$(dirname "$0")" && pwd) || exit 1 MANAGED_CONFIG_HELPERS=$SCRIPT_DIR/scripts/managed-config.sh if [ ! -f "$MANAGED_CONFIG_HELPERS" ]; then printf '错误: 找不到受管配置辅助脚本:%s\n' "$MANAGED_CONFIG_HELPERS" >&2 exit 1 fi # shellcheck source=scripts/managed-config.sh . "$MANAGED_CONFIG_HELPERS" PACKAGE_SOURCE=${PI_PACKAGE_SOURCE:-git:git@bitbucket.org:siakitem/my-pi.git} POWERLEVEL10K_PRESET_FILE=$SCRIPT_DIR/config/p10k.zsh CODEGRAPH_INSTALL_URL=https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh CODEGRAPH_LATEST_RELEASE_URL=https://github.com/colbymchenry/codegraph/releases/latest KITTY_INSTALL_URL=https://sw.kovidgoyal.net/kitty/installer.sh KITTY_VERSION_URL=https://sw.kovidgoyal.net/kitty/current-version.txt update_failed=0 brew_metadata_refreshed=0 say() { printf '%s\n' "$*" } warn() { printf '警告: %s\n' "$*" >&2 } record_update_failure() { warn "$1 升级或配置同步失败;其余已安装组件仍会继续处理。" update_failed=1 } select_updated_bundle_config() { updated_bundle_dir= case $PACKAGE_SOURCE in /*) updated_bundle_dir=$PACKAGE_SOURCE ;; ./*|../*) updated_bundle_dir=$(CDPATH=; cd "$PACKAGE_SOURCE" 2>/dev/null && pwd) || updated_bundle_dir= ;; git:git@bitbucket.org:siakitem/my-pi.git) updated_bundle_dir=${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/git/bitbucket.org/siakitem/my-pi ;; esac if [ -n "$updated_bundle_dir" ] && [ -f "$updated_bundle_dir/config/p10k.zsh" ]; then POWERLEVEL10K_PRESET_FILE=$updated_bundle_dir/config/p10k.zsh elif [ ! -f "$POWERLEVEL10K_PRESET_FILE" ]; then warn "无法定位升级后的组合包配置,找不到 ${POWERLEVEL10K_PRESET_FILE}。" return 1 else warn "无法从当前 PI_PACKAGE_SOURCE 推导升级后的包目录,将使用脚本目录中的配置。" fi } resolve_zsh_paths() { oh_my_zsh_dir=${ZSH:-$HOME/.oh-my-zsh} zsh_custom_dir=${ZSH_CUSTOM:-$oh_my_zsh_dir/custom} zshrc_file=${ZDOTDIR:-$HOME}/.zshrc my_pi_config_dir=${XDG_CONFIG_HOME:-$HOME/.config}/my-pi p10k_managed_config_file=$my_pi_config_dir/p10k.zsh } replace_zsh_managed_block() { managed_id=$1 managed_placement=$2 managed_anchor=$3 shift 3 managed_content=$(mktemp "${TMPDIR:-/tmp}/my-pi-zsh-block.XXXXXX") || return 1 printf '%s\n' "$@" > "$managed_content" my_pi_replace_managed_block "$zshrc_file" "$managed_id" "$managed_content" \ "$managed_placement" "$managed_anchor" managed_status=$? rm -f "$managed_content" return "$managed_status" } update_git_checkout() { component=$1 checkout=$2 if [ ! -d "$checkout/.git" ]; then warn "$component 已安装,但不是脚本可安全升级的 Git checkout,已跳过版本升级。" return 0 fi if ! command -v git >/dev/null 2>&1; then warn "升级 $component 需要 git。" return 1 fi upstream=$(git -C "$checkout" rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' 2>/dev/null) || { warn "$component 的 Git checkout 没有可识别的上游分支,已跳过版本升级。" return 1 } git -C "$checkout" fetch --quiet || return 1 local_revision=$(git -C "$checkout" rev-parse HEAD) || return 1 remote_revision=$(git -C "$checkout" rev-parse "$upstream") || return 1 if [ "$local_revision" = "$remote_revision" ]; then say "✓ $component 已是最新版本。" return 0 fi say "发现 $component 有新版本,正在快进升级。" git -C "$checkout" merge --ff-only "$upstream" } refresh_brew_metadata() { if [ "$brew_metadata_refreshed" -eq 1 ]; then return 0 fi say "正在刷新 Homebrew 版本信息。" brew update || return 1 brew_metadata_refreshed=1 } update_brew_formula_if_installed() { formula=$1 description=$2 command_name=$3 if command -v brew >/dev/null 2>&1 && brew list --formula "$formula" >/dev/null 2>&1; then refresh_brew_metadata || { record_update_failure "$description" return 0 } outdated_formula=$(brew outdated --formula --quiet "$formula") || { record_update_failure "$description" return 0 } if [ -z "$outdated_formula" ]; then say "✓ $description 已是最新版本。" else say "发现 $description 有新版本,正在升级。" brew upgrade "$formula" || record_update_failure "$description" fi elif command -v "$command_name" >/dev/null 2>&1; then say "- $description 已安装,但不由 Homebrew 管理,已保留现有安装。" else say "- $description 未安装,升级流程跳过。" fi } detect_kitty() { kitty_bin= if command -v kitty >/dev/null 2>&1; then kitty_bin=$(command -v kitty) return 0 fi case $(uname -s) in Darwin) kitty_candidates="/Applications/kitty.app/Contents/MacOS/kitty" ;; *) kitty_candidates="$HOME/.local/kitty.app/bin/kitty" ;; esac for candidate in $kitty_candidates; do if [ -x "$candidate" ]; then kitty_bin=$candidate return 0 fi done return 1 } resolve_kitty_config_dir() { kitty_config_dir=${KITTY_CONFIG_DIRECTORY:-${XDG_CONFIG_HOME:-$HOME/.config}/kitty} } kitty_uses_solarized_dark() { resolve_kitty_config_dir [ -f "$kitty_config_dir/current-theme.conf" ] && grep -F "Solarized Dark" "$kitty_config_dir/current-theme.conf" >/dev/null 2>&1 } update_kitty() { if ! detect_kitty; then say "- Kitty 未安装,升级流程跳过。" return 0 fi if command -v brew >/dev/null 2>&1 && brew list --cask kitty >/dev/null 2>&1; then if refresh_brew_metadata; then if outdated_kitty=$(brew outdated --cask --quiet kitty); then if [ -z "$outdated_kitty" ]; then say "✓ Kitty 已是最新版本。" else say "发现 Kitty 有新版本,正在升级。" brew upgrade --cask kitty || record_update_failure "Kitty" fi else record_update_failure "Kitty" fi else record_update_failure "Kitty" fi elif [ "$kitty_bin" = "$HOME/.local/kitty.app/bin/kitty" ] || \ [ "$kitty_bin" = "/Applications/kitty.app/Contents/MacOS/kitty" ]; then if ! command -v curl >/dev/null 2>&1; then record_update_failure "Kitty(检查版本需要 curl)" else kitty_version_output=$("$kitty_bin" --version 2>/dev/null) || kitty_version_output= case $kitty_version_output in kitty\ *) installed_kitty_version=${kitty_version_output#kitty } installed_kitty_version=${installed_kitty_version%% *} ;; *) installed_kitty_version= ;; esac latest_kitty_version=$(curl -fsSL "$KITTY_VERSION_URL") || latest_kitty_version= if [ -z "$installed_kitty_version" ] || [ -z "$latest_kitty_version" ]; then record_update_failure "Kitty 版本检查" elif [ "$installed_kitty_version" = "$latest_kitty_version" ]; then say "✓ Kitty $installed_kitty_version 已是最新版本。" else say "发现 Kitty ${latest_kitty_version}(当前 ${installed_kitty_version}),正在升级。" installer_file=$(mktemp "${TMPDIR:-/tmp}/my-pi-kitty.XXXXXX") || return 1 if ! curl -fsSL "$KITTY_INSTALL_URL" -o "$installer_file" || \ ! sh "$installer_file" launch=n "installer=version-$latest_kitty_version"; then record_update_failure "Kitty" fi rm -f "$installer_file" fi fi else say "- Kitty 已安装,但不由 Homebrew 或官方 ~/.local 安装管理,已保留现有安装。" fi if kitty_uses_solarized_dark; then "$kitty_bin" +kitten themes --reload-in=none "Solarized Dark" || \ record_update_failure "Kitty Solarized Dark 主题" fi } update_codegraph() { if ! command -v codegraph >/dev/null 2>&1; then say "- CodeGraph 未安装,升级流程跳过。" return 0 fi if ! command -v curl >/dev/null 2>&1; then warn "检查 CodeGraph 版本需要 curl。" return 1 fi installed_codegraph_version=$(codegraph --version 2>/dev/null) || installed_codegraph_version= latest_release_url=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \ "$CODEGRAPH_LATEST_RELEASE_URL") || latest_release_url= case $latest_release_url in */releases/tag/*) latest_codegraph_tag=${latest_release_url##*/} ;; *) latest_codegraph_tag= ;; esac latest_codegraph_version=${latest_codegraph_tag#v} installed_codegraph_version=${installed_codegraph_version#v} if [ -z "$installed_codegraph_version" ] || [ -z "$latest_codegraph_version" ]; then warn "无法比较 CodeGraph 的本地版本与最新发布版本,已跳过替换。" return 1 fi if [ "$installed_codegraph_version" = "$latest_codegraph_version" ]; then say "✓ CodeGraph $installed_codegraph_version 已是最新版本。" return 0 fi say "发现 CodeGraph ${latest_codegraph_version}(当前 ${installed_codegraph_version}),正在升级。" installer_file=$(mktemp "${TMPDIR:-/tmp}/my-pi-codegraph.XXXXXX") || return 1 if ! curl -fsSL "$CODEGRAPH_INSTALL_URL" -o "$installer_file" || \ ! CODEGRAPH_VERSION="$latest_codegraph_tag" sh "$installer_file"; then rm -f "$installer_file" return 1 fi rm -f "$installer_file" } update_zsh_environment() { resolve_zsh_paths if [ ! -f "$oh_my_zsh_dir/oh-my-zsh.sh" ]; then say "- Oh My Zsh 未安装,Powerlevel10k 和 Zsh 插件升级均跳过。" return 0 fi if [ -f "$oh_my_zsh_dir/tools/upgrade.sh" ] && command -v zsh >/dev/null 2>&1; then ZSH="$oh_my_zsh_dir" zsh "$oh_my_zsh_dir/tools/upgrade.sh" || record_update_failure "Oh My Zsh" else update_git_checkout "Oh My Zsh" "$oh_my_zsh_dir" || record_update_failure "Oh My Zsh" fi powerlevel10k_dir=$zsh_custom_dir/themes/powerlevel10k if [ -f "$powerlevel10k_dir/powerlevel10k.zsh-theme" ]; then update_git_checkout "Powerlevel10k" "$powerlevel10k_dir" || record_update_failure "Powerlevel10k" else say "- Powerlevel10k 未安装,升级流程跳过。" fi for plugin in zsh-autosuggestions zsh-syntax-highlighting; do plugin_dir=$zsh_custom_dir/plugins/$plugin if [ -d "$plugin_dir" ]; then update_git_checkout "$plugin" "$plugin_dir" || record_update_failure "$plugin" else say "- $plugin 未安装,升级流程跳过。" fi done [ -f "$zshrc_file" ] || return 0 if my_pi_managed_block_exists "$zshrc_file" "oh-my-zsh-loader"; then replace_zsh_managed_block "oh-my-zsh-loader" "append" "" \ "export ZSH=\"$oh_my_zsh_dir\"" \ "source \"\$ZSH/oh-my-zsh.sh\"" || record_update_failure ".zshrc Oh My Zsh 受管块" fi if my_pi_managed_block_exists "$zshrc_file" "powerlevel10k-theme"; then replace_zsh_managed_block "powerlevel10k-theme" "before" \ '^(# >>> my-pi:oh-my-zsh-loader >>>|[[:space:]]*(source|\.)[[:space:]].*oh-my-zsh\.sh)' \ 'ZSH_THEME="powerlevel10k/powerlevel10k"' || record_update_failure ".zshrc Powerlevel10k 主题受管块" fi for plugin in git zsh-autosuggestions zsh-syntax-highlighting; do if my_pi_managed_block_exists "$zshrc_file" "zsh-plugin-$plugin"; then replace_zsh_managed_block "zsh-plugin-$plugin" "before" \ '^(# >>> my-pi:oh-my-zsh-loader >>>|[[:space:]]*(source|\.)[[:space:]].*oh-my-zsh\.sh)' \ "plugins+=($plugin)" || record_update_failure ".zshrc $plugin 受管块" fi done if [ -f "$p10k_managed_config_file" ] || \ my_pi_managed_block_exists "$zshrc_file" "powerlevel10k-config"; then if my_pi_sync_managed_file "$POWERLEVEL10K_PRESET_FILE" "$p10k_managed_config_file"; then replace_zsh_managed_block "powerlevel10k-config" "append" "" \ "[[ ! -f \"$p10k_managed_config_file\" ]] || source \"$p10k_managed_config_file\"" || \ record_update_failure ".zshrc Powerlevel10k 配置受管块" else record_update_failure "Powerlevel10k 受管配置" fi fi } if ! command -v pi >/dev/null 2>&1; then warn "找不到 pi,无法升级组合包。请确保 pi 位于 PATH。" exit 127 fi say "正在升级 Pi 组合包:$PACKAGE_SOURCE" if ! pi update "$PACKAGE_SOURCE"; then warn "pi update 执行失败,已停止后续升级。" exit 1 fi select_updated_bundle_config || exit 1 say "组合包升级完成,开始升级已安装的终端环境和机器级 CLI;未安装项将直接跳过。" update_kitty || record_update_failure "Kitty" update_zsh_environment || record_update_failure "Zsh 环境" update_codegraph || record_update_failure "CodeGraph" update_brew_formula_if_installed "kotlin-lsp" "kotlin-lsp" "kotlin-lsp" update_brew_formula_if_installed "openjdk@21" "Java 21" "java" update_brew_formula_if_installed "jdtls" "jdtls" "jdtls" say "已安装组件的升级与受管配置同步完成。" exit "$update_failed"