diff --git a/README.md b/README.md index af8022afc..ad507ecbd 100644 --- a/README.md +++ b/README.md @@ -474,6 +474,22 @@ zstyle ':omz:update' frequency 7 zstyle ':omz:update' frequency 0 ``` +By default, updates always pull the latest changes. If you'd rather let others kick the tires first +before an update reaches your machine, you can set a cooldown (in days). You'll still get everything — +just a little later: + +```sh +# Only apply updates that are at least 10 days old +zstyle ':omz:update' cooldown 10 +``` + +`omz update` honors this setting. If you call `upgrade.sh` directly, pass the same +value with `-c`, because that script does not read your `.zshrc`: + +```sh +$ZSH/tools/upgrade.sh -c 10 +``` + ### Updates Verbosity You can also limit the update verbosity with the following settings: diff --git a/lib/cli.zsh b/lib/cli.zsh index a86434411..6605fe66d 100644 --- a/lib/cli.zsh +++ b/lib/cli.zsh @@ -903,8 +903,11 @@ function _omz::update { } # Run update script + local verbose_mode cooldown_days zstyle -s ':omz:update' verbose verbose_mode || verbose_mode=default - ZSH="$ZSH" command zsh -f "$ZSH/tools/upgrade.sh" -i -v $verbose_mode || return $? + zstyle -s ':omz:update' cooldown cooldown_days || cooldown_days=0 + [[ $cooldown_days == <-> ]] || cooldown_days=0 + ZSH="$ZSH" command zsh -f "$ZSH/tools/upgrade.sh" -i -v $verbose_mode -c $cooldown_days || return $? # Update last updated file zmodload zsh/datetime diff --git a/templates/zshrc.zsh-template b/templates/zshrc.zsh-template index fa83cc0c6..4e38f59e7 100644 --- a/templates/zshrc.zsh-template +++ b/templates/zshrc.zsh-template @@ -28,9 +28,12 @@ ZSH_THEME="robbyrussell" # zstyle ':omz:update' mode auto # update automatically without asking # zstyle ':omz:update' mode reminder # just remind me to update when it's time -# Uncomment the following line to change how often to auto-update (in days). +# Uncomment the following line to change the frequency the auto-updater is run (in days). # zstyle ':omz:update' frequency 13 +# Uncomment the following line to set how old an update must be before it's applied, manually or via the auto-updater (in days). +# zstyle ':omz:update' cooldown 10 + # Uncomment the following line if pasting URLs and other text is messed up. # DISABLE_MAGIC_FUNCTIONS="true" diff --git a/tools/check_for_upgrade.sh b/tools/check_for_upgrade.sh index 6f3ed5061..b40a98931 100644 --- a/tools/check_for_upgrade.sh +++ b/tools/check_for_upgrade.sh @@ -67,6 +67,26 @@ function is_update_available() { remote=${"$(builtin cd -q "$ZSH"; git config --local oh-my-zsh.remote)":-origin} remote_url=$(builtin cd -q "$ZSH"; git config remote.$remote.url) + local cooldown_days + zstyle -s ':omz:update' cooldown cooldown_days || cooldown_days=0 + [[ $cooldown_days == <-> ]] || cooldown_days=0 + + if (( cooldown_days > 0 )); then + local local_head cutoff_epoch cooldown_ref + local_head=$(builtin cd -q "$ZSH"; git rev-parse $branch 2>/dev/null) || return 0 + (builtin cd -q "$ZSH"; LANG= git fetch --quiet $remote $branch) || return 1 + zmodload zsh/datetime + cutoff_epoch=$(( EPOCHSECONDS - cooldown_days * 86400 )) + cooldown_ref=$(builtin cd -q "$ZSH"; git log --first-parent --format="%H %ct" FETCH_HEAD \ + | awk -v c="$cutoff_epoch" '$2 <= c { print $1; exit }') + [[ -n "$cooldown_ref" ]] || return 1 + [[ "$cooldown_ref" != "$local_head" ]] || return 1 + if (builtin cd -q "$ZSH"; git merge-base --is-ancestor "$cooldown_ref" "$local_head" 2>/dev/null); then + return 1 + fi + return 0 + fi + local repo case "$remote_url" in https://github.com/*) repo=${${remote_url#https://github.com/}%.git} ;; @@ -129,8 +149,10 @@ EOD } function update_ohmyzsh() { - local verbose_mode + local verbose_mode cooldown_days zstyle -s ':omz:update' verbose verbose_mode || verbose_mode=default + zstyle -s ':omz:update' cooldown cooldown_days || cooldown_days=0 + [[ $cooldown_days == <-> ]] || cooldown_days=0 # Force verbose mode to silent if p10k instant prompt is enabled if [[ ${POWERLEVEL9K_INSTANT_PROMPT:-off} != "off" ]]; then @@ -138,13 +160,13 @@ function update_ohmyzsh() { fi if [[ "$update_mode" != background-alpha ]] \ - && LANG= ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" -i -v $verbose_mode; then + && LANG= ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" -i -v $verbose_mode -c $cooldown_days; then update_last_updated_file return $? fi local exit_status error - if error=$(LANG= ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" -i -v silent 2>&1); then + if error=$(LANG= ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" -i -v silent -c $cooldown_days 2>&1); then update_last_updated_file 0 "Update successful" else exit_status=$? diff --git a/tools/upgrade.sh b/tools/upgrade.sh index 8a875e27e..22b3ac9a9 100755 --- a/tools/upgrade.sh +++ b/tools/upgrade.sh @@ -13,6 +13,8 @@ case "$ZSH_EVAL_CONTEXT" in *:file) echo "error: this file should not be sourced" && return 1 ;; esac +zmodload zsh/datetime + # Define "$ZSH" if not defined -- in theory this should be `export`ed by the calling script if [[ -z "$ZSH" ]]; then ZSH="${0:a:h:h}" @@ -22,8 +24,9 @@ cd "$ZSH" verbose_mode="default" interactive=false +cooldown_days=0 -while getopts "v:i" opt; do +while getopts "v:ic:" opt; do case $opt in v) if [[ $OPTARG == default || $OPTARG == minimal || $OPTARG == silent ]]; then @@ -34,6 +37,14 @@ while getopts "v:i" opt; do fi ;; i) interactive=true ;; + c) + if [[ $OPTARG == <-> ]]; then + cooldown_days=$OPTARG + else + echo "[oh-my-zsh] update cooldown '$OPTARG' is not valid" + echo "[oh-my-zsh] valid options are a non-negative integer (days)" + fi + ;; esac done @@ -231,6 +242,28 @@ local ret=0 remote=${"$(git config --local oh-my-zsh.remote)":-origin} branch=${"$(git config --local oh-my-zsh.branch)":-master} +update_with_cooldown() { + local cutoff_epoch cooldown_ref + + cutoff_epoch=$(( EPOCHSECONDS - cooldown_days * 86400 )) + LANG= git fetch --quiet $remote $branch || return $? + + cooldown_ref=$(git log --first-parent --format="%H %ct" FETCH_HEAD \ + | awk -v c="$cutoff_epoch" '$2 <= c { print $1; exit }') + + [[ -n "$cooldown_ref" ]] || return 0 + + LANG= git merge --ff-only --quiet "$cooldown_ref" +} + +perform_update() { + if (( cooldown_days > 0 )); then + update_with_cooldown + else + LANG= git pull --quiet --rebase $remote $branch + fi +} + # repository state last_head=$(git symbolic-ref --quiet --short HEAD || git rev-parse HEAD) # checkout update branch @@ -242,10 +275,17 @@ last_commit=$(git rev-parse "$branch") if [[ $verbose_mode != silent ]]; then printf "${BLUE}%s${RESET}\n" "Updating Oh My Zsh" fi -if LANG= git pull --quiet --rebase $remote $branch; then +if perform_update; then # Check if it was really updated or not if [[ "$(git rev-parse HEAD)" = "$last_commit" ]]; then - message="Oh My Zsh is already at the latest version." + if (( cooldown_days > 0 )); then + head_ct=$(git log -1 --format=%ct HEAD) + age_days=$(( (EPOCHSECONDS - head_ct) / 86400 )) + (( age_days < 0 )) && age_days=0 + message="Oh My Zsh is already at a version ${age_days} days old." + else + message="Oh My Zsh is already at the latest version." + fi else message="Hooray! Oh My Zsh has been updated!"