check_for_upgrade.sh: Update Process
Visual State Diagram
State Transition Table
BOOTSTRAP Phase
| State |
Condition |
Command/Check |
Next State |
| START |
— |
— |
CheckLegacyUpdateFile |
| CheckLegacyUpdateFile |
[[ -f ~/.zsh-update && ! -f $ZSH_CACHE_DIR/.zsh-update ]] |
TRUE |
MigrateLegacyFile |
| CheckLegacyUpdateFile |
— |
FALSE (no legacy file) |
LoadMode |
| MigrateLegacyFile |
— |
mv ~/.zsh-update $ZSH_CACHE_DIR/.zsh-update |
LoadMode |
| LoadMode |
zstyle -s ':omz:update' mode update_mode |
SUCCESS |
PreFlightGate |
| LoadMode |
— |
FAIL (zstyle missing) |
LegacyModeFallback |
| LegacyModeFallback |
— |
set update_mode=prompt (default) |
LegacyPromptFlag |
| LegacyPromptFlag |
[[ $DISABLE_UPDATE_PROMPT != true ]] |
TRUE |
LegacyAutoFlag |
| LegacyPromptFlag |
— |
FALSE |
SetAutoMode |
| SetAutoMode |
— |
update_mode=auto |
LegacyAutoFlag |
| LegacyAutoFlag |
[[ $DISABLE_AUTO_UPDATE != true ]] |
TRUE |
PreFlightGate |
| LegacyAutoFlag |
— |
FALSE |
SetDisabledMode |
| SetDisabledMode |
— |
update_mode=disabled |
PreFlightGate |
PRE-FLIGHT GATE (Early Exit Checks)
| State |
Condition |
Command/Check |
Next State |
| PreFlightGate |
[[ $update_mode = disabled ]] |
TRUE |
ExitNoUpdate |
| PreFlightGate |
[[ ! -w $ZSH || ! -O $ZSH ]] |
TRUE |
ExitNoUpdate |
| PreFlightGate |
[[ ! -t 1 && ${POWERLEVEL9K_INSTANT_PROMPT:-off} == off ]] |
TRUE |
ExitNoUpdate |
| PreFlightGate |
! command git --version 2>&1 >/dev/null |
TRUE |
ExitNoUpdate |
| PreFlightGate |
(cd $ZSH; ! git rev-parse --is-inside-work-tree &>/dev/null) |
TRUE |
ExitNoUpdate |
| PreFlightGate |
— |
ALL FALSE |
ModeDispatch |
| ExitNoUpdate |
— |
unset update_mode; return |
[*] |
MODE DISPATCH & BACKGROUND SETUP
| State |
Condition |
Command/Check |
Next State |
| ModeDispatch |
[[ $update_mode = background-alpha ]] |
TRUE |
SetupBackgroundHooks |
| ModeDispatch |
— |
FALSE |
RunHandleUpdate |
| SetupBackgroundHooks |
— |
autoload -Uz add-zsh-hook |
RegisterBgPrecmd |
| RegisterBgPrecmd |
— |
add-zsh-hook precmd _omz_bg_update |
BackgroundStatusHook |
| RunHandleUpdate |
— |
enter HandleUpdateCore |
HandleUpdateCore |
HANDLE UPDATE CORE (Main Logic)
Lock Management
| State |
Condition |
Command/Check |
Next State |
| HandleUpdateCore |
— |
zmodload zsh/datetime |
LockCleanupCheck |
| LockCleanupCheck |
zstat +mtime $ZSH/log/update.lock 2>/dev/null |
SUCCESS (mtime exists) |
CheckStaleAge |
| LockCleanupCheck |
— |
FAIL (no lock file) |
AcquireLock |
| CheckStaleAge |
(mtime + 86400) < EPOCHSECONDS |
TRUE (older than 24h) |
RemoveStaleLock |
| CheckStaleAge |
— |
FALSE |
AcquireLock |
| RemoveStaleLock |
— |
rm -rf $ZSH/log/update.lock |
AcquireLock |
| AcquireLock |
mkdir $ZSH/log/update.lock 2>/dev/null |
SUCCESS |
InstallTrap |
| AcquireLock |
— |
FAIL (lock exists) |
ExitHandle |
| InstallTrap |
— |
trap "...cleanup..." EXIT INT QUIT |
LoadStatusFile |
Status File Validation
| State |
Condition |
Command/Check |
Next State |
| LoadStatusFile |
source $ZSH_CACHE_DIR/.zsh-update 2>/dev/null && [[ -n $LAST_EPOCH ]] |
SUCCESS |
FrequencyCheck |
| LoadStatusFile |
— |
FAIL or missing LAST_EPOCH |
InitStatusFile |
| InitStatusFile |
— |
update_last_updated_file (writes LAST_EPOCH) |
ExitHandle |
Frequency Check
| State |
Condition |
Command/Check |
Next State |
| FrequencyCheck |
zstyle -s ':omz:update' frequency epoch_target |
SUCCESS |
PeriodElapsed |
| FrequencyCheck |
— |
FAIL |
SetDefaultFrequency |
| SetDefaultFrequency |
— |
epoch_target=${UPDATE_ZSH_DAYS:-13} |
PeriodElapsed |
| PeriodElapsed |
(current_epoch - LAST_EPOCH) >= epoch_target |
TRUE |
RepoCheck |
| PeriodElapsed |
— |
FALSE |
ExitHandle |
Git Repository Check
| State |
Condition |
Command/Check |
Next State |
| RepoCheck |
(cd $ZSH && LANG= git rev-parse) |
SUCCESS |
CheckUpdateAvailable |
| RepoCheck |
— |
FAIL |
ExitHandle |
Post-Update Decision
| State |
Condition |
Command/Check |
Next State |
| CheckUpdateAvailable |
— |
function returns TRUE |
ReminderOrTypedInput |
| CheckUpdateAvailable |
— |
function returns FALSE |
ExitHandleWithTimestamp |
| ExitHandleWithTimestamp |
— |
update_last_updated_file |
ExitHandle |
REMINDER OR INPUT CHECK
| State |
Condition |
Command/Check |
Next State |
| ReminderOrTypedInput |
[[ $update_mode = reminder ]] |
TRUE |
ReminderExit |
| ReminderOrTypedInput |
[[ $update_mode != background-alpha ]] |
TRUE |
TypedInputCheck |
| ReminderOrTypedInput |
— |
FALSE (background-alpha) |
ModeAutoGate |
| TypedInputCheck |
has_typed_input |
TRUE |
ReminderExit |
| TypedInputCheck |
— |
FALSE |
ModeAutoGate |
| ReminderExit |
— |
echo "[oh-my-zsh] It's time to update!..." |
ExitHandle |
has_typed_input internals:
stty --save
stty -icanon
zselect -t 0 -r 0 (poll stdin fd 0)
stty $termios (restore)
UPDATE MODE GATE
| State |
Condition |
Command/Check |
Next State |
| ModeAutoGate |
[[ $update_mode = (auto|background-alpha) ]] |
TRUE |
RunUpgrade |
| ModeAutoGate |
— |
FALSE (prompt) |
PromptUser |
Prompt Mode
| State |
Condition |
Command/Check |
Next State |
| PromptUser |
— |
read -r -k 1 option |
ProcessResponse |
| ProcessResponse |
[[ $option = [yY$'\n'] ]] |
TRUE |
RunUpgrade |
| ProcessResponse |
[[ $option = [nN] ]] |
TRUE |
WriteTimestampOnly |
| ProcessResponse |
— |
OTHER |
ManualMsg |
| WriteTimestampOnly |
— |
update_last_updated_file |
ManualMsg |
| ManualMsg |
— |
echo "[oh-my-zsh] You can update manually..." |
ExitHandle |
RUN UPGRADE SUBPROCESS
| State |
Condition |
Command/Check |
Next State |
| RunUpgrade |
— |
zstyle -s ':omz:update' verbose verbose_mode |
ResolveVerbose |
| ResolveVerbose |
— |
SET to default if missing |
CheckP10kPrompt |
| CheckP10kPrompt |
[[ ${POWERLEVEL9K_INSTANT_PROMPT:-off} != off ]] |
TRUE |
ForceSilent |
| CheckP10kPrompt |
— |
FALSE |
UpgradePath |
| ForceSilent |
— |
verbose_mode=silent |
UpgradePath |
| UpgradePath |
[[ $update_mode != background-alpha ]] |
TRUE |
InteractiveUpgrade |
| UpgradePath |
— |
FALSE |
SilentCaptureUpgrade |
Interactive Mode (TTY + user interaction)
| State |
Condition |
Command/Check |
Next State |
| InteractiveUpgrade |
LANG= ZSH=$ZSH zsh -f $ZSH/tools/upgrade.sh -i -v $verbose_mode |
SUCCESS (exit 0) |
UpdateTimestampOnly |
| InteractiveUpgrade |
— |
FAIL (exit >0) |
SilentCaptureUpgrade |
| UpdateTimestampOnly |
— |
update_last_updated_file |
ExitHandle |
Silent Mode (Background/Capture)
| State |
Condition |
Command/Check |
Next State |
| SilentCaptureUpgrade |
error=$(LANG= ZSH=$ZSH zsh -f $ZSH/tools/upgrade.sh -i -v silent 2>&1) |
SUCCESS |
UpdateSuccessStatus |
| SilentCaptureUpgrade |
— |
FAIL (nonzero exit) |
UpdateErrorStatus |
| UpdateSuccessStatus |
— |
update_last_updated_file 0 "Update successful" |
ExitHandle |
| UpdateErrorStatus |
— |
update_last_updated_file $exit_status "$error" |
ExitHandle |
CHECK UPDATE AVAILABLE (Nested Function)
Configuration Retrieval
| State |
Condition |
Command/Check |
Next State |
| CheckUpdateAvailable |
— |
cd $ZSH; git config --local oh-my-zsh.branch |
ReadRemote |
| ReadRemote |
— |
cd $ZSH; git config --local oh-my-zsh.remote |
ReadRemoteUrl |
| ReadRemoteUrl |
— |
cd $ZSH; git config remote.$remote.url |
ParseRemoteStyle |
Remote Validation
| State |
Condition |
Command/Check |
Next State |
| ParseRemoteStyle |
URL matches https://github.com/* or git@github.com:* |
TRUE |
ValidateOfficialRepo |
| ParseRemoteStyle |
— |
FALSE (non-GitHub) |
AssumeUpdateYes |
| ValidateOfficialRepo |
[[ $repo = ohmyzsh/ohmyzsh ]] |
TRUE |
LocalHeadCheck |
| ValidateOfficialRepo |
— |
FALSE |
AssumeUpdateYes |
Local HEAD Retrieval
| State |
Condition |
Command/Check |
Next State |
| LocalHeadCheck |
cd $ZSH; git rev-parse $branch 2>/dev/null |
SUCCESS |
RemoteHeadFetch |
| LocalHeadCheck |
— |
FAIL |
AssumeUpdateYes |
Remote HEAD Fetch (Prefer curl > wget > fetch)
| State |
Condition |
Command/Check |
Next State |
| RemoteHeadFetch |
(( ${+commands[curl]} )) |
TRUE |
UseCurl |
| RemoteHeadFetch |
(( ${+commands[wget]} )) |
TRUE |
UseWget |
| RemoteHeadFetch |
(( ${+commands[fetch]} )) |
TRUE |
UseFetch |
| RemoteHeadFetch |
— |
NONE (no http tool) |
AssumeUpdateNo |
| UseCurl |
curl --connect-timeout 2 -fsSL -H 'Accept: application/vnd.github.v3.sha' $api_url 2>/dev/null |
SUCCESS |
CompareHeads |
| UseCurl |
— |
FAIL |
AssumeUpdateNo |
| UseWget |
wget -T 2 -O- --header='Accept: application/vnd.github.v3.sha' $api_url 2>/dev/null |
SUCCESS |
CompareHeads |
| UseWget |
— |
FAIL |
AssumeUpdateNo |
| UseFetch |
HTTP_ACCEPT='...' fetch -T 2 -o - $api_url 2>/dev/null |
SUCCESS |
CompareHeads |
| UseFetch |
— |
FAIL |
AssumeUpdateNo |
Head Comparison
| State |
Condition |
Command/Check |
Next State |
| CompareHeads |
[[ $local_head != $remote_head ]] |
TRUE |
MergeBaseCheck |
| CompareHeads |
— |
FALSE (equal) |
AssumeUpdateNo |
Ancestry Check
| State |
Condition |
Command/Check |
Next State |
| MergeBaseCheck |
cd $ZSH; git merge-base $local_head $remote_head 2>/dev/null |
SUCCESS |
EvaluateAncestry |
| MergeBaseCheck |
— |
FAIL |
AssumeUpdateYes |
| EvaluateAncestry |
[[ $base != $remote_head ]] |
TRUE |
AssumeUpdateYes |
| EvaluateAncestry |
— |
FALSE |
AssumeUpdateNo |
Results
| State |
Condition |
Command/Check |
Next State |
| AssumeUpdateYes |
— |
return 0 (update available) |
return from function |
| AssumeUpdateNo |
— |
return 1 (no update) |
return from function |
BACKGROUND UPDATE STATUS HOOK
| State |
Condition |
Command/Check |
Next State |
| BackgroundStatusHook |
— |
register precmd → _omz_bg_update_status |
PollStatus |
| PollStatus |
[[ ! -f $ZSH_CACHE_DIR/.zsh-update ]] |
TRUE |
WaitMore |
| PollStatus |
source $ZSH_CACHE_DIR/.zsh-update |
SUCCESS + [[ -z $EXIT_STATUS || -z $ERROR ]] |
WaitMore |
| PollStatus |
[[ $EXIT_STATUS -eq 0 ]] |
TRUE |
PrintSuccess |
| PollStatus |
[[ $EXIT_STATUS -ne 0 ]] |
TRUE |
PrintFailure |
| WaitMore |
— |
return 1 (continue polling on next precmd) |
PollStatus (next precmd) |
| PrintSuccess |
— |
print -P "\n%F{green}[oh-my-zsh] Update successful.%f" |
CleanupStatusHook |
| PrintFailure |
— |
print -P "\n%F{red}[oh-my-zsh] There was an error updating:%f"; printf "${ERROR}" |
CleanupStatusHook |
| CleanupStatusHook |
(( TRY_BLOCK_ERROR == 0 )) |
TRUE |
DeregisterHook |
| DeregisterHook |
— |
update_last_updated_file (reset status file) |
DeregisterStatusFunc |
| DeregisterStatusFunc |
— |
add-zsh-hook -d precmd _omz_bg_update_status |
[*] |
EXIT & CLEANUP
| State |
Condition |
Command/Check |
Next State |
| ExitHandle |
— |
trap fires (EXIT/INT/QUIT) → rm -rf $ZSH/log/update.lock |
TrapExit |
| TrapExit |
— |
unset update_mode |
TrapExit2 |
| TrapExit |
— |
unset -f current_epoch is_update_available ... |
TrapExit2 |
| TrapExit2 |
— |
return $ret |
[*] |
Key Functions (Always Available)
current_epoch()
Returns the current day count since epoch.
is_update_available()
Returns 0 (update available) or 1 (no update).
See "CHECK UPDATE AVAILABLE" section above.
update_last_updated_file()
- Called with no args → writes
LAST_EPOCH=$(current_epoch)
- Called with
exit_status and error → writes status + error msg
update_ohmyzsh()
Calls upgrade.sh subprocesses (interactive or silent capture).
Returns exit code of upgrade operation.
has_typed_input()
Polls stdin with stty/zselect. Returns 0 if input detected, 1 if not.
Summary Statistics
- Total States (conceptual): 60+
- Total Transitions: 80+
- Early Exit Points (PreFlightGate): 5 conditions
- Update Decision Points: 3 (mode, frequency, availability)
- User Prompt Paths: 2 (prompt mode vs. auto)
- Background Poller States: 4
- Nested Function Depth: 2 (handle_update → is_update_available)