Skip to content

Commit a32c66a

Browse files
committed
fix(scripts): enhance execution robustness
- Add a 3-minute timeout and logging to bootstrap.sh to prevent hanging while waiting for Xcode Command Line Tools. - Add a 5-second timeout in install.sh using perl to prevent hanging when reading the legacy zsh PATH. - Replace unsafe eval with array-based command execution in aic.fish to prevent command injection from malicious git diffs. - Enforce strict prompt constraints in aic.fish and ait.fish to forbid markdown blocks and conversational filler text. - Add awk-based fallback parser in aic.fish to forcefully extract the commit message if the AI model disobeys output constraints. - Include bootstrap.sh in the Makefile shellcheck linting target. - Clear the local fisher plugin cache during Makefile plugin updates.
1 parent 8acfd13 commit a32c66a

5 files changed

Lines changed: 87 additions & 31 deletions

File tree

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ plugins: ## 安装/更新 Fisher 插件
154154
fish -c "source '$$tmp' && fisher install jorgebucaran/fisher"; \
155155
rm -f "$$tmp"; \
156156
fi
157+
@echo " 清理残留插件缓存..."
158+
@rm -rf $(HOME)/.local/share/fisher
157159
@if [ -f fish/dot-config/fish/fish_plugins ]; then \
158160
fish -c "fisher install (cat fish/dot-config/fish/fish_plugins)"; \
159161
fi
@@ -171,7 +173,7 @@ lint: ## 静态分析 Shell 脚本 (shellcheck)
171173
exit 1; \
172174
fi
173175
@errors=0; \
174-
for script in install.sh macos.sh bin/*; do \
176+
for script in bootstrap.sh install.sh macos.sh bin/*; do \
175177
if [ -f "$$script" ]; then \
176178
if file "$$script" | grep -q "shell script" || head -1 "$$script" | grep -Eq '^#!.*(bash|sh)'; then \
177179
if shellcheck -S warning "$$script" 2>/dev/null; then \

bootstrap.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,17 @@ if ! command -v git &> /dev/null; then
2727
xcode-select --install
2828

2929
info "Waiting for Git installation to complete..."
30+
wait_count=0
3031
until command -v git &> /dev/null; do
3132
sleep 5
33+
wait_count=$((wait_count + 1))
34+
if [ $((wait_count % 6)) -eq 0 ]; then
35+
info "Still waiting for Xcode Command Line Tools. Please click 'Install' on the macOS popup..."
36+
fi
37+
if [ $wait_count -ge 180 ]; then
38+
error "Git installation took too long or was cancelled. Please install manually: xcode-select --install"
39+
exit 1
40+
fi
3241
done
3342
success "Git installed successfully."
3443
fi

fish/dot-config/fish/functions/aic.fish

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,12 @@ function aic -d "根据代码变更自动生成 Git Commit 信息"
6060
$lang_prompt
6161
6262
类型可选: feat, fix, docs, style, refactor, test, chore
63-
只返回完整的提交信息本身(包含首行和 Body/Footer),不加任何啰嗦的解释和外层的 Markdown 代码块 (```)。"
63+
64+
⚠️ 绝对约束:
65+
- 绝对禁止输出任何解释、思考过程、分析意图(如 'I detect implementation intent')或引导语!
66+
- 只返回严格的提交信息文本本身。
67+
- 禁止使用外层的 Markdown 代码块 (\`\`\`)。
68+
- 第一行必须直接是 type(scope): description 格式!"
6469

6570
if test -n "$supplementary_info"
6671
set prompt_text "$prompt_text
@@ -75,17 +80,39 @@ $supplementary_info"
7580
$diff
7681
</diff>"
7782

83+
# 安全执行 AI_CMD(禁止 eval),避免 git diff 中的特殊字符触发命令注入
84+
# 兼容两种配置:
85+
# 1) set -gx AI_CMD opencode run
86+
# 2) set -gx AI_CMD "opencode run"
87+
set -l ai_cmd_argv $AI_CMD
88+
if test (count $ai_cmd_argv) -eq 1
89+
set ai_cmd_argv (string split -n " " -- $ai_cmd_argv[1])
90+
end
91+
92+
if test (count $ai_cmd_argv) -eq 0
93+
echo "❌ AI_CMD 配置无效,请检查 ~/.config/fish/config.local.fish"
94+
return 1
95+
end
96+
7897
# 调用检测到的 AI 工具生成内容
7998
set -l msg_tmpfile (mktemp)
80-
eval $ai_cmd \"\$prompt_text\" > $msg_tmpfile
99+
$ai_cmd_argv "$prompt_text" > $msg_tmpfile
81100
set -l ai_exit_status $status
82101

83102
# 捕捉在 AI 生成过程中被 Ctrl+C 中断的情况或者命令执行失败
84103
# 先检查退出码:Ctrl+C (130) 或其他错误
85104
if test $ai_exit_status -ne 0
86105
rm -f $msg_tmpfile
87106
echo ""
88-
echo "❌ 操作已中断"
107+
echo "❌ 操作已中断或报错退出"
108+
return 1
109+
end
110+
111+
# 如果文件为空(AI未输出或因发生类似模型未找到的错误而仅输出到了 stderr)
112+
if not test -s $msg_tmpfile
113+
rm -f $msg_tmpfile
114+
echo ""
115+
echo "❌ AI 生成失败 (未获取到输出内容,请检查上方报错信息)"
89116
return 1
90117
end
91118

@@ -98,9 +125,18 @@ $diff
98125
return 1
99126
end
100127

101-
# 清理响应
128+
# 清理响应: 移除 Markdown 块标记
102129
sed -i '' -e '/^```\(commit\|text\)/d' -e '/^```$/d' $msg_tmpfile
103130

131+
# 兜底防御:大模型有时仍会输出思考过程或废话。
132+
# 这里使用 awk 截取从标准的 Conventional Commit 起始的所有内容,抛弃前面的废话
133+
awk '/^(feat|fix|docs|style|refactor|test|chore|perf|build|ci|revert)(\([^)]+\))?: / {found=1} found {print}' $msg_tmpfile > $msg_tmpfile.tmp
134+
if test -s $msg_tmpfile.tmp
135+
mv $msg_tmpfile.tmp $msg_tmpfile
136+
else
137+
rm -f $msg_tmpfile.tmp
138+
end
139+
104140
echo ""
105141
echo "📝 建议提交信息:"
106142
awk '{print " " $0}' $msg_tmpfile

fish/dot-config/fish/functions/ait.fish

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ $commit_logs
8888
第一部分:仅包含推断出的纯版本号(例如 1.2.0,不要带 v)
8989
第二部分:对应的 CHANGELOG 内容,格式遵循 Keep a Changelog 规范。二级标题必须为 ## [VERSION] - DATE。具体条目按 Added, Changed, Fixed 等归类,内容使用无序列表即可。
9090
91-
4. 强制约束:只返回以上两部分内容,不带 Markdown 代码块或其他多余解释"
91+
4. 强制约束:绝对禁止输出任何解释、思考过程、分析意图或引导语!只返回以上两部分内容本身,严禁带 Markdown 代码块或其余口语化废话"
9292

9393
if test -n "$supplementary_info"
9494
set prompt "$prompt

install.sh

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -225,33 +225,42 @@ fish -c "fish_add_path (brew --prefix)/bin" 2>/dev/null || true
225225

226226
# Migrate PATH from zsh to fish (for users switching from zsh)
227227
if command -v zsh &>/dev/null; then
228-
info "Migrating PATH from zsh to fish..."
229-
230-
ZSH_PATHS=$(/bin/zsh -l -c 'echo "$PATH"' 2>/dev/null | tr ':' '\n')
231-
FISH_PATHS=$(fish -l -c 'string join \n $PATH' 2>/dev/null)
232-
233-
MIGRATED=0
234-
while IFS= read -r p; do
235-
[ -z "$p" ] && continue
236-
[ ! -d "$p" ] && continue
237-
238-
# Skip system/default paths (already handled)
239-
case "$p" in
240-
/usr/bin|/bin|/usr/sbin|/sbin|/usr/local/bin|/opt/homebrew/bin|/opt/homebrew/sbin) continue ;;
241-
esac
242-
243-
# Check if path is already in fish
244-
if ! echo "$FISH_PATHS" | grep -qxF "$p"; then
245-
fish -c "fish_add_path --append '$p'" 2>/dev/null || true
246-
info " Added: $p"
247-
MIGRATED=$((MIGRATED + 1))
248-
fi
249-
done <<< "$ZSH_PATHS"
228+
info "Migrating PATH from zsh to fish (with 5-second timeout protection)..."
229+
230+
if command -v perl &>/dev/null; then
231+
ZSH_PATHS=$(perl -e 'alarm 5; exec @ARGV' /bin/zsh -l -c 'echo "$PATH"' 2>/dev/null | tr ':' '\n' || true)
232+
else
233+
ZSH_PATHS=$(/bin/zsh -l -c 'echo "$PATH"' 2>/dev/null | tr ':' '\n' || true)
234+
fi
250235

251-
if [ "$MIGRATED" -gt 0 ]; then
252-
success "Migrated $MIGRATED PATH entries from zsh to fish."
236+
if [ -z "$ZSH_PATHS" ]; then
237+
warn "Could not read legacy zsh PATH or timed out. Skipping migration."
253238
else
254-
success "No additional PATH entries to migrate from zsh."
239+
FISH_PATHS=$(fish -l -c 'string join \n $PATH' 2>/dev/null)
240+
241+
MIGRATED=0
242+
while IFS= read -r p; do
243+
[ -z "$p" ] && continue
244+
[ ! -d "$p" ] && continue
245+
246+
# Skip system/default paths (already handled)
247+
case "$p" in
248+
/usr/bin|/bin|/usr/sbin|/sbin|/usr/local/bin|/opt/homebrew/bin|/opt/homebrew/sbin) continue ;;
249+
esac
250+
251+
# Check if path is already in fish
252+
if ! echo "$FISH_PATHS" | grep -qxF "$p"; then
253+
fish -c "fish_add_path --append '$p'" 2>/dev/null || true
254+
info " Added: $p"
255+
MIGRATED=$((MIGRATED + 1))
256+
fi
257+
done <<< "$ZSH_PATHS"
258+
259+
if [ "$MIGRATED" -gt 0 ]; then
260+
success "Migrated $MIGRATED PATH entries from zsh to fish."
261+
else
262+
success "No additional PATH entries to migrate from zsh."
263+
fi
255264
fi
256265
else
257266
info "zsh not found, skipping PATH migration."

0 commit comments

Comments
 (0)