Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion public/checksum.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
6de66efba021ebcf462e7672577d9f6876f13db55691f04e7009ff7e78d658b8 public/sh/install_zpmod.sh
6e753a0314ba9f2e4f0a2c429798ca3767af4fb94c669e6923d161d6af7aa745 public/sh/install.sh
08cc893ceb982fc99d17db1966c6c30790cc571e16e4f5392352d995f5252952 public/sh/sync-init.sh
e5db3eb099daf0098bed263f4f1960adc9d3d0074a19260fae7ab3df8bf08573 public/zsh/init.zsh
c979e39748d1d86ace17a61ff2b1bf6e1224a43291c25bf7fad985d1e9e11af1 public/zsh/init.zsh
9 changes: 6 additions & 3 deletions public/zsh/init.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ _zi_pmod() {
module_path+=( "$module_dir" )
if ! zmodload zi/zpmod 2>/dev/null; then
[[ ${ZI[MUTE_WARNINGS]} == 1 ]] ||
_zi_err "zpmod.so is present but zmodload zi/zpmod failed; rebuild it with \`zi module build\`."
_zi_err "zpmod.so is present but zmodload zi/zpmod failed; rebuild it with \`zi module build\`, then run \`zzinit\` again."
return 1
fi
return 0
Expand Down Expand Up @@ -329,8 +329,11 @@ zzinit() {
_zi_pmod
status_pmod=$?

# Zi is usable at this point, but an optional stage reported a problem. Keep
# the helpers so the diagnostics above can be acted on and zzinit re-run.
(( status_comps == 0 && status_pmod == 0 )) || return 1

# Helpers are only removed on success so a failed run stays retryable.
unset -f _zi_err _zi_fetch _zi_check_stream _zi_setup _zi_source _zi_comps _zi_pmod zzinit 2>/dev/null

(( status_comps == 0 && status_pmod == 0 ))
return 0
}
40 changes: 40 additions & 0 deletions tests/installers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,45 @@ test_init_rejects_invalid_stream() {
pass "invalid ZI[STREAM] is rejected before reaching git"
}

test_init_keeps_helpers_when_zpmod_fails() {
values_log="${TMP_ROOT}/init-zpmod-values"
err_log="${TMP_ROOT}/init-zpmod-err"

# zi.zsh loads, but an optional stage fails: zpmod.so exists and cannot be
# loaded. The helpers and zzinit itself must survive so that, once the module
# is rebuilt, the advertised `zzinit` retry has something to call.
zsh -f -c '
typeset -ghA ZI
ZI[LOADER_HISTORY]=0
ZI[HOME_DIR]="$4/zpmod-home"
ZI[BIN_DIR]="$4/zpmod-home/bin"
module_dir="${ZI[HOME_DIR]}/zmodules/zpmod/Src/zi"
command mkdir -p "${ZI[BIN_DIR]}" "$module_dir"
print -r -- "# fake zi.zsh" > "${ZI[BIN_DIR]}/zi.zsh"
# A file that is not a shared object: zmodload refuses it.
print -r -- "not a shared object" > "$module_dir/zpmod.so"

source "$1"
zzinit 2>"$3"
print -r -- "first_status:$?" >"$2"
print -r -- "first_retryable:${+functions[zzinit]}" >>"$2"

command rm -f -- "$module_dir/zpmod.so"
zzinit 2>>"$3"
print -r -- "second_status:$?" >>"$2"
print -r -- "second_retryable:${+functions[zzinit]}" >>"$2"
' zsh "${ROOT}/public/zsh/init.zsh" "${values_log}" "${err_log}" "${TMP_ROOT}"

contains "${values_log}" 'first_status:1'
contains "${err_log}" 'rebuild it with'
# The failed run keeps zzinit defined so the user can rebuild and retry.
contains "${values_log}" 'first_retryable:1'
# The retry succeeds and only then removes the helpers.
contains "${values_log}" 'second_status:0'
contains "${values_log}" 'second_retryable:0'
pass "optional zpmod failure keeps zzinit retryable"
}

test_init_progress_filter_url() {
# The loader downloads this file and executes it. A 404 previously aborted
# every clean install with no diagnostic, so the path is asserted here.
Expand Down Expand Up @@ -639,6 +678,7 @@ test_init_defaults_are_single_arguments
test_init_preserves_caller_options
test_init_history_opt_out
test_init_rejects_invalid_stream
test_init_keeps_helpers_when_zpmod_fails
test_init_progress_filter_url
test_init_uses_private_tempdir
test_init_path_resolution
Expand Down
Loading