24d5b7445f
- install.sh: - 滚动更新流程新增「选择 Git 访问方式」步骤(HTTPS 登录 / SSH 访问) - SSH 方式支持指定私钥文件路径或粘贴私钥内容(临时文件用后即删) - 仓库地址输入回填当前 remote(同协议时)作为默认值 - 选环境后立即检测工作区变更,提供暂存/放弃/取消处理 - 更新结束后按 HEAD 是否变化决定自动恢复暂存或提示手动恢复 - scripts/update.sh: - 新增 --ssh-key 参数,SSH 协议下通过 GIT_SSH_COMMAND -i 指定私钥 - 新增 --skip-dirty-check 参数,供 install.sh 预检后透传 - scripts/common.sh: - ctms_ask_line 提示语改输出到 stderr,避免被命令替换吞掉 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
167 lines
5.4 KiB
Bash
Executable File
167 lines
5.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=scripts/common.sh
|
|
source "$SCRIPT_DIR/common.sh"
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
${C_BOLD}用法:${C_RESET}
|
|
bash scripts/status.sh <环境>
|
|
|
|
${C_BOLD}环境:${C_RESET}
|
|
dev | main | release
|
|
|
|
${C_BOLD}说明:${C_RESET}
|
|
交互终端下动态刷新容器资源面板(按 q 或 Enter 返回);
|
|
非交互终端或带 --once 时只输出一次快照。
|
|
|
|
${C_BOLD}选项:${C_RESET}
|
|
--once 仅输出一次快照,不进入动态刷新
|
|
EOF
|
|
}
|
|
|
|
STATS_OUTPUT=""
|
|
STATS_ERROR=""
|
|
STATUS_BOX_WIDTH=127
|
|
STATUS_REFRESH_INTERVAL=2
|
|
|
|
collect_compose_stats() {
|
|
local env="$1"
|
|
local output="" exit_code=0
|
|
STATS_OUTPUT=""
|
|
STATS_ERROR=""
|
|
|
|
set +e
|
|
output="$(ctms_compose "$env" stats --all --no-stream \
|
|
--format '{{.Name}}|{{.CPUPerc}}|{{.MemUsage}}|{{.MemPerc}}|{{.NetIO}}|{{.BlockIO}}|{{.PIDs}}' 2>&1)"
|
|
exit_code=$?
|
|
set -e
|
|
|
|
if [[ "$exit_code" -ne 0 ]]; then
|
|
set +e
|
|
output="$(ctms_compose "$env" stats --all --no-stream \
|
|
--format '{{.Container}}|{{.CPUPerc}}|{{.MemUsage}}|{{.MemPerc}}|{{.NetIO}}|{{.BlockIO}}|{{.PIDs}}' 2>&1)"
|
|
exit_code=$?
|
|
set -e
|
|
fi
|
|
|
|
if [[ "$exit_code" -ne 0 ]]; then
|
|
STATS_ERROR="$output"
|
|
return "$exit_code"
|
|
fi
|
|
|
|
STATS_OUTPUT="$output"
|
|
}
|
|
|
|
render_stats_panel() {
|
|
local env="$1"
|
|
local project="$2"
|
|
local generated_at
|
|
generated_at="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
|
|
printf '\n'
|
|
ctms_box_top "${CC_PRIMARY}${C_BOLD}" "CTMS 资源状态快照" "$STATUS_BOX_WIDTH"
|
|
ctms_box_line "${CC_PRIMARY}${C_BOLD}" "$STATUS_BOX_WIDTH" "环境 ${env} Compose 项目 ${project} 时间 ${generated_at}" "${CC_MUTED}环境${C_RESET} ${CC_INFO}${C_BOLD}${env}${C_RESET} ${CC_MUTED}Compose 项目${C_RESET} ${CC_INFO}${C_BOLD}${project}${C_RESET} ${CC_MUTED}时间 ${generated_at}${C_RESET}"
|
|
ctms_box_bottom "${CC_PRIMARY}${C_BOLD}" "$STATUS_BOX_WIDTH"
|
|
printf '\n'
|
|
|
|
if [[ -n "$STATS_ERROR" ]]; then
|
|
printf ' %s%s%s %s无法获取 docker compose stats 快照%s\n' "${CC_DANGER}${C_BOLD}" "$ICON_FAIL" "${C_RESET}" "${CC_DANGER}" "${C_RESET}"
|
|
printf ' %s环境%s %s %sCompose 项目%s %s\n' "${CC_MUTED}" "${C_RESET}" "$env" "${CC_MUTED}" "${C_RESET}" "$project"
|
|
printf ' %s原因%s %s\n\n' "${CC_MUTED}" "${C_RESET}" "$STATS_ERROR"
|
|
return 1
|
|
fi
|
|
|
|
if [[ -z "$STATS_OUTPUT" ]]; then
|
|
printf ' %s%s%s %s未获取到容器资源数据%s\n' "${CC_WARN}${C_BOLD}" "$ICON_WARN" "${C_RESET}" "${CC_WARN}" "${C_RESET}"
|
|
printf ' %s可能原因:该环境尚未部署,或所有容器已被移除。%s\n\n' "${CC_MUTED}" "${C_RESET}"
|
|
return
|
|
fi
|
|
|
|
ctms_box_top "${CC_ACCENT}${C_BOLD}" "容器资源" "$STATUS_BOX_WIDTH"
|
|
# 表头含中文(双宽),用 ctms_pad_right 按显示宽度对齐,列宽与下方数据行的 %-Ns 一致
|
|
local header
|
|
header="$(ctms_pad_right "容器" 22) $(ctms_pad_right "CPU" 9) $(ctms_pad_right "内存使用 / 限制" 27) $(ctms_pad_right "内存%" 8) $(ctms_pad_right "网络 I/O" 21) $(ctms_pad_right "磁盘 I/O" 21) $(ctms_pad_right "PIDs" 6)"
|
|
ctms_box_line "${CC_ACCENT}${C_BOLD}" "$STATUS_BOX_WIDTH" "$header" "${CC_MUTED}${header}${C_RESET}"
|
|
printf '%s├%s┤%s\n' "${CC_ACCENT}${C_BOLD}" "$(ctms_repeat_char '─' "$(( STATUS_BOX_WIDTH - 2 ))")" "${C_RESET}"
|
|
|
|
printf '%s\n' "$STATS_OUTPUT" | while IFS='|' read -r name cpu mem mem_percent net_io block_io pids; do
|
|
[[ -n "$name" ]] || continue
|
|
ctms_box_line "${CC_ACCENT}${C_BOLD}" "$STATUS_BOX_WIDTH" "$(printf '%-22.22s %-9.9s %-27.27s %-8.8s %-21.21s %-21.21s %-6.6s' "$name" "$cpu" "$mem" "$mem_percent" "$net_io" "$block_io" "$pids")"
|
|
done
|
|
|
|
ctms_box_bottom "${CC_ACCENT}${C_BOLD}" "$STATUS_BOX_WIDTH"
|
|
printf '\n'
|
|
printf ' %s数据来源%s docker compose stats --all --no-stream\n\n' "${CC_MUTED}" "${C_RESET}"
|
|
}
|
|
|
|
run_dynamic() {
|
|
local env="$1" project="$2"
|
|
local key=""
|
|
|
|
# 隐藏光标,退出时(含 Ctrl-C)务必恢复并清屏,避免终端残留
|
|
printf '\033[?25l'
|
|
trap 'ctms_restore_cursor; printf "\033[2J\033[H"; exit 0' INT TERM
|
|
|
|
while true; do
|
|
collect_compose_stats "$env" || true
|
|
# 定位左上角重绘,再清除光标之后的残留行(容器数变化时不留尾巴)
|
|
printf '\033[H'
|
|
render_stats_panel "$env" "$project" || true
|
|
printf ' %s每 %ss 自动刷新%s %s按 q 或 Enter 返回菜单%s\n' \
|
|
"${CC_MUTED}" "$STATUS_REFRESH_INTERVAL" "${C_RESET}" \
|
|
"${CC_INFO}${C_BOLD}" "${C_RESET}"
|
|
printf '\033[J'
|
|
|
|
# 刷新间隔内监听退出键;超时(无输入)则继续刷新
|
|
key=""
|
|
if IFS= read -rsn1 -t "$STATUS_REFRESH_INTERVAL" key; then
|
|
case "$key" in
|
|
q|Q|'') break ;; # q/Q 或 Enter 退出
|
|
esac
|
|
fi
|
|
done
|
|
|
|
trap - INT TERM
|
|
ctms_restore_cursor
|
|
printf '\033[2J\033[H'
|
|
}
|
|
|
|
main() {
|
|
local once=0
|
|
local -a positional=()
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--once) once=1; shift ;;
|
|
*) positional+=("$1"); shift ;;
|
|
esac
|
|
done
|
|
|
|
if [[ "${#positional[@]}" -ne 1 ]]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
local env="${positional[0]}"
|
|
local project=""
|
|
ctms_require_env "$env"
|
|
project="$(ctms_project_name "$env")"
|
|
|
|
ctms_require_docker_compose
|
|
cd "$CTMS_ROOT_DIR"
|
|
|
|
# 非交互终端或显式 --once:只输出一次快照
|
|
if [[ "$once" -eq 1 || ! -t 1 || ! -t 0 ]]; then
|
|
collect_compose_stats "$env" || true
|
|
render_stats_panel "$env" "$project"
|
|
return
|
|
fi
|
|
|
|
printf '\033[2J'
|
|
run_dynamic "$env" "$project"
|
|
}
|
|
|
|
main "$@"
|