From b006ef523aa3d8306d6a60510f71242af2e84e3f Mon Sep 17 00:00:00 2001 From: cotore-game <102813037+cotore-game@users.noreply.github.com> Date: Thu, 17 Sep 2026 13:15:31 +0900 Subject: [PATCH] fix: keep server operation errors visible in the TUI --- libexec/mcserver-kit/server-manager.sh | 13 +++++- libexec/mcserver-kit/server-properties-tui.sh | 39 ++++++++++++---- tests/run-tests.sh | 46 +++++++++++++++++++ 3 files changed, 87 insertions(+), 11 deletions(-) diff --git a/libexec/mcserver-kit/server-manager.sh b/libexec/mcserver-kit/server-manager.sh index 0e2a1c4..0c24749 100755 --- a/libexec/mcserver-kit/server-manager.sh +++ b/libexec/mcserver-kit/server-manager.sh @@ -125,7 +125,7 @@ import_properties() { } open_folder() { - local directory="$1" part="${2:-data}" target windows_path + local directory="$1" part="${2:-data}" target windows_path output status case "$part" in data) target="${directory}/data" ;; server) target="$directory" ;; @@ -136,7 +136,16 @@ open_folder() { die "$(tr server.explorer_unavailable)" fi windows_path="$(wslpath -w "$target")" || die "$(tr server.explorer_unavailable)" - explorer.exe "$windows_path" + # Explorer can return 1 after handing the folder to an existing window. + # Only accept that status when it supplied no error diagnostics. + if output="$(explorer.exe "$windows_path" 2>&1)"; then + return 0 + else + status=$? + fi + [[ "$status" == 1 && -z "$output" ]] && return 0 + [[ -z "$output" ]] || printf '%s\n' "$output" >&2 + die "$(tr server.explorer_unavailable)" } manage_server() { diff --git a/libexec/mcserver-kit/server-properties-tui.sh b/libexec/mcserver-kit/server-properties-tui.sh index 2622eab..79052b2 100755 --- a/libexec/mcserver-kit/server-properties-tui.sh +++ b/libexec/mcserver-kit/server-properties-tui.sh @@ -37,10 +37,30 @@ actcheckbox=white,blue fi die() { - printf '%s: %s\n' "$(tr common.error)" "$*" >&2 + if command -v whiptail >/dev/null 2>&1; then + whiptail --title "$(tr common.error)" --msgbox "$*" 14 82 || true + else + printf '%s: %s\n' "$(tr common.error)" "$*" >&2 + fi exit 1 } +run_checked() { + local output status + output="$(mktemp)" + if "$@" >"$output" 2>&1; then + [[ ! -s "$output" ]] || whiptail --title "$SERVER_ID" --textbox "$output" 22 84 || true + rm -f -- "$output" + else + status=$? + # Preserve Docker's actual diagnostic until the user dismisses the dialog. + [[ -s "$output" ]] || printf '%s (exit %s)\n' "$1" "$status" >"$output" + whiptail --title "$(tr common.error)" --textbox "$output" 22 84 || true + rm -f -- "$output" + exit "$status" + fi +} + setting_get() { if [[ "$manual_properties" == true ]]; then case "$1" in @@ -210,13 +230,13 @@ menu_item() { } finish() { - [[ "$changed" == true ]] || return - (cd "$SERVER_DIR" && docker compose config --quiet) || die "$(tr properties.compose_invalid)" + [[ "$changed" == true ]] || return 0 + cd -- "$SERVER_DIR" + run_checked docker compose config --quiet if whiptail --yesno "$(tr properties.restart_prompt "$SERVER_ID")" 10 72; then - printf '%s\n' "$(tr properties.applying "$SERVER_ID")" - (cd "$SERVER_DIR" && docker compose up -d --force-recreate) + run_checked docker compose up -d --force-recreate else - printf '%s\n' "$(tr properties.saved_restart_later)" + whiptail --msgbox "$(tr properties.saved_restart_later)" 10 76 || true fi } @@ -246,9 +266,9 @@ main() { [[ -f "${SERVER_DIR}/compose.yaml" ]] || die "$(tr server.compose_missing "$SERVER_ID")" if [[ ! -f "$SERVER_ENV" ]]; then - whiptail --yesno "$(tr properties.migration_prompt)" 11 76 || return + whiptail --yesno "$(tr properties.migration_prompt)" 11 76 || return 0 fi - python3 "$CONFIG_TOOL" migrate "$SERVER_DIR" + run_checked python3 "$CONFIG_TOOL" migrate "$SERVER_DIR" if [[ "$(python3 "$CONFIG_TOOL" get "$SERVER_ENV" OVERRIDE_SERVER_PROPERTIES true)" == false ]]; then [[ -f "$SERVER_PROPERTIES" ]] || die "$(tr properties.file_missing)" local running @@ -258,7 +278,8 @@ main() { fi manual_properties=true fi - (cd "$SERVER_DIR" && docker compose config --quiet) || die "$(tr properties.compose_invalid)" + cd -- "$SERVER_DIR" + run_checked docker compose config --quiet while true; do items=( diff --git a/tests/run-tests.sh b/tests/run-tests.sh index 49a6d7b..18cf47d 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -762,6 +762,10 @@ PROPERTIES if [[ "$*" == 'compose ps --status running --services' && "${MCSERVER_KIT_TEST_RUNNING:-false}" == true ]]; then printf 'minecraft\n' fi +if [[ "${MCSERVER_KIT_TEST_DOCKER_MISSING:-false}" == true ]]; then + printf "The command 'docker' could not be found in this WSL 2 distro.\n" >&2 + exit 127 +fi DOCKER cat >"${fake_bin}/wslpath" <<'WSLPATH' #!/usr/bin/env bash @@ -770,6 +774,8 @@ WSLPATH cat >"${fake_bin}/explorer.exe" <<'EXPLORER' #!/usr/bin/env bash printf '%s\n' "$1" >"$MCSERVER_KIT_TEST_EXPLORER_LOG" +[[ -z "${MCSERVER_KIT_TEST_EXPLORER_ERROR:-}" ]] || printf '%s\n' "$MCSERVER_KIT_TEST_EXPLORER_ERROR" >&2 +exit "${MCSERVER_KIT_TEST_EXPLORER_STATUS:-0}" EXPLORER chmod +x "${fake_bin}/docker" "${fake_bin}/wslpath" "${fake_bin}/explorer.exe" @@ -835,6 +841,46 @@ WHIPTAIL MCSERVER_KIT_TEST_EXPLORER_LOG="${temp_dir}/property-import/explorer.log" \ bash "${REPO_ROOT}/mcserver-kit" server alpha open data assert_equal "WIN:${server_dir}/data" "$(<"${temp_dir}/property-import/explorer.log")" 'open data passes the persistent folder to Explorer' + + for part in data server; do + output="$(PATH="${fake_bin}:$PATH" MCSERVER_KIT_LANG=en MCSERVER_KIT_CONFIG="$config_file" \ + MCSERVER_KIT_TEST_EXPLORER_LOG="${temp_dir}/property-import/explorer.log" \ + MCSERVER_KIT_TEST_EXPLORER_STATUS=1 \ + bash "${REPO_ROOT}/mcserver-kit" server alpha open "$part")" + assert_equal '' "$output" "Explorer silent status 1 succeeds for $part without an error dialog" + done + assert_fails 'Explorer failure with diagnostics remains an error' \ + env PATH="${fake_bin}:$PATH" MCSERVER_KIT_CONFIG="$config_file" \ + MCSERVER_KIT_TEST_EXPLORER_LOG="${temp_dir}/property-import/explorer.log" \ + MCSERVER_KIT_TEST_EXPLORER_STATUS=1 MCSERVER_KIT_TEST_EXPLORER_ERROR='Access denied' \ + bash "${REPO_ROOT}/mcserver-kit" server alpha open data + assert_fails 'Explorer execution failure remains an error' \ + env PATH="${fake_bin}:$PATH" MCSERVER_KIT_CONFIG="$config_file" \ + MCSERVER_KIT_TEST_EXPLORER_LOG="${temp_dir}/property-import/explorer.log" \ + MCSERVER_KIT_TEST_EXPLORER_STATUS=126 \ + bash "${REPO_ROOT}/mcserver-kit" server alpha open server + + cat >"${fake_bin}/whiptail" <<'WHIPTAIL' +#!/usr/bin/env bash +while (($#)); do + case "$1" in + --textbox) cat "$2" >"$MCSERVER_KIT_TEST_DIALOG_LOG"; exit 0 ;; + --menu) printf '__exit' >&2; exit 0 ;; + esac + shift +done +WHIPTAIL + local result=0 + output="$(PATH="${fake_bin}:$PATH" MCSERVER_KIT_LANG=en \ + MCSERVER_KIT_TEST_DOCKER_MISSING=true \ + MCSERVER_KIT_TEST_DIALOG_LOG="${temp_dir}/property-import/dialog.log" \ + bash "${REPO_ROOT}/libexec/mcserver-kit/server-properties-tui.sh" alpha "$server_dir" 2>&1)" || result=$? + assert_equal 127 "$result" 'properties preserves the Docker failure exit code' + assert_equal '' "$output" 'Docker errors do not flash on the underlying terminal' + assert_equal "The command 'docker' could not be found in this WSL 2 distro." "$(<"${temp_dir}/property-import/dialog.log")" 'Docker diagnostic is presented in a persistent TUI textbox' + PATH="${fake_bin}:$PATH" MCSERVER_KIT_LANG=en \ + bash "${REPO_ROOT}/libexec/mcserver-kit/server-properties-tui.sh" alpha "$server_dir" + assert_equal 0 "$?" 'closing properties without changes succeeds' } main() {