feat(easy-docker): add production stack wizard flow and modularize tui screens

This commit is contained in:
RocketQuack 2026-02-26 01:03:20 +01:00
parent c2c90e62aa
commit 37122d20c1
5 changed files with 374 additions and 64 deletions

View file

@ -1,14 +1,95 @@
#!/usr/bin/env bash
get_easy_docker_repo_root() {
local app_lib_dir=""
app_lib_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
(cd "${app_lib_dir}/../../../.." && pwd)
}
get_easy_docker_stacks_dir() {
printf '%s/.easy-docker/stacks\n' "$(get_easy_docker_repo_root)"
}
is_valid_stack_name() {
local stack_name="${1}"
if [ -z "${stack_name}" ]; then
return 1
fi
case "${stack_name}" in
*[!A-Za-z0-9._-]*)
return 1
;;
*)
return 0
;;
esac
}
create_stack_env_file() {
local result_var="${1}"
local stack_name="${2}"
local stacks_dir=""
local env_path=""
stacks_dir="$(get_easy_docker_stacks_dir)"
env_path="${stacks_dir}/${stack_name}.env"
if ! mkdir -p "${stacks_dir}"; then
return 1
fi
if [ -e "${env_path}" ]; then
return 2
fi
: >"${env_path}"
printf -v "${result_var}" "%s" "${env_path}"
return 0
}
prompt_stack_name_with_cancel() {
local result_var="${1}"
local input_value=""
local input_status=0
input_value="$(prompt_new_stack_name)"
input_status=$?
if [ "${input_status}" -ne 0 ]; then
return 3
fi
input_value="$(printf '%s' "${input_value}" | tr -d '\r\n')"
case "${input_value}" in
/cancel | /CANCEL | /Cancel)
return 3
;;
esac
printf -v "${result_var}" "%s" "${input_value}"
return 0
}
run_easy_docker_app() {
local action=""
local local_env_action=""
local local_production_action=""
local local_production_sub_action=""
local stack_name=""
local stack_env_path=""
local create_stack_status=0
local stack_input_status=0
enter_alt_screen
render_main_screen 1
while true; do
local_env_action=""
local_production_action=""
local_production_sub_action=""
action="$(show_main_menu || true)"
if [ -z "${action}" ]; then
@ -16,6 +97,96 @@ run_easy_docker_app() {
fi
case "${action}" in
"Production setup")
while true; do
local_production_action="$(show_production_setup_menu || true)"
case "${local_production_action}" in
"Create new stack")
while true; do
stack_name=""
if ! prompt_stack_name_with_cancel stack_name; then
stack_input_status=$?
if [ "${stack_input_status}" -eq 3 ]; then
break
fi
show_warning_message "Input canceled."
sleep 1
break
fi
if [ -z "${stack_name}" ]; then
break
fi
if ! is_valid_stack_name "${stack_name}"; then
show_warning_message "Invalid stack name. Use letters, numbers, dot, underscore, or hyphen."
sleep 2
continue
fi
stack_env_path=""
if create_stack_env_file stack_env_path "${stack_name}"; then
local_production_sub_action="$(show_create_stack_created "${stack_name}" "${stack_env_path}" || true)"
else
create_stack_status=$?
if [ "${create_stack_status}" -eq 2 ]; then
show_warning_message "Stack already exists: ${stack_name}"
sleep 2
continue
else
show_warning_message "Could not create stack env file for: ${stack_name}"
sleep 2
break
fi
fi
case "${local_production_sub_action}" in
"Continue stack wizard")
show_warning_message "Next wizard step is coming soon."
sleep 2
;;
"Back to production setup" | "") ;;
*)
show_warning_message "Unknown create-stack action: ${local_production_sub_action}"
sleep 1
;;
esac
break
done
;;
"Manage existing stacks")
local_production_sub_action="$(show_manage_stacks_placeholder || true)"
case "${local_production_sub_action}" in
"Back to production setup") ;;
"Back to main menu" | "")
render_main_screen 1
break
;;
"Exit and close easy-docker")
return 0
;;
*)
show_warning_message "Unknown manage-stacks action: ${local_production_sub_action}"
sleep 1
;;
esac
;;
"Back to main menu" | "")
render_main_screen 1
break
;;
"Exit and close easy-docker")
return 0
;;
*)
show_warning_message "Unknown production action: ${local_production_action}"
sleep 1
;;
esac
done
;;
"Environment check")
local_env_action="$(show_environment_status || true)"
case "${local_env_action}" in

View file

@ -1,69 +1,15 @@
#!/usr/bin/env bash
render_main_screen() {
local clear_screen="${1:-0}"
local header_text=""
load_ui_screen_modules() {
local screens_dir=""
screens_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/screens"
if [ "${clear_screen}" = "1" ]; then
clear
fi
header_text="$(printf "Easy Frappe Docker\nManage Docker setups quickly and easily")"
gum style \
--border rounded \
--border-foreground 63 \
--padding "1 2" \
--margin "1 2" \
--foreground 252 \
"${header_text}"
# shellcheck source=scripts/easy-docker/lib/ui/screens/base.sh
source "${screens_dir}/base.sh"
# shellcheck source=scripts/easy-docker/lib/ui/screens/production.sh
source "${screens_dir}/production.sh"
# shellcheck source=scripts/easy-docker/lib/ui/screens/environment.sh
source "${screens_dir}/environment.sh"
}
show_main_menu() {
gum choose \
--height 7 \
--header "Choose an action" \
--cursor.foreground 63 \
--selected.foreground 45 \
"Environment check" \
"Exit"
}
show_environment_status() {
local docker_status="not installed"
local docker_daemon_status="not running"
local status_text=""
if command_exists docker; then
docker_status="installed"
if docker_daemon_running; then
docker_daemon_status="running"
fi
fi
render_main_screen 1 >&2
status_text="$(printf "Environment status\n\n- docker: %s\n- docker daemon: %s" "${docker_status}" "${docker_daemon_status}")"
gum style \
--border rounded \
--border-foreground 63 \
--padding "1 2" \
--margin "0 2" \
--foreground 252 \
"${status_text}" >&2
gum choose \
--height 6 \
--header "Environment actions" \
--cursor.foreground 63 \
--selected.foreground 45 \
"Back to main menu" \
"Exit and close easy-docker"
}
show_warning_message() {
local message="${1}"
gum style --foreground 214 "${message}"
}
load_ui_screen_modules

View file

@ -0,0 +1,89 @@
#!/usr/bin/env bash
get_terminal_cols() {
local cols="80"
if command_exists tput; then
cols="$(tput cols 2>/dev/null || printf "80")"
fi
if ! [[ "${cols}" =~ ^[0-9]+$ ]] || [ "${cols}" -le 0 ]; then
cols="80"
fi
printf '%s\n' "${cols}"
}
get_box_wrap_width() {
local cols=""
local width=""
cols="$(get_terminal_cols)"
width="$((cols - 16))"
if [ "${width}" -lt 12 ]; then
width="12"
fi
printf '%s\n' "${width}"
}
wrap_box_text() {
local raw_text="${1}"
local wrap_width=""
wrap_width="$(get_box_wrap_width)"
if command_exists fold; then
printf '%s' "${raw_text}" | fold -s -w "${wrap_width}"
return
fi
printf '%s' "${raw_text}"
}
render_box_message() {
local raw_text="${1}"
local margin="${2:-0 2}"
local padding="${3:-0 1}"
local wrapped_text=""
wrapped_text="$(wrap_box_text "${raw_text}")"
gum style \
--border rounded \
--border-foreground 63 \
--padding "${padding}" \
--margin "${margin}" \
--foreground 252 \
"${wrapped_text}"
}
render_main_screen() {
local clear_screen="${1:-0}"
local header_text=""
if [ "${clear_screen}" = "1" ]; then
clear
fi
header_text="$(printf "Easy Frappe Docker\nManage Docker setups quickly and easily")"
render_box_message "${header_text}" "1 2" "0 1"
}
show_main_menu() {
gum choose \
--height 7 \
--header "Choose an action" \
--cursor.foreground 63 \
--selected.foreground 45 \
"Production setup" \
"Environment check" \
"Exit"
}
show_warning_message() {
local message="${1}"
gum style --foreground 214 "${message}"
}

View file

@ -0,0 +1,29 @@
#!/usr/bin/env bash
show_environment_status() {
local docker_status="not installed"
local docker_daemon_status="not running"
local status_text=""
if command_exists docker; then
docker_status="installed"
if docker_daemon_running; then
docker_daemon_status="running"
fi
fi
render_main_screen 1 >&2
status_text="$(printf "Environment status\n\n- docker: %s\n- docker daemon: %s" "${docker_status}" "${docker_daemon_status}")"
render_box_message "${status_text}" "0 2" >&2
gum choose \
--height 6 \
--header "Environment actions" \
--cursor.foreground 63 \
--selected.foreground 45 \
"Back to main menu" \
"Exit and close easy-docker"
}

View file

@ -0,0 +1,75 @@
#!/usr/bin/env bash
show_production_setup_menu() {
local status_text=""
render_main_screen 1 >&2
status_text="$(printf "Production setup\n\nChoose whether to create a new stack or manage an existing one.")"
render_box_message "${status_text}" "0 2" >&2
gum choose \
--height 8 \
--header "Production setup actions" \
--cursor.foreground 63 \
--selected.foreground 45 \
"Create new stack" \
"Manage existing stacks" \
"Back to main menu" \
"Exit and close easy-docker"
}
prompt_new_stack_name() {
local status_text=""
render_main_screen 1 >&2
status_text="$(printf "Create new stack\n\nEnter a stack name.\nType /cancel or press Ctrl+C to abort.")"
render_box_message "${status_text}" "0 2" >&2
gum input \
--header "Stack name (/cancel to abort)" \
--prompt "name> " \
--placeholder "my-production-stack"
}
show_create_stack_created() {
local stack_name="${1}"
local env_path="${2}"
local status_text=""
render_main_screen 1 >&2
status_text="$(printf "Create new stack\n\nStack created: %s\nEnv file: %s" "${stack_name}" "${env_path}")"
render_box_message "${status_text}" "0 2" >&2
gum choose \
--height 6 \
--header "Create stack actions" \
--cursor.foreground 63 \
--selected.foreground 45 \
"Continue stack wizard" \
"Back to production setup"
}
show_manage_stacks_placeholder() {
local status_text=""
render_main_screen 1 >&2
status_text="$(printf "Manage existing stacks")"
render_box_message "${status_text}" "0 2" >&2
gum choose \
--height 7 \
--header "Manage stacks actions" \
--cursor.foreground 63 \
--selected.foreground 45 \
"Back to production setup" \
"Back to main menu" \
"Exit and close easy-docker"
}