Files
my-pi/update.sh
T
云服务部-叶林立andDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 2aa1be0b54 feat: 组合包配置以包内 config/ 为唯一来源,并新增受保护写入人工审批
配置统一:权限策略、硬防御、Tool Search、提示词封面以及新迁入的
RTK、pi-compaction、pi-worktree、pi-hermes-memory 配置全部由扩展
直接读取已安装组合包内的 config/,pi install / pi update 即完成部署;
删除 rebuild-bundle-config.mjs 与 bundle-manifest.json,my-pi/ 只保留
日志与缓存。update.sh 成功升级后将默认 git 来源的已安装副本
reset 回发布版本。AGENTS.md 新增「组合包配置原则」。

脚本整合:search_config.sh / ssh_config.sh 合并为 configure.sh
search|ssh;install.sh / update.sh 共享函数抽到 scripts/lib.sh,远程
安装器改为先下载到临时文件再执行,下载失败可被检测。

权限:hard-guards 新增 protectedWrites,写入 Pi 凭据/设置、已安装
组合包与扩展目录、pi-ssh 凭据库时一律人工确认(跳过 AutoReview、
yolo 与 Session grant,无 UI 时拒绝);放行 ls、LSP 查询、ctx 只读、
web_search、Chrome 只读与 git 只读命令,skill_manage 改为 ask。

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-20 01:05:31 -07:00

323 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
set -u
SCRIPT_DIR=$(CDPATH=; cd "$(dirname "$0")" && pwd) || exit 1
MY_PI_LIB=$SCRIPT_DIR/scripts/lib.sh
if [ ! -f "$MY_PI_LIB" ]; then
printf '错误: 找不到共享辅助脚本:%s\n' "$MY_PI_LIB" >&2
exit 1
fi
# shellcheck source=scripts/lib.sh
. "$MY_PI_LIB"
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
record_update_failure() {
warn "$1 升级或配置同步失败;其余已安装组件仍会继续处理。"
update_failed=1
}
select_updated_bundle_config() {
updated_bundle_dir=
updated_bundle_managed_git=0
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
updated_bundle_managed_git=1
;;
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
}
# pi update 只在有新提交时覆盖已安装副本;update.sh 每次成功升级后都把受管
# checkout 还原到已发布版本,使对已安装副本的本地修改(包括扩展设置界面的
# 保存)不积累。只对默认 git 来源的受管 checkout 执行:本地路径来源就是用户的
# 工作仓库,绝不能 reset。不做 git clean,以保留 node_modules。
restore_installed_bundle() {
[ "${updated_bundle_managed_git:-0}" = "1" ] || return 0
[ -d "$updated_bundle_dir/.git" ] || return 0
if git -C "$updated_bundle_dir" reset --quiet --hard HEAD; then
say "✓ 已还原已安装组合包中的本地修改"
else
record_update_failure "已安装组合包本地修改还原"
fi
}
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
}
# 输出过期包列表:brew outdated 发现过期包时退出码为 1,只有无输出且非零才视为查询失败。
brew_outdated() {
brew_outdated_output=$(brew outdated "$@")
brew_outdated_status=$?
if [ "$brew_outdated_status" -ne 0 ] && [ -z "$brew_outdated_output" ]; then
return 1
fi
printf '%s\n' "$brew_outdated_output"
}
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 有新版本,正在升级。"
HOMEBREW_NO_ASK=1 brew upgrade --formula "$formula" || record_update_failure "$description"
fi
elif command -v "$command_name" >/dev/null 2>&1; then
say "- $description 已安装,但不由 Homebrew 管理,已保留现有安装。"
else
say "- $description 未安装,升级流程跳过。"
fi
}
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 有新版本,正在升级。"
HOMEBREW_NO_ASK=1 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_ZSH_LOADER_ANCHOR" \
'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_ZSH_LOADER_ANCHOR" \
"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 执行失败,已停止后续升级。重新运行 update.sh 即可,Pi 会自行恢复未完成的升级。"
exit 1
fi
select_updated_bundle_config || exit 1
restore_installed_bundle
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"