This commit is contained in:
Marc Cornellà 2026-03-26 19:38:22 +01:00 committed by GitHub
commit d6c20c01c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 555 additions and 0 deletions

517
tools/UPDATE_PROCESS.md Normal file
View file

@ -0,0 +1,517 @@
# check_for_upgrade.sh: Update Process
## Visual State Diagram
```mermaid
stateDiagram-v2
[*] --> Bootstrap
state Bootstrap {
[*] --> CheckLegacyUpdateFile
CheckLegacyUpdateFile: check [[ -f ~/.zsh-update && ! -f $ZSH_CACHE_DIR/.zsh-update ]]
CheckLegacyUpdateFile --> MigrateLegacyFile: TRUE
CheckLegacyUpdateFile --> LoadMode: FALSE
MigrateLegacyFile: mv ~/.zsh-update $ZSH_CACHE_DIR/.zsh-update
MigrateLegacyFile --> LoadMode
LoadMode: read update mode from zstyle
LoadMode --> LegacyModeFallback: fail
LegacyModeFallback: set update_mode=prompt
LegacyModeFallback --> LegacyPromptFlag
LegacyPromptFlag: evaluate DISABLE_UPDATE_PROMPT
LegacyPromptFlag --> LegacyAutoFlag
LegacyAutoFlag: evaluate DISABLE_AUTO_UPDATE
LegacyAutoFlag --> PreFlightGate
LoadMode --> PreFlightGate: success
}
PreFlightGate: aggregated early-return gate
PreFlightGate --> ExitNoUpdate: mode=disabled
PreFlightGate --> ExitNoUpdate: write permission or owner check failed
PreFlightGate --> ExitNoUpdate: tty gate failed
PreFlightGate --> ExitNoUpdate: git command unavailable
PreFlightGate --> ExitNoUpdate: repository check failed
PreFlightGate --> ModeDispatch: all checks pass
ExitNoUpdate: unset update_mode; return
state ModeDispatch {
[*] --> SetupBackgroundHooks: mode=background-alpha
[*] --> RunHandleUpdate: otherwise
}
SetupBackgroundHooks: autoload -Uz add-zsh-hook
SetupBackgroundHooks --> RegisterBgPrecmd
RegisterBgPrecmd: add-zsh-hook precmd _omz_bg_update
RegisterBgPrecmd --> BackgroundStatusHook
RunHandleUpdate --> HandleUpdateCore
state HandleUpdateCore {
[*] --> LockCleanupCheck
LockCleanupCheck: load datetime and read lock mtime
LockCleanupCheck --> CheckStaleAge: lock exists
LockCleanupCheck --> AcquireLock: no lock
CheckStaleAge: stale if lock older than 24h
CheckStaleAge --> RemoveStaleLock: TRUE
CheckStaleAge --> AcquireLock: FALSE
RemoveStaleLock: rm -rf $ZSH/log/update.lock
RemoveStaleLock --> AcquireLock
AcquireLock: mkdir $ZSH/log/update.lock
AcquireLock --> ExitHandle: fail (lock exists)
AcquireLock --> InstallTrap: success
InstallTrap: install trap to cleanup lock and functions
InstallTrap --> LoadStatusFile
LoadStatusFile: source status file and validate last epoch
LoadStatusFile --> InitStatusFile: fail or empty LAST_EPOCH
LoadStatusFile --> FrequencyCheck: success
InitStatusFile: update_last_updated_file
InitStatusFile --> ExitHandle
FrequencyCheck: resolve frequency from zstyle or default
FrequencyCheck --> PeriodElapsed
PeriodElapsed: enough days elapsed since last check
PeriodElapsed --> RepoCheck: TRUE
PeriodElapsed --> ExitHandle: FALSE
RepoCheck: verify ZSH path is a git repository
RepoCheck --> CheckUpdateAvailable: success
RepoCheck --> ExitHandle: fail
CheckUpdateAvailable --> ReminderOrTypedInput: update available (return 0)
CheckUpdateAvailable --> ExitHandleWithTimestamp: no update (return 1)
ExitHandleWithTimestamp: update_last_updated_file
ExitHandleWithTimestamp --> ExitHandle
ReminderOrTypedInput --> ReminderExit: mode=reminder
ReminderOrTypedInput --> TypedInputCheck: mode!=background-alpha
ReminderOrTypedInput --> ModeAutoGate: mode=background-alpha
TypedInputCheck: detect pending typed input from terminal
TypedInputCheck --> ReminderExit: input detected
TypedInputCheck --> ModeAutoGate: no input
ReminderExit: echo reminder message
ReminderExit --> ExitHandle
ModeAutoGate --> RunUpgrade: mode is auto or background alpha
ModeAutoGate --> PromptUser: mode=prompt
PromptUser: read -r -k 1 option
PromptUser --> RunUpgrade: yes or enter
PromptUser --> WriteTimestampOnly: [nN]
PromptUser --> ManualMsg: other
WriteTimestampOnly: update_last_updated_file
WriteTimestampOnly --> ManualMsg
ManualMsg: echo manual update hint
ManualMsg --> ExitHandle
RunUpgrade --> ExitHandle
ExitHandle --> [*]
}
state CheckUpdateAvailable {
[*] --> ReadBranch
ReadBranch: read configured branch default master
ReadBranch --> ReadRemote
ReadRemote: read configured remote default origin
ReadRemote --> ReadRemoteUrl
ReadRemoteUrl: read remote URL from git config
ReadRemoteUrl --> ParseRemoteStyle
ParseRemoteStyle --> AssumeUpdateYes: non-GitHub remote
ParseRemoteStyle --> ValidateOfficialRepo: GitHub URL
ValidateOfficialRepo --> AssumeUpdateYes: repo != ohmyzsh/ohmyzsh
ValidateOfficialRepo --> LocalHeadCheck: repo == ohmyzsh/ohmyzsh
LocalHeadCheck: resolve local branch HEAD hash
LocalHeadCheck --> AssumeUpdateYes: fail
LocalHeadCheck --> RemoteHeadFetch: success
RemoteHeadFetch --> UseCurl: curl available
RemoteHeadFetch --> UseWget: wget available
RemoteHeadFetch --> UseFetch: fetch available
RemoteHeadFetch --> AssumeUpdateNo: none available
UseCurl: fetch remote head using curl
UseWget: fetch remote head using wget
UseFetch: fetch remote head using fetch utility
UseCurl --> CompareHeads: success
UseCurl --> AssumeUpdateNo: fail
UseWget --> CompareHeads: success
UseWget --> AssumeUpdateNo: fail
UseFetch --> CompareHeads: success
UseFetch --> AssumeUpdateNo: fail
CompareHeads: compare local and remote head hashes
CompareHeads --> MergeBaseCheck: TRUE
CompareHeads --> AssumeUpdateNo: FALSE
MergeBaseCheck: compute merge base from both hashes
MergeBaseCheck --> AssumeUpdateYes: fail
MergeBaseCheck --> EvaluateAncestry: success
EvaluateAncestry: decide if local is behind remote
EvaluateAncestry --> AssumeUpdateYes: TRUE
EvaluateAncestry --> AssumeUpdateNo: FALSE
AssumeUpdateYes --> [*]
AssumeUpdateNo --> [*]
}
state RunUpgrade {
[*] --> ResolveVerbose
ResolveVerbose: resolve verbose mode from zstyle
ResolveVerbose --> CheckP10kPrompt
CheckP10kPrompt: check instant prompt setting
CheckP10kPrompt --> ForceSilent: TRUE
CheckP10kPrompt --> UpgradePath: FALSE
ForceSilent: verbose_mode=silent
ForceSilent --> UpgradePath
UpgradePath --> InteractiveUpgrade: mode != background-alpha
UpgradePath --> SilentCaptureUpgrade: mode=background-alpha
InteractiveUpgrade: run upgrade script with interactive verbosity
InteractiveUpgrade --> UpdateTimestampOnly: success
InteractiveUpgrade --> SilentCaptureUpgrade: fail
SilentCaptureUpgrade: run upgrade script and capture output
SilentCaptureUpgrade --> UpdateSuccessStatus: success
SilentCaptureUpgrade --> UpdateErrorStatus: fail
UpdateTimestampOnly: update_last_updated_file
UpdateSuccessStatus: write status file with success
UpdateErrorStatus: write status file with captured error
UpdateTimestampOnly --> [*]
UpdateSuccessStatus --> [*]
UpdateErrorStatus --> [*]
}
state BackgroundStatusHook {
[*] --> RegisterStatusHook
RegisterStatusHook: register precmd status hook
RegisterStatusHook --> PollStatus
PollStatus: poll status file on each precmd
PollStatus --> WaitMore: status file not ready
PollStatus --> PrintSuccess: EXIT_STATUS==0
PollStatus --> PrintFailure: EXIT_STATUS!=0
WaitMore: return 1 (continue polling)
WaitMore --> [*]
PrintSuccess: print success message
PrintSuccess --> CleanupStatusHook
PrintFailure: print error message with details
PrintFailure --> CleanupStatusHook
CleanupStatusHook: reset status file and deregister status hook
CleanupStatusHook --> [*]
}
SetupBackgroundHooks --> BackgroundStatusHook
ExitNoUpdate --> [*]
```
---
## 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()
```zsh
zmodload zsh/datetime
echo $(( EPOCHSECONDS / 60 / 60 / 24 ))
```
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)

View file

@ -39,6 +39,7 @@ function current_epoch() {
echo $(( EPOCHSECONDS / 60 / 60 / 24 ))
}
# TODO: change this to support stable releases
function is_update_available() {
local branch
branch=${"$(builtin cd -q "$ZSH"; git config --local oh-my-zsh.branch)":-master}
@ -211,6 +212,8 @@ function handle_update() {
return
fi
# TODO: somewhere like here we should support fast-tracking of security patches
# Check if there are updates available before proceeding
if ! is_update_available; then
update_last_updated_file

View file

@ -191,6 +191,8 @@ if is_tty; then
RESET=$(printf '\033[0m')
fi
## START Migrations
# Update upstream remote to ohmyzsh org
git remote -v | while read remote url extra; do
case "$url" in
@ -213,6 +215,14 @@ git remote -v | while read remote url extra; do
break
done
# Set default release mode
# We should really move away from `.branch`, and keep it only for forks
# then we can set `.release` and `.remote` as the right ones for us
# Release options: master, release/YY0M, vYY0M.x.y
if [[ -z "$(git config --local oh-my-zsh.release)" ]]; then
git config --local oh-my-zsh.release "master"
fi
# Set git-config values known to fix git errors
# Line endings (#4069)
git config core.eol lf
@ -225,11 +235,14 @@ git config receive.fsck.zeroPaddedFilemode ignore
resetAutoStash=$(git config --bool rebase.autoStash 2>/dev/null)
git config rebase.autoStash true
## END migrations
local ret=0
# repository settings
remote=${"$(git config --local oh-my-zsh.remote)":-origin}
branch=${"$(git config --local oh-my-zsh.branch)":-master}
release=${"$(git config --local oh-my-zsh.release)":-master}
# repository state
last_head=$(git symbolic-ref --quiet --short HEAD || git rev-parse HEAD)
@ -242,6 +255,28 @@ last_commit=$(git rev-parse "$branch")
if [[ $verbose_mode != silent ]]; then
printf "${BLUE}%s${RESET}\n" "Updating Oh My Zsh"
fi
# master -> git pull
# release/YY0M -> git pull origin release/YY0M
# vYY0M.x.y -> only update if release/ branch of the tag has a security update
case "$release" in
master|release/*) git pull --quiet --rebase "$remote" "$release" ;;
v*) ;; # do nothing, but we should really make sure that security patches are fast-tracked: if the "$release" tag is in branch release/YY0M, we should pull that branch and then check out the latest tag, and inform the user that we upgraded their release
*) echo "error: unknown release '$release'" && exit 1 ;; # here we should show how to fix the issue
esac
# test: are we updating on the main remote or from a fork?
# if main remote then
# case "$release" in
# master|release/*) git pull --quiet --rebase "$remote" "$release" ;;
# release/*) git pull --quiet --rebase "$remote" "$release" ;;
# v*) ;; # nothing
# *) echo "error: unknown release '$release'" && exit 1 ;;
# esac
# fi
# if master or release/* do just git pull $remote $branch
if LANG= git pull --quiet --rebase $remote $branch; then
# Check if it was really updated or not
if [[ "$(git rev-parse HEAD)" = "$last_commit" ]]; then