From 46275bb3e2263c9b198d1d01c57fc3d739d440db Mon Sep 17 00:00:00 2001 From: newbieQQ Date: Fri, 1 May 2026 20:35:30 +0800 Subject: [PATCH] update: enhance dependency installation process and state management Co-authored-by: Copilot --- README.md | 28 ++++- config/conda.zsh | 39 +++++-- config/init.zsh | 147 ++++++++++++++++++++++++++ lib/omz.sh | 263 +++++++++++++++++++++++++++++++++++++++++++++++ omz.zsh | 5 +- 5 files changed, 472 insertions(+), 10 deletions(-) create mode 100644 config/init.zsh diff --git a/README.md b/README.md index c12b548..4dd4bf9 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,34 @@ fork自 oh my zsh,更纯净 更快速 # 在你的zsh配置里 source ~/.config/omz/omz.zsh (举例) echo "source ~/.config/omz/omz.zsh" >> ~/.zshrc - # 请务必安装fzf和lua 依赖 + # 重新加载zsh配置 + source ~/.zshrc + + # 首次加载会询问是否安装依赖(conda / fzf / fd / lua) + # 如果选择 N,会写入 defer 标记,不再自动安装 + # 后续可手动执行 inttall 触发首次安装 + + # 首次安装完成后会写入 managed 标记 + # 后续执行 install(不带参数)会走更新流程 + + # 若你需要系统原生 install 命令,正常带参数即可 + # 例如 install -m 644 ./a ./b ``` +依赖安装状态文件在 `cache/install.state`: + +- `deferred`: 已拒绝自动安装,等待手动 `inttall` +- `managed`: 已接管依赖管理,后续 `install` 为更新 + +可选环境变量: + +- `_OMZ_INSTALL_PROMPT=false` 关闭首次询问 +- `_OMZ_ENABLE_INSTALL_COMMAND=false` 不接管 `install` 空命令 +- `_OMZ_PREFER_FLATPAK=true` 依赖安装优先尝试 flatpak(默认 true) +- `_OMZ_FLATPAK_APP_FZF=...` 自定义 fzf 的 flatpak app id +- `_OMZ_FLATPAK_APP_FD=...` 自定义 fd 的 flatpak app id +- `_OMZ_FLATPAK_APP_LUA=...` 自定义 lua 的 flatpak app id + ## UPDATE ```plaintext @@ -60,6 +85,7 @@ omz │   ├── fzf.zsh -- fzf 及 fzf-tab配置 │   ├── git.zsh -- git相关配置 │   ├── hook.zsh -- 命令或启动钩子配置 + │   ├── init.zsh -- 依赖安装初始化及状态管理 │   └── omz.zsh -- omz基础配置 ├── plugins/ -- 插件 ├── themes/ -- 主题 diff --git a/config/conda.zsh b/config/conda.zsh index 07c73f7..93d9292 100644 --- a/config/conda.zsh +++ b/config/conda.zsh @@ -1,17 +1,40 @@ # >>> conda initialize >>> # !! Contents within this block are managed by 'conda init' !! -__conda_setup="$('/home/newbie/.local/share/miniconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)" -if [ $? -eq 0 ]; then - eval "$__conda_setup" -else - if [ -f "/home/newbie/.local/share/miniconda3/etc/profile.d/conda.sh" ]; then - . "/home/newbie/.local/share/miniconda3/etc/profile.d/conda.sh" +if command -v conda >/dev/null 2>&1; then + __conda_setup="$(conda shell.zsh hook 2> /dev/null)" + if [ $? -eq 0 ]; then + eval "$__conda_setup" else - export PATH="/home/newbie/.local/share/miniconda3/bin:$PATH" + _OMZ_CONDA_BIN_DIR="$(cd "$(dirname "$(command -v conda)")" && pwd)" + if [ -f "${_OMZ_CONDA_BIN_DIR}/../etc/profile.d/conda.sh" ]; then + . "${_OMZ_CONDA_BIN_DIR}/../etc/profile.d/conda.sh" + else + export PATH="${_OMZ_CONDA_BIN_DIR}:$PATH" + fi + unset _OMZ_CONDA_BIN_DIR fi + unset __conda_setup +fi + +if command -v conda >/dev/null 2>&1; then + _OMZ_CONDA_EXE="${CONDA_EXE:-$(whence -p conda 2>/dev/null)}" + if [ -x "$_OMZ_CONDA_EXE" ]; then + _OMZ_CONDA_ROOT="$(cd "$(dirname "$_OMZ_CONDA_EXE")/.." && pwd)" + _OMZ_CONDA_COMP_DIR="${_OMZ_CONDA_ROOT}/share/zsh/site-functions" + + if [ -d "$_OMZ_CONDA_COMP_DIR" ] && [ -f "$_OMZ_CONDA_COMP_DIR/_conda" ]; then + (( ${fpath[(I)$_OMZ_CONDA_COMP_DIR]} == 0 )) && fpath=("$_OMZ_CONDA_COMP_DIR" $fpath) + autoload -Uz _conda + compdef _conda conda + fi + + unset _OMZ_CONDA_ROOT + unset _OMZ_CONDA_COMP_DIR + fi + unset _OMZ_CONDA_EXE fi -unset __conda_setup # <<< conda initialize <<< alias ca="conda activate" +compdef ca=conda 2>/dev/null || true diff --git a/config/init.zsh b/config/init.zsh new file mode 100644 index 0000000..b3c9f7b --- /dev/null +++ b/config/init.zsh @@ -0,0 +1,147 @@ +_OMZ_INSTALL_STATE_FILE="$OMZ/cache/install.state" + +_omz_read_install_state() { + if [ -f "$_OMZ_INSTALL_STATE_FILE" ]; then + cat "$_OMZ_INSTALL_STATE_FILE" 2>/dev/null + fi +} + +_omz_write_install_state() { + local state="$1" + mkdir -p "$OMZ/cache" + echo "$state" > "$_OMZ_INSTALL_STATE_FILE" +} + +_omz_run_install_steps() { + local prev_auto_install prev_retry_seconds failed + prev_auto_install="${_OMZ_AUTO_INSTALL:-}" + prev_retry_seconds="${_OMZ_AUTO_INSTALL_RETRY_SECONDS:-}" + _OMZ_AUTO_INSTALL=true + _OMZ_AUTO_INSTALL_RETRY_SECONDS=0 + failed=0 + + _omz_install_fd || failed=1 + _omz_install_fzf || failed=1 + _omz_install_lua || failed=1 + _omz_install_conda || failed=1 + + if [ -n "$prev_auto_install" ]; then + _OMZ_AUTO_INSTALL="$prev_auto_install" + else + unset _OMZ_AUTO_INSTALL + fi + + if [ -n "$prev_retry_seconds" ]; then + _OMZ_AUTO_INSTALL_RETRY_SECONDS="$prev_retry_seconds" + else + unset _OMZ_AUTO_INSTALL_RETRY_SECONDS + fi + + return $failed +} + +_omz_run_update_steps() { + local prev_retry_seconds failed + prev_retry_seconds="${_OMZ_AUTO_INSTALL_RETRY_SECONDS:-}" + _OMZ_AUTO_INSTALL_RETRY_SECONDS=0 + failed=0 + + if _omz_has_cmd fd; then + _omz_install_with_pkg_manager "fd-find" || _omz_install_with_pkg_manager "fd" || failed=1 + else + _omz_install_fd || failed=1 + fi + + if [ -d "$HOME/.fzf/.git" ] && _omz_has_cmd git; then + git -C "$HOME/.fzf" pull --ff-only || failed=1 + "$HOME/.fzf/install" --key-bindings --completion --no-bash --no-fish --no-update-rc || failed=1 + elif _omz_has_cmd fzf; then + _omz_install_with_pkg_manager "fzf" || failed=1 + else + _omz_install_fzf || failed=1 + fi + + if _omz_has_cmd lua; then + _omz_install_with_pkg_manager "lua" || _omz_install_with_pkg_manager "lua5.4" || failed=1 + else + _omz_install_lua || failed=1 + fi + + if _omz_has_cmd conda; then + conda update -n base -y conda || failed=1 + else + _omz_install_conda || failed=1 + fi + + if [ -n "$prev_retry_seconds" ]; then + _OMZ_AUTO_INSTALL_RETRY_SECONDS="$prev_retry_seconds" + else + unset _OMZ_AUTO_INSTALL_RETRY_SECONDS + fi + + return $failed +} + +omz_install() { + local state + state="$(_omz_read_install_state)" + + if [ "$state" = "managed" ]; then + echo "[omz] install mode=managed, running dependency update..." + _omz_run_update_steps || { + echo "[omz] some dependencies failed to update" + return 1 + } + echo "[omz] dependency update finished" + return 0 + fi + + echo "[omz] running first-time dependency install..." + _omz_run_install_steps || { + echo "[omz] some dependencies failed to install" + return 1 + } + + _omz_write_install_state "managed" + echo "[omz] install finished, future install will perform update" +} + +inttall() { + omz_install "$@" +} + +if [[ "${_OMZ_ENABLE_INSTALL_COMMAND:-true}" == "true" ]]; then + install() { + if [ "$#" -eq 0 ]; then + omz_install + else + command install "$@" + fi + } +fi + +_omz_init_install_prompt() { + local state answer + [[ -o interactive ]] || return 0 + [[ "${_OMZ_INSTALL_PROMPT:-true}" == "true" ]] || return 0 + + state="$(_omz_read_install_state)" + if [ "$state" = "managed" ] || [ "$state" = "deferred" ]; then + return 0 + fi + + printf "[omz] Install dependencies now (fzf/fd/lua/conda)? [y/N]: " + read -r answer + + case "$answer" in + y|Y|yes|YES) + omz_install + ;; + *) + _omz_write_install_state "deferred" + echo "[omz] dependencies skipped, run 'inttall' to install later" + ;; + esac +} + +_omz_init_install_prompt diff --git a/lib/omz.sh b/lib/omz.sh index 5d5fd24..d4fffaa 100644 --- a/lib/omz.sh +++ b/lib/omz.sh @@ -5,6 +5,269 @@ SHORT_HOST=${HOST/.*/} autoload -Uz add-zsh-hook zmodload -i zsh/complist unsetopt correct + +_omz_auto_install_enabled() { + [[ "${_OMZ_AUTO_INSTALL:-true}" == "true" ]] && [[ -o interactive ]] +} + +_omz_can_retry_install() { + local key="$1" + local cooldown_seconds now ts_file ts + + cooldown_seconds=${_OMZ_AUTO_INSTALL_RETRY_SECONDS:-86400} + ts_file="$OMZ/cache/.auto_install_${key}.ts" + now=$(date +%s) + + [[ ! -f "$ts_file" ]] && return 0 + + ts=$(cat "$ts_file" 2>/dev/null || true) + [[ -z "$ts" ]] && return 0 + + (( now - ts >= cooldown_seconds )) +} + +_omz_mark_install_attempt() { + local key="$1" + mkdir -p "$OMZ/cache" + date +%s > "$OMZ/cache/.auto_install_${key}.ts" +} + +_omz_has_cmd() { + command -v "$1" >/dev/null 2>&1 +} + +_omz_should_prefer_flatpak() { + [[ "${_OMZ_PREFER_FLATPAK:-true}" == "true" ]] +} + +_omz_detect_pkg_manager() { + if _omz_should_prefer_flatpak && _omz_has_cmd flatpak; then + echo "flatpak" + elif _omz_has_cmd apt-get; then + echo "apt" + elif _omz_has_cmd dnf; then + echo "dnf" + elif _omz_has_cmd yum; then + echo "yum" + elif _omz_has_cmd pacman; then + echo "pacman" + elif _omz_has_cmd zypper; then + echo "zypper" + elif _omz_has_cmd apk; then + echo "apk" + elif _omz_has_cmd brew; then + echo "brew" + else + echo "" + fi +} + +_omz_get_flatpak_app_id() { + local pkg="$1" + case "$pkg" in + fzf) + echo "${_OMZ_FLATPAK_APP_FZF:-io.github.junegunn.fzf}" + ;; + fd|fd-find) + echo "${_OMZ_FLATPAK_APP_FD:-io.github.sharkdp.fd}" + ;; + lua|lua5.4) + echo "${_OMZ_FLATPAK_APP_LUA:-org.lua.Lua}" + ;; + *) + echo "" + ;; + esac +} + +_omz_install_with_flatpak() { + local pkg="$1" + local app_id + + _omz_has_cmd flatpak || return 1 + app_id="$(_omz_get_flatpak_app_id "$pkg")" + [[ -n "$app_id" ]] || return 1 + + flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo >/dev/null 2>&1 || return 1 + flatpak install -y --noninteractive flathub "$app_id" || return 1 + + return 0 +} + +_omz_run_install_cmd() { + if (( EUID == 0 )); then + "$@" + return $? + fi + + if _omz_has_cmd sudo; then + sudo "$@" + return $? + fi + + return 1 +} + +_omz_install_with_pkg_manager() { + local pkg="$1" + local manager + manager=$(_omz_detect_pkg_manager) + + case "$manager" in + flatpak) + _omz_install_with_flatpak "$pkg" || { + if _omz_has_cmd apt-get; then + _omz_run_install_cmd apt-get update && _omz_run_install_cmd apt-get install -y "$pkg" + elif _omz_has_cmd dnf; then + _omz_run_install_cmd dnf install -y "$pkg" + elif _omz_has_cmd yum; then + _omz_run_install_cmd yum install -y "$pkg" + elif _omz_has_cmd pacman; then + _omz_run_install_cmd pacman -Sy --noconfirm "$pkg" + elif _omz_has_cmd zypper; then + _omz_run_install_cmd zypper --non-interactive install "$pkg" + elif _omz_has_cmd apk; then + _omz_run_install_cmd apk add "$pkg" + elif _omz_has_cmd brew; then + brew install "$pkg" + else + return 1 + fi + } + ;; + apt) + _omz_run_install_cmd apt-get update && _omz_run_install_cmd apt-get install -y "$pkg" + ;; + dnf) + _omz_run_install_cmd dnf install -y "$pkg" + ;; + yum) + _omz_run_install_cmd yum install -y "$pkg" + ;; + pacman) + _omz_run_install_cmd pacman -Sy --noconfirm "$pkg" + ;; + zypper) + _omz_run_install_cmd zypper --non-interactive install "$pkg" + ;; + apk) + _omz_run_install_cmd apk add "$pkg" + ;; + brew) + brew install "$pkg" + ;; + *) + return 1 + ;; + esac +} + +_omz_install_fd() { + _omz_has_cmd fd && return 0 + _omz_auto_install_enabled || return 1 + _omz_can_retry_install "fd" || return 1 + _omz_mark_install_attempt "fd" + + local manager pkg + manager=$(_omz_detect_pkg_manager) + pkg="fd" + [[ "$manager" == "apt" ]] && pkg="fd-find" + + echo "[omz] fd not found, installing ${pkg}..." + _omz_install_with_pkg_manager "$pkg" || return 1 + + if _omz_has_cmd fd; then + return 0 + fi + + if _omz_has_cmd fdfind; then + mkdir -p "$HOME/.local/bin" + ln -sf "$(command -v fdfind)" "$HOME/.local/bin/fd" + export PATH="$HOME/.local/bin:$PATH" + fi + + _omz_has_cmd fd +} + +_omz_install_fzf() { + _omz_has_cmd fzf && return 0 + _omz_auto_install_enabled || return 1 + _omz_can_retry_install "fzf" || return 1 + _omz_mark_install_attempt "fzf" + + echo "[omz] fzf not found, trying package manager install..." + _omz_install_with_pkg_manager "fzf" || true + _omz_has_cmd fzf && return 0 + + if [[ ! -d "$HOME/.fzf" ]] && _omz_has_cmd git; then + echo "[omz] fallback: install fzf from github to ~/.fzf" + git clone --depth 1 https://github.com/junegunn/fzf.git "$HOME/.fzf" || return 1 + "$HOME/.fzf/install" --key-bindings --completion --no-bash --no-fish --no-update-rc || return 1 + fi + + _omz_has_cmd fzf +} + +_omz_install_lua() { + _omz_has_cmd lua && return 0 + _omz_auto_install_enabled || return 1 + _omz_can_retry_install "lua" || return 1 + _omz_mark_install_attempt "lua" + + echo "[omz] lua not found, installing..." + _omz_install_with_pkg_manager "lua" || _omz_install_with_pkg_manager "lua5.4" + _omz_has_cmd lua +} + +_omz_install_conda() { + _omz_has_cmd conda && return 0 + _omz_auto_install_enabled || return 1 + _omz_can_retry_install "conda" || return 1 + _omz_mark_install_attempt "conda" + + local installer target_dir tmp_file arch os_name + target_dir="$HOME/.local/share/miniconda3" + os_name="$(uname -s)" + arch="$(uname -m)" + + case "$os_name" in + Linux) os_name="Linux" ;; + Darwin) os_name="MacOSX" ;; + *) + echo "[omz] unsupported OS for auto conda install: $os_name" + return 1 + ;; + esac + + case "$arch" in + x86_64|amd64) arch="x86_64" ;; + aarch64|arm64) arch="aarch64" ;; + *) + echo "[omz] unsupported CPU for auto conda install: $arch" + return 1 + ;; + esac + + installer="Miniconda3-latest-${os_name}-${arch}.sh" + tmp_file="/tmp/${installer}" + + echo "[omz] conda not found, installing miniconda..." + if _omz_has_cmd curl; then + curl -fsSL "https://repo.anaconda.com/miniconda/${installer}" -o "$tmp_file" || return 1 + elif _omz_has_cmd wget; then + wget -q "https://repo.anaconda.com/miniconda/${installer}" -O "$tmp_file" || return 1 + else + echo "[omz] curl or wget is required to install conda" + return 1 + fi + + bash "$tmp_file" -b -p "$target_dir" || return 1 + rm -f "$tmp_file" + + export PATH="$target_dir/bin:$PATH" + _omz_has_cmd conda +} + autoload -U compaudit compinit autoload -U compinit && compinit setopt auto_pushd diff --git a/omz.zsh b/omz.zsh index 26feb81..b92733d 100644 --- a/omz.zsh +++ b/omz.zsh @@ -1,6 +1,9 @@ -export OMZ=$(cd $(dirname $0);pwd) +if [ -z "${OMZ:-}" ]; then + export OMZ="${${(%):-%N}:A:h}" +fi export UEBERZUGPP_PID_FILE=$OMZ/cache/ubpidfile source $OMZ/config/omz.zsh +source $OMZ/config/init.zsh source $OMZ/config/git.zsh source $OMZ/config/fzf.zsh source $OMZ/config/hook.zsh