mirror of
https://github.com/frappe/frappe_docker.git
synced 2026-06-21 15:25:09 +00:00
refactor(easy-docker): split compose and production modules
This commit is contained in:
parent
d2ee473c68
commit
2a9aabbdb3
8 changed files with 906 additions and 834 deletions
|
|
@ -4,420 +4,16 @@ EASY_DOCKER_BUILD_ERROR_DETAIL=""
|
||||||
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata fails.
|
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata fails.
|
||||||
EASY_DOCKER_COMPOSE_ERROR_DETAIL=""
|
EASY_DOCKER_COMPOSE_ERROR_DETAIL=""
|
||||||
|
|
||||||
render_stack_compose_from_metadata() {
|
load_easy_docker_compose_modules() {
|
||||||
local stack_dir="${1}"
|
local compose_dir=""
|
||||||
local metadata_path=""
|
compose_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/compose"
|
||||||
local env_path=""
|
|
||||||
local generated_compose_path=""
|
|
||||||
local generated_compose_tmp_path=""
|
|
||||||
local compose_files_lines=""
|
|
||||||
local compose_file=""
|
|
||||||
local source_compose_path=""
|
|
||||||
local env_erpnext_version=""
|
|
||||||
local fallback_erpnext_version=""
|
|
||||||
local repo_root=""
|
|
||||||
local -a compose_args=()
|
|
||||||
|
|
||||||
metadata_path="${stack_dir}/metadata.json"
|
# shellcheck source=scripts/easy-docker/lib/app/wizard/common/compose/render.sh
|
||||||
env_path="$(get_stack_env_path "${stack_dir}")"
|
source "${compose_dir}/render.sh"
|
||||||
generated_compose_path="$(get_stack_generated_compose_path "${stack_dir}")"
|
# shellcheck source=scripts/easy-docker/lib/app/wizard/common/compose/start.sh
|
||||||
generated_compose_tmp_path="${generated_compose_path}.tmp"
|
source "${compose_dir}/start.sh"
|
||||||
|
# shellcheck source=scripts/easy-docker/lib/app/wizard/common/compose/build.sh
|
||||||
if [ ! -f "${metadata_path}" ]; then
|
source "${compose_dir}/build.sh"
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "${env_path}" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
env_erpnext_version="$(get_env_file_key_value "${env_path}" "ERPNEXT_VERSION" || true)"
|
|
||||||
if [ -z "${env_erpnext_version}" ]; then
|
|
||||||
fallback_erpnext_version="$(get_default_erpnext_version || true)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
compose_files_lines="$(get_metadata_compose_files_lines "${metadata_path}" || true)"
|
|
||||||
if [ -z "${compose_files_lines}" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
repo_root="$(get_easy_docker_repo_root)"
|
|
||||||
while IFS= read -r compose_file; do
|
|
||||||
if [ -z "${compose_file}" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
source_compose_path="${repo_root}/${compose_file}"
|
|
||||||
if [ ! -f "${source_compose_path}" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
compose_args+=(-f "${source_compose_path}")
|
|
||||||
done <<EOF
|
|
||||||
${compose_files_lines}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ "${#compose_args[@]}" -eq 0 ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "${fallback_erpnext_version}" ]; then
|
|
||||||
if ! ERPNEXT_VERSION="${fallback_erpnext_version}" docker compose --env-file "${env_path}" "${compose_args[@]}" config >"${generated_compose_tmp_path}"; then
|
|
||||||
rm -f -- "${generated_compose_tmp_path}" >/dev/null 2>&1 || true
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
elif ! docker compose --env-file "${env_path}" "${compose_args[@]}" config >"${generated_compose_tmp_path}"; then
|
|
||||||
rm -f -- "${generated_compose_tmp_path}" >/dev/null 2>&1 || true
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! mv -- "${generated_compose_tmp_path}" "${generated_compose_path}"; then
|
|
||||||
rm -f -- "${generated_compose_tmp_path}" >/dev/null 2>&1 || true
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start_stack_with_compose_from_metadata() {
|
load_easy_docker_compose_modules
|
||||||
local stack_dir="${1}"
|
|
||||||
local metadata_path=""
|
|
||||||
local env_path=""
|
|
||||||
local compose_files_lines=""
|
|
||||||
local compose_file=""
|
|
||||||
local source_compose_path=""
|
|
||||||
local env_erpnext_version=""
|
|
||||||
local fallback_erpnext_version=""
|
|
||||||
local configured_pull_policy=""
|
|
||||||
local runtime_pull_policy=""
|
|
||||||
local custom_image=""
|
|
||||||
local custom_tag=""
|
|
||||||
local image_ref=""
|
|
||||||
local stack_topology=""
|
|
||||||
local repo_root=""
|
|
||||||
local -a compose_args=()
|
|
||||||
|
|
||||||
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata fails.
|
|
||||||
EASY_DOCKER_COMPOSE_ERROR_DETAIL=""
|
|
||||||
|
|
||||||
metadata_path="${stack_dir}/metadata.json"
|
|
||||||
env_path="$(get_stack_env_path "${stack_dir}")"
|
|
||||||
|
|
||||||
if [ ! -f "${metadata_path}" ]; then
|
|
||||||
return 31
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "${env_path}" ]; then
|
|
||||||
return 32
|
|
||||||
fi
|
|
||||||
|
|
||||||
stack_topology="$(get_stack_topology "${stack_dir}" || true)"
|
|
||||||
if [ -z "${stack_topology}" ]; then
|
|
||||||
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata returns 33.
|
|
||||||
EASY_DOCKER_COMPOSE_ERROR_DETAIL="metadata.json missing topology"
|
|
||||||
return 33
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "${stack_topology}" in
|
|
||||||
"single-host") ;;
|
|
||||||
*)
|
|
||||||
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata returns 34.
|
|
||||||
EASY_DOCKER_COMPOSE_ERROR_DETAIL="${stack_topology}"
|
|
||||||
return 34
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
env_erpnext_version="$(get_env_file_key_value "${env_path}" "ERPNEXT_VERSION" || true)"
|
|
||||||
if [ -z "${env_erpnext_version}" ]; then
|
|
||||||
fallback_erpnext_version="$(get_default_erpnext_version || true)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
configured_pull_policy="$(get_env_file_key_value "${env_path}" "PULL_POLICY" || true)"
|
|
||||||
if [ -z "${configured_pull_policy}" ]; then
|
|
||||||
custom_image="$(get_env_file_key_value "${env_path}" "CUSTOM_IMAGE" || true)"
|
|
||||||
custom_tag="$(get_env_file_key_value "${env_path}" "CUSTOM_TAG" || true)"
|
|
||||||
if [ -n "${custom_image}" ] && [ -n "${custom_tag}" ]; then
|
|
||||||
image_ref="${custom_image}:${custom_tag}"
|
|
||||||
if docker image inspect "${image_ref}" >/dev/null 2>&1; then
|
|
||||||
runtime_pull_policy="if_not_present"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
compose_files_lines="$(get_metadata_compose_files_lines "${metadata_path}" || true)"
|
|
||||||
if [ -z "${compose_files_lines}" ]; then
|
|
||||||
return 35
|
|
||||||
fi
|
|
||||||
|
|
||||||
repo_root="$(get_easy_docker_repo_root)"
|
|
||||||
while IFS= read -r compose_file; do
|
|
||||||
if [ -z "${compose_file}" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
source_compose_path="${repo_root}/${compose_file}"
|
|
||||||
if [ ! -f "${source_compose_path}" ]; then
|
|
||||||
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata returns 36.
|
|
||||||
EASY_DOCKER_COMPOSE_ERROR_DETAIL="${source_compose_path}"
|
|
||||||
return 36
|
|
||||||
fi
|
|
||||||
|
|
||||||
compose_args+=(-f "${source_compose_path}")
|
|
||||||
done <<EOF
|
|
||||||
${compose_files_lines}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ "${#compose_args[@]}" -eq 0 ]; then
|
|
||||||
return 35
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "${fallback_erpnext_version}" ] && [ -n "${runtime_pull_policy}" ]; then
|
|
||||||
if ! ERPNEXT_VERSION="${fallback_erpnext_version}" PULL_POLICY="${runtime_pull_policy}" docker compose --env-file "${env_path}" "${compose_args[@]}" up -d; then
|
|
||||||
return 37
|
|
||||||
fi
|
|
||||||
elif [ -n "${fallback_erpnext_version}" ]; then
|
|
||||||
if ! ERPNEXT_VERSION="${fallback_erpnext_version}" docker compose --env-file "${env_path}" "${compose_args[@]}" up -d; then
|
|
||||||
return 37
|
|
||||||
fi
|
|
||||||
elif [ -n "${runtime_pull_policy}" ]; then
|
|
||||||
if ! PULL_POLICY="${runtime_pull_policy}" docker compose --env-file "${env_path}" "${compose_args[@]}" up -d; then
|
|
||||||
return 37
|
|
||||||
fi
|
|
||||||
elif ! docker compose --env-file "${env_path}" "${compose_args[@]}" up -d; then
|
|
||||||
return 37
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
get_stack_compose_runtime_status_label() {
|
|
||||||
local result_var="${1}"
|
|
||||||
local stack_dir="${2}"
|
|
||||||
local metadata_path=""
|
|
||||||
local env_path=""
|
|
||||||
local stack_topology=""
|
|
||||||
local compose_files_lines=""
|
|
||||||
local compose_file=""
|
|
||||||
local source_compose_path=""
|
|
||||||
local env_erpnext_version=""
|
|
||||||
local fallback_erpnext_version=""
|
|
||||||
local running_services_lines=""
|
|
||||||
local compose_status=0
|
|
||||||
local running_services_count=0
|
|
||||||
local repo_root=""
|
|
||||||
local status_label=""
|
|
||||||
local -a compose_args=()
|
|
||||||
|
|
||||||
metadata_path="${stack_dir}/metadata.json"
|
|
||||||
env_path="$(get_stack_env_path "${stack_dir}")"
|
|
||||||
|
|
||||||
if [ ! -f "${metadata_path}" ]; then
|
|
||||||
printf -v "${result_var}" "%s" "Unknown (metadata missing)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
stack_topology="$(get_stack_topology "${stack_dir}" || true)"
|
|
||||||
if [ -z "${stack_topology}" ]; then
|
|
||||||
printf -v "${result_var}" "%s" "Unknown (topology missing)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "${stack_topology}" in
|
|
||||||
"single-host") ;;
|
|
||||||
*)
|
|
||||||
printf -v "${result_var}" "%s" "N/A (${stack_topology})"
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ ! -f "${env_path}" ]; then
|
|
||||||
printf -v "${result_var}" "%s" "Unknown (env missing)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
env_erpnext_version="$(get_env_file_key_value "${env_path}" "ERPNEXT_VERSION" || true)"
|
|
||||||
if [ -z "${env_erpnext_version}" ]; then
|
|
||||||
fallback_erpnext_version="$(get_default_erpnext_version || true)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
compose_files_lines="$(get_metadata_compose_files_lines "${metadata_path}" || true)"
|
|
||||||
if [ -z "${compose_files_lines}" ]; then
|
|
||||||
printf -v "${result_var}" "%s" "Unknown (compose files missing)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
repo_root="$(get_easy_docker_repo_root)"
|
|
||||||
while IFS= read -r compose_file; do
|
|
||||||
if [ -z "${compose_file}" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
source_compose_path="${repo_root}/${compose_file}"
|
|
||||||
if [ ! -f "${source_compose_path}" ]; then
|
|
||||||
printf -v "${result_var}" "%s" "Unknown (missing file: ${compose_file})"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
compose_args+=(-f "${source_compose_path}")
|
|
||||||
done <<EOF
|
|
||||||
${compose_files_lines}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ "${#compose_args[@]}" -eq 0 ]; then
|
|
||||||
printf -v "${result_var}" "%s" "Unknown (compose files missing)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "${fallback_erpnext_version}" ]; then
|
|
||||||
running_services_lines="$(
|
|
||||||
ERPNEXT_VERSION="${fallback_erpnext_version}" docker compose --env-file "${env_path}" "${compose_args[@]}" ps --status running --services 2>/dev/null
|
|
||||||
)"
|
|
||||||
compose_status=$?
|
|
||||||
else
|
|
||||||
running_services_lines="$(
|
|
||||||
docker compose --env-file "${env_path}" "${compose_args[@]}" ps --status running --services 2>/dev/null
|
|
||||||
)"
|
|
||||||
compose_status=$?
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${compose_status}" -ne 0 ]; then
|
|
||||||
printf -v "${result_var}" "%s" "Unknown (docker compose status failed)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
while IFS= read -r compose_file; do
|
|
||||||
if [ -n "${compose_file}" ]; then
|
|
||||||
running_services_count=$((running_services_count + 1))
|
|
||||||
fi
|
|
||||||
done <<EOF
|
|
||||||
${running_services_lines}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ "${running_services_count}" -gt 0 ]; then
|
|
||||||
status_label="Running (${running_services_count} services)"
|
|
||||||
else
|
|
||||||
status_label="Not running"
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf -v "${result_var}" "%s" "${status_label}"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
build_stack_custom_image() {
|
|
||||||
local stack_dir="${1}"
|
|
||||||
local metadata_path=""
|
|
||||||
local env_path=""
|
|
||||||
local apps_json_path=""
|
|
||||||
local custom_image=""
|
|
||||||
local custom_tag=""
|
|
||||||
local frappe_branch=""
|
|
||||||
local frappe_path="https://github.com/frappe/frappe"
|
|
||||||
local repo_root=""
|
|
||||||
local containerfile_path=""
|
|
||||||
local apps_json_base64=""
|
|
||||||
local apps_refs_lines=""
|
|
||||||
local app_ref_line=""
|
|
||||||
local app_url=""
|
|
||||||
local app_branch=""
|
|
||||||
local git_error=""
|
|
||||||
local image_ref=""
|
|
||||||
|
|
||||||
EASY_DOCKER_BUILD_ERROR_DETAIL=""
|
|
||||||
|
|
||||||
metadata_path="${stack_dir}/metadata.json"
|
|
||||||
env_path="$(get_stack_env_path "${stack_dir}")"
|
|
||||||
apps_json_path="${stack_dir}/apps.json"
|
|
||||||
|
|
||||||
if [ ! -f "${metadata_path}" ]; then
|
|
||||||
return 11
|
|
||||||
fi
|
|
||||||
if [ ! -f "${env_path}" ]; then
|
|
||||||
return 12
|
|
||||||
fi
|
|
||||||
|
|
||||||
custom_image="$(get_env_file_key_value "${env_path}" "CUSTOM_IMAGE" || true)"
|
|
||||||
custom_tag="$(get_env_file_key_value "${env_path}" "CUSTOM_TAG" || true)"
|
|
||||||
frappe_branch="$(get_stack_frappe_branch "${stack_dir}" || true)"
|
|
||||||
if [ -z "${custom_image}" ]; then
|
|
||||||
return 13
|
|
||||||
fi
|
|
||||||
if [ -z "${custom_tag}" ]; then
|
|
||||||
return 14
|
|
||||||
fi
|
|
||||||
if [ -z "${frappe_branch}" ]; then
|
|
||||||
return 15
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Keep apps.json aligned with current metadata app selection before build.
|
|
||||||
if ! persist_stack_apps_json_from_metadata_apps "${stack_dir}"; then
|
|
||||||
return 16
|
|
||||||
fi
|
|
||||||
if [ ! -f "${apps_json_path}" ]; then
|
|
||||||
return 17
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command_exists git; then
|
|
||||||
return 22
|
|
||||||
fi
|
|
||||||
|
|
||||||
apps_refs_lines="$(
|
|
||||||
awk '
|
|
||||||
match($0, /"url"[[:space:]]*:[[:space:]]*"([^"]+)"/, url_parts) &&
|
|
||||||
match($0, /"branch"[[:space:]]*:[[:space:]]*"([^"]+)"/, branch_parts) {
|
|
||||||
print url_parts[1] "|" branch_parts[1]
|
|
||||||
}
|
|
||||||
' "${apps_json_path}"
|
|
||||||
)"
|
|
||||||
if [ -z "${apps_refs_lines}" ]; then
|
|
||||||
return 23
|
|
||||||
fi
|
|
||||||
|
|
||||||
while IFS= read -r app_ref_line; do
|
|
||||||
if [ -z "${app_ref_line}" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
app_url="${app_ref_line%%|*}"
|
|
||||||
app_branch="${app_ref_line#*|}"
|
|
||||||
if [ -z "${app_url}" ] || [ -z "${app_branch}" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
if git_error="$(git ls-remote --exit-code --heads "${app_url}" "${app_branch}" 2>&1)"; then
|
|
||||||
:
|
|
||||||
else
|
|
||||||
# shellcheck disable=SC2034 # Read by manage flow after build_stack_custom_image returns 24.
|
|
||||||
EASY_DOCKER_BUILD_ERROR_DETAIL="$(printf '%s@%s :: %s' "${app_url}" "${app_branch}" "${git_error}")"
|
|
||||||
return 24
|
|
||||||
fi
|
|
||||||
done <<EOF
|
|
||||||
${apps_refs_lines}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if ! command_exists base64; then
|
|
||||||
return 18
|
|
||||||
fi
|
|
||||||
|
|
||||||
apps_json_base64="$(base64 "${apps_json_path}" | tr -d '\r\n')"
|
|
||||||
if [ -z "${apps_json_base64}" ]; then
|
|
||||||
return 19
|
|
||||||
fi
|
|
||||||
|
|
||||||
repo_root="$(get_easy_docker_repo_root)"
|
|
||||||
containerfile_path="${repo_root}/images/layered/Containerfile"
|
|
||||||
if [ ! -f "${containerfile_path}" ]; then
|
|
||||||
return 20
|
|
||||||
fi
|
|
||||||
|
|
||||||
image_ref="${custom_image}:${custom_tag}"
|
|
||||||
|
|
||||||
docker build \
|
|
||||||
-f "${containerfile_path}" \
|
|
||||||
--build-arg "FRAPPE_BRANCH=${frappe_branch}" \
|
|
||||||
--build-arg "FRAPPE_PATH=${frappe_path}" \
|
|
||||||
--build-arg "APPS_JSON_BASE64=${apps_json_base64}" \
|
|
||||||
-t "${image_ref}" \
|
|
||||||
"${repo_root}" || return 21
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
|
||||||
120
scripts/easy-docker/lib/app/wizard/common/compose/build.sh
Executable file
120
scripts/easy-docker/lib/app/wizard/common/compose/build.sh
Executable file
|
|
@ -0,0 +1,120 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
build_stack_custom_image() {
|
||||||
|
local stack_dir="${1}"
|
||||||
|
local metadata_path=""
|
||||||
|
local env_path=""
|
||||||
|
local apps_json_path=""
|
||||||
|
local custom_image=""
|
||||||
|
local custom_tag=""
|
||||||
|
local frappe_branch=""
|
||||||
|
local frappe_path="https://github.com/frappe/frappe"
|
||||||
|
local repo_root=""
|
||||||
|
local containerfile_path=""
|
||||||
|
local apps_json_base64=""
|
||||||
|
local apps_refs_lines=""
|
||||||
|
local app_ref_line=""
|
||||||
|
local app_url=""
|
||||||
|
local app_branch=""
|
||||||
|
local git_error=""
|
||||||
|
local image_ref=""
|
||||||
|
|
||||||
|
EASY_DOCKER_BUILD_ERROR_DETAIL=""
|
||||||
|
|
||||||
|
metadata_path="${stack_dir}/metadata.json"
|
||||||
|
env_path="$(get_stack_env_path "${stack_dir}")"
|
||||||
|
apps_json_path="${stack_dir}/apps.json"
|
||||||
|
|
||||||
|
if [ ! -f "${metadata_path}" ]; then
|
||||||
|
return 11
|
||||||
|
fi
|
||||||
|
if [ ! -f "${env_path}" ]; then
|
||||||
|
return 12
|
||||||
|
fi
|
||||||
|
|
||||||
|
custom_image="$(get_env_file_key_value "${env_path}" "CUSTOM_IMAGE" || true)"
|
||||||
|
custom_tag="$(get_env_file_key_value "${env_path}" "CUSTOM_TAG" || true)"
|
||||||
|
frappe_branch="$(get_stack_frappe_branch "${stack_dir}" || true)"
|
||||||
|
if [ -z "${custom_image}" ]; then
|
||||||
|
return 13
|
||||||
|
fi
|
||||||
|
if [ -z "${custom_tag}" ]; then
|
||||||
|
return 14
|
||||||
|
fi
|
||||||
|
if [ -z "${frappe_branch}" ]; then
|
||||||
|
return 15
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Keep apps.json aligned with current metadata app selection before build.
|
||||||
|
if ! persist_stack_apps_json_from_metadata_apps "${stack_dir}"; then
|
||||||
|
return 16
|
||||||
|
fi
|
||||||
|
if [ ! -f "${apps_json_path}" ]; then
|
||||||
|
return 17
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command_exists git; then
|
||||||
|
return 22
|
||||||
|
fi
|
||||||
|
|
||||||
|
apps_refs_lines="$(
|
||||||
|
awk '
|
||||||
|
match($0, /"url"[[:space:]]*:[[:space:]]*"([^"]+)"/, url_parts) &&
|
||||||
|
match($0, /"branch"[[:space:]]*:[[:space:]]*"([^"]+)"/, branch_parts) {
|
||||||
|
print url_parts[1] "|" branch_parts[1]
|
||||||
|
}
|
||||||
|
' "${apps_json_path}"
|
||||||
|
)"
|
||||||
|
if [ -z "${apps_refs_lines}" ]; then
|
||||||
|
return 23
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r app_ref_line; do
|
||||||
|
if [ -z "${app_ref_line}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
app_url="${app_ref_line%%|*}"
|
||||||
|
app_branch="${app_ref_line#*|}"
|
||||||
|
if [ -z "${app_url}" ] || [ -z "${app_branch}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git_error="$(git ls-remote --exit-code --heads "${app_url}" "${app_branch}" 2>&1)"; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
# shellcheck disable=SC2034 # Read by manage flow after build_stack_custom_image returns 24.
|
||||||
|
EASY_DOCKER_BUILD_ERROR_DETAIL="$(printf '%s@%s :: %s' "${app_url}" "${app_branch}" "${git_error}")"
|
||||||
|
return 24
|
||||||
|
fi
|
||||||
|
done <<EOF
|
||||||
|
${apps_refs_lines}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if ! command_exists base64; then
|
||||||
|
return 18
|
||||||
|
fi
|
||||||
|
|
||||||
|
apps_json_base64="$(base64 "${apps_json_path}" | tr -d '\r\n')"
|
||||||
|
if [ -z "${apps_json_base64}" ]; then
|
||||||
|
return 19
|
||||||
|
fi
|
||||||
|
|
||||||
|
repo_root="$(get_easy_docker_repo_root)"
|
||||||
|
containerfile_path="${repo_root}/images/layered/Containerfile"
|
||||||
|
if [ ! -f "${containerfile_path}" ]; then
|
||||||
|
return 20
|
||||||
|
fi
|
||||||
|
|
||||||
|
image_ref="${custom_image}:${custom_tag}"
|
||||||
|
|
||||||
|
docker build \
|
||||||
|
-f "${containerfile_path}" \
|
||||||
|
--build-arg "FRAPPE_BRANCH=${frappe_branch}" \
|
||||||
|
--build-arg "FRAPPE_PATH=${frappe_path}" \
|
||||||
|
--build-arg "APPS_JSON_BASE64=${apps_json_base64}" \
|
||||||
|
-t "${image_ref}" \
|
||||||
|
"${repo_root}" || return 21
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
76
scripts/easy-docker/lib/app/wizard/common/compose/render.sh
Executable file
76
scripts/easy-docker/lib/app/wizard/common/compose/render.sh
Executable file
|
|
@ -0,0 +1,76 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
render_stack_compose_from_metadata() {
|
||||||
|
local stack_dir="${1}"
|
||||||
|
local metadata_path=""
|
||||||
|
local env_path=""
|
||||||
|
local generated_compose_path=""
|
||||||
|
local generated_compose_tmp_path=""
|
||||||
|
local compose_files_lines=""
|
||||||
|
local compose_file=""
|
||||||
|
local source_compose_path=""
|
||||||
|
local env_erpnext_version=""
|
||||||
|
local fallback_erpnext_version=""
|
||||||
|
local repo_root=""
|
||||||
|
local -a compose_args=()
|
||||||
|
|
||||||
|
metadata_path="${stack_dir}/metadata.json"
|
||||||
|
env_path="$(get_stack_env_path "${stack_dir}")"
|
||||||
|
generated_compose_path="$(get_stack_generated_compose_path "${stack_dir}")"
|
||||||
|
generated_compose_tmp_path="${generated_compose_path}.tmp"
|
||||||
|
|
||||||
|
if [ ! -f "${metadata_path}" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${env_path}" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
env_erpnext_version="$(get_env_file_key_value "${env_path}" "ERPNEXT_VERSION" || true)"
|
||||||
|
if [ -z "${env_erpnext_version}" ]; then
|
||||||
|
fallback_erpnext_version="$(get_default_erpnext_version || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
compose_files_lines="$(get_metadata_compose_files_lines "${metadata_path}" || true)"
|
||||||
|
if [ -z "${compose_files_lines}" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
repo_root="$(get_easy_docker_repo_root)"
|
||||||
|
while IFS= read -r compose_file; do
|
||||||
|
if [ -z "${compose_file}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
source_compose_path="${repo_root}/${compose_file}"
|
||||||
|
if [ ! -f "${source_compose_path}" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
compose_args+=(-f "${source_compose_path}")
|
||||||
|
done <<EOF
|
||||||
|
${compose_files_lines}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ "${#compose_args[@]}" -eq 0 ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${fallback_erpnext_version}" ]; then
|
||||||
|
if ! ERPNEXT_VERSION="${fallback_erpnext_version}" docker compose --env-file "${env_path}" "${compose_args[@]}" config >"${generated_compose_tmp_path}"; then
|
||||||
|
rm -f -- "${generated_compose_tmp_path}" >/dev/null 2>&1 || true
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
elif ! docker compose --env-file "${env_path}" "${compose_args[@]}" config >"${generated_compose_tmp_path}"; then
|
||||||
|
rm -f -- "${generated_compose_tmp_path}" >/dev/null 2>&1 || true
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! mv -- "${generated_compose_tmp_path}" "${generated_compose_path}"; then
|
||||||
|
rm -f -- "${generated_compose_tmp_path}" >/dev/null 2>&1 || true
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
242
scripts/easy-docker/lib/app/wizard/common/compose/start.sh
Executable file
242
scripts/easy-docker/lib/app/wizard/common/compose/start.sh
Executable file
|
|
@ -0,0 +1,242 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
start_stack_with_compose_from_metadata() {
|
||||||
|
local stack_dir="${1}"
|
||||||
|
local metadata_path=""
|
||||||
|
local env_path=""
|
||||||
|
local compose_files_lines=""
|
||||||
|
local compose_file=""
|
||||||
|
local source_compose_path=""
|
||||||
|
local env_erpnext_version=""
|
||||||
|
local fallback_erpnext_version=""
|
||||||
|
local configured_pull_policy=""
|
||||||
|
local runtime_pull_policy=""
|
||||||
|
local custom_image=""
|
||||||
|
local custom_tag=""
|
||||||
|
local image_ref=""
|
||||||
|
local image_inspect_error=""
|
||||||
|
local stack_topology=""
|
||||||
|
local repo_root=""
|
||||||
|
local -a compose_args=()
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata fails.
|
||||||
|
EASY_DOCKER_COMPOSE_ERROR_DETAIL=""
|
||||||
|
|
||||||
|
metadata_path="${stack_dir}/metadata.json"
|
||||||
|
env_path="$(get_stack_env_path "${stack_dir}")"
|
||||||
|
|
||||||
|
if [ ! -f "${metadata_path}" ]; then
|
||||||
|
return 31
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${env_path}" ]; then
|
||||||
|
return 32
|
||||||
|
fi
|
||||||
|
|
||||||
|
stack_topology="$(get_stack_topology "${stack_dir}" || true)"
|
||||||
|
if [ -z "${stack_topology}" ]; then
|
||||||
|
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata returns 33.
|
||||||
|
EASY_DOCKER_COMPOSE_ERROR_DETAIL="metadata.json missing topology"
|
||||||
|
return 33
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${stack_topology}" in
|
||||||
|
"single-host") ;;
|
||||||
|
*)
|
||||||
|
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata returns 34.
|
||||||
|
EASY_DOCKER_COMPOSE_ERROR_DETAIL="${stack_topology}"
|
||||||
|
return 34
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
env_erpnext_version="$(get_env_file_key_value "${env_path}" "ERPNEXT_VERSION" || true)"
|
||||||
|
if [ -z "${env_erpnext_version}" ]; then
|
||||||
|
fallback_erpnext_version="$(get_default_erpnext_version || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
configured_pull_policy="$(get_env_file_key_value "${env_path}" "PULL_POLICY" || true)"
|
||||||
|
if [ -z "${configured_pull_policy}" ]; then
|
||||||
|
custom_image="$(get_env_file_key_value "${env_path}" "CUSTOM_IMAGE" || true)"
|
||||||
|
custom_tag="$(get_env_file_key_value "${env_path}" "CUSTOM_TAG" || true)"
|
||||||
|
if [ -n "${custom_image}" ] && [ -n "${custom_tag}" ]; then
|
||||||
|
image_ref="${custom_image}:${custom_tag}"
|
||||||
|
if image_inspect_error="$(docker image inspect "${image_ref}" 2>&1 >/dev/null)"; then
|
||||||
|
runtime_pull_policy="if_not_present"
|
||||||
|
else
|
||||||
|
case "${image_inspect_error}" in
|
||||||
|
*"No such image"* | *"No such object"*)
|
||||||
|
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata returns 38.
|
||||||
|
EASY_DOCKER_COMPOSE_ERROR_DETAIL="${image_ref}"
|
||||||
|
return 38
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [ -z "${image_inspect_error}" ]; then
|
||||||
|
image_inspect_error="docker image inspect failed for ${image_ref}"
|
||||||
|
fi
|
||||||
|
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata returns 39.
|
||||||
|
EASY_DOCKER_COMPOSE_ERROR_DETAIL="${image_inspect_error}"
|
||||||
|
return 39
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
compose_files_lines="$(get_metadata_compose_files_lines "${metadata_path}" || true)"
|
||||||
|
if [ -z "${compose_files_lines}" ]; then
|
||||||
|
return 35
|
||||||
|
fi
|
||||||
|
|
||||||
|
repo_root="$(get_easy_docker_repo_root)"
|
||||||
|
while IFS= read -r compose_file; do
|
||||||
|
if [ -z "${compose_file}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
source_compose_path="${repo_root}/${compose_file}"
|
||||||
|
if [ ! -f "${source_compose_path}" ]; then
|
||||||
|
# shellcheck disable=SC2034 # Read by manage flow after start_stack_with_compose_from_metadata returns 36.
|
||||||
|
EASY_DOCKER_COMPOSE_ERROR_DETAIL="${source_compose_path}"
|
||||||
|
return 36
|
||||||
|
fi
|
||||||
|
|
||||||
|
compose_args+=(-f "${source_compose_path}")
|
||||||
|
done <<EOF
|
||||||
|
${compose_files_lines}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ "${#compose_args[@]}" -eq 0 ]; then
|
||||||
|
return 35
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${fallback_erpnext_version}" ] && [ -n "${runtime_pull_policy}" ]; then
|
||||||
|
if ! ERPNEXT_VERSION="${fallback_erpnext_version}" PULL_POLICY="${runtime_pull_policy}" docker compose --env-file "${env_path}" "${compose_args[@]}" up -d; then
|
||||||
|
return 37
|
||||||
|
fi
|
||||||
|
elif [ -n "${fallback_erpnext_version}" ]; then
|
||||||
|
if ! ERPNEXT_VERSION="${fallback_erpnext_version}" docker compose --env-file "${env_path}" "${compose_args[@]}" up -d; then
|
||||||
|
return 37
|
||||||
|
fi
|
||||||
|
elif [ -n "${runtime_pull_policy}" ]; then
|
||||||
|
if ! PULL_POLICY="${runtime_pull_policy}" docker compose --env-file "${env_path}" "${compose_args[@]}" up -d; then
|
||||||
|
return 37
|
||||||
|
fi
|
||||||
|
elif ! docker compose --env-file "${env_path}" "${compose_args[@]}" up -d; then
|
||||||
|
return 37
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
get_stack_compose_runtime_status_label() {
|
||||||
|
local result_var="${1}"
|
||||||
|
local stack_dir="${2}"
|
||||||
|
local metadata_path=""
|
||||||
|
local env_path=""
|
||||||
|
local stack_topology=""
|
||||||
|
local compose_files_lines=""
|
||||||
|
local compose_file=""
|
||||||
|
local source_compose_path=""
|
||||||
|
local env_erpnext_version=""
|
||||||
|
local fallback_erpnext_version=""
|
||||||
|
local running_services_lines=""
|
||||||
|
local compose_status=0
|
||||||
|
local running_services_count=0
|
||||||
|
local repo_root=""
|
||||||
|
local status_label=""
|
||||||
|
local -a compose_args=()
|
||||||
|
|
||||||
|
metadata_path="${stack_dir}/metadata.json"
|
||||||
|
env_path="$(get_stack_env_path "${stack_dir}")"
|
||||||
|
|
||||||
|
if [ ! -f "${metadata_path}" ]; then
|
||||||
|
printf -v "${result_var}" "%s" "Unknown (metadata missing)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
stack_topology="$(get_stack_topology "${stack_dir}" || true)"
|
||||||
|
if [ -z "${stack_topology}" ]; then
|
||||||
|
printf -v "${result_var}" "%s" "Unknown (topology missing)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${stack_topology}" in
|
||||||
|
"single-host") ;;
|
||||||
|
*)
|
||||||
|
printf -v "${result_var}" "%s" "N/A (${stack_topology})"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ ! -f "${env_path}" ]; then
|
||||||
|
printf -v "${result_var}" "%s" "Unknown (env missing)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
env_erpnext_version="$(get_env_file_key_value "${env_path}" "ERPNEXT_VERSION" || true)"
|
||||||
|
if [ -z "${env_erpnext_version}" ]; then
|
||||||
|
fallback_erpnext_version="$(get_default_erpnext_version || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
compose_files_lines="$(get_metadata_compose_files_lines "${metadata_path}" || true)"
|
||||||
|
if [ -z "${compose_files_lines}" ]; then
|
||||||
|
printf -v "${result_var}" "%s" "Unknown (compose files missing)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
repo_root="$(get_easy_docker_repo_root)"
|
||||||
|
while IFS= read -r compose_file; do
|
||||||
|
if [ -z "${compose_file}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
source_compose_path="${repo_root}/${compose_file}"
|
||||||
|
if [ ! -f "${source_compose_path}" ]; then
|
||||||
|
printf -v "${result_var}" "%s" "Unknown (missing file: ${compose_file})"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
compose_args+=(-f "${source_compose_path}")
|
||||||
|
done <<EOF
|
||||||
|
${compose_files_lines}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ "${#compose_args[@]}" -eq 0 ]; then
|
||||||
|
printf -v "${result_var}" "%s" "Unknown (compose files missing)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${fallback_erpnext_version}" ]; then
|
||||||
|
running_services_lines="$(
|
||||||
|
ERPNEXT_VERSION="${fallback_erpnext_version}" docker compose --env-file "${env_path}" "${compose_args[@]}" ps --status running --services 2>/dev/null
|
||||||
|
)"
|
||||||
|
compose_status=$?
|
||||||
|
else
|
||||||
|
running_services_lines="$(
|
||||||
|
docker compose --env-file "${env_path}" "${compose_args[@]}" ps --status running --services 2>/dev/null
|
||||||
|
)"
|
||||||
|
compose_status=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${compose_status}" -ne 0 ]; then
|
||||||
|
printf -v "${result_var}" "%s" "Unknown (docker compose status failed)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r compose_file; do
|
||||||
|
if [ -n "${compose_file}" ]; then
|
||||||
|
running_services_count=$((running_services_count + 1))
|
||||||
|
fi
|
||||||
|
done <<EOF
|
||||||
|
${running_services_lines}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ "${running_services_count}" -gt 0 ]; then
|
||||||
|
status_label="Running (${running_services_count} services)"
|
||||||
|
else
|
||||||
|
status_label="Not running"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf -v "${result_var}" "%s" "${status_label}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
@ -1,425 +1,15 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
get_setup_display_label() {
|
load_production_screen_modules() {
|
||||||
local setup_type="${1}"
|
local screen_dir=""
|
||||||
|
screen_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
case "${setup_type}" in
|
# shellcheck source=scripts/easy-docker/lib/ui/screens/production/setup.sh
|
||||||
development)
|
source "${screen_dir}/production/setup.sh"
|
||||||
printf 'Development'
|
# shellcheck source=scripts/easy-docker/lib/ui/screens/production/topology.sh
|
||||||
;;
|
source "${screen_dir}/production/topology.sh"
|
||||||
production | *)
|
# shellcheck source=scripts/easy-docker/lib/ui/screens/production/manage.sh
|
||||||
printf 'Production'
|
source "${screen_dir}/production/manage.sh"
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
}
|
||||||
|
|
||||||
show_setup_menu() {
|
load_production_screen_modules
|
||||||
local setup_type="${1}"
|
|
||||||
local setup_label=""
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
setup_label="$(get_setup_display_label "${setup_type}")"
|
|
||||||
status_text="$(printf "%s stack\n\nChoose whether to create a new stack or manage an existing one." "${setup_label}")"
|
|
||||||
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 8 \
|
|
||||||
--header "${setup_label} stack actions" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"Create new stack" \
|
|
||||||
"Manage existing stacks" \
|
|
||||||
"Back" \
|
|
||||||
"Exit and close easy-docker"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_production_setup_menu() {
|
|
||||||
show_setup_menu "production"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_development_setup_menu() {
|
|
||||||
show_setup_menu "development"
|
|
||||||
}
|
|
||||||
|
|
||||||
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_frappe_version_profile_menu() {
|
|
||||||
local stack_name="${1}"
|
|
||||||
local options_lines="${2:-}"
|
|
||||||
local selected_label="${3:-}"
|
|
||||||
local status_text=""
|
|
||||||
local option_line=""
|
|
||||||
local -a menu_options=()
|
|
||||||
local -a gum_args=()
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
status_text="$(printf "Create stack: %s\n\nSelect the Frappe branch profile from frappe.tsv.\nThis sets the stack default for branch suggestions." "${stack_name}")"
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
while IFS= read -r option_line; do
|
|
||||||
if [ -z "${option_line}" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
menu_options+=("${option_line}")
|
|
||||||
done <<EOF
|
|
||||||
${options_lines}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ "${#menu_options[@]}" -eq 0 ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
gum_args=(
|
|
||||||
--height 10
|
|
||||||
--header "Frappe branch profile"
|
|
||||||
--cursor.foreground 63
|
|
||||||
--selected.foreground 45
|
|
||||||
)
|
|
||||||
if [ -n "${selected_label}" ]; then
|
|
||||||
gum_args+=(--selected "${selected_label}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
gum choose "${gum_args[@]}" "${menu_options[@]}" "Back"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_stack_topology_menu() {
|
|
||||||
local stack_dir="${1}"
|
|
||||||
local stack_name=""
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
stack_name="${stack_dir##*/}"
|
|
||||||
status_text="$(printf "Stack created: %s\nDirectory: %s\n\nChoose the deployment topology.\n\n- Single-host: easiest setup on one server.\n- Split services: separate app and infra stacks for more control." "${stack_name}" "${stack_dir}")"
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 8 \
|
|
||||||
--header "Topology" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"Single-host (recommended)" \
|
|
||||||
"Split services" \
|
|
||||||
"Abort wizard to main menu"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_single_host_proxy_menu() {
|
|
||||||
local stack_dir="${1}"
|
|
||||||
local stack_name=""
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
stack_name="${stack_dir##*/}"
|
|
||||||
status_text="$(printf "Stack: %s\n\nSingle-host setup (step 1/3)\nChoose the proxy mode.\n\n- Traefik and nginx-proxy run inside compose.\n- Caddy is external and uses the no-proxy compose mode." "${stack_name}")"
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 11 \
|
|
||||||
--header "Single-host: proxy mode" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"Traefik (HTTP, built-in proxy)" \
|
|
||||||
"Traefik (HTTPS + Let's Encrypt)" \
|
|
||||||
"nginx-proxy (HTTP)" \
|
|
||||||
"nginx-proxy + acme-companion (HTTPS)" \
|
|
||||||
"Caddy (external reverse proxy)" \
|
|
||||||
"No reverse proxy (direct :8080)" \
|
|
||||||
"Back to topology selection"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_single_host_database_menu() {
|
|
||||||
local stack_dir="${1}"
|
|
||||||
local stack_name=""
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
stack_name="${stack_dir##*/}"
|
|
||||||
status_text="$(printf "Stack: %s\n\nSingle-host setup (step 2/3)\nChoose the database service." "${stack_name}")"
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 8 \
|
|
||||||
--header "Single-host: database" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"MariaDB (recommended)" \
|
|
||||||
"PostgreSQL" \
|
|
||||||
"Back to topology selection"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_single_host_redis_menu() {
|
|
||||||
local stack_dir="${1}"
|
|
||||||
local stack_name=""
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
stack_name="${stack_dir##*/}"
|
|
||||||
status_text="$(printf "Stack: %s\n\nSingle-host setup (step 3/3)\nChoose whether Redis services should be included." "${stack_name}")"
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 8 \
|
|
||||||
--header "Single-host: redis" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"Include Redis (recommended)" \
|
|
||||||
"Skip Redis (experienced users only)" \
|
|
||||||
"Back to topology selection"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_custom_modular_apps_multi_select() {
|
|
||||||
local stack_dir="${1}"
|
|
||||||
local options_lines="${2:-}"
|
|
||||||
local selected_labels_csv="${3:-}"
|
|
||||||
local stack_name=""
|
|
||||||
local status_text=""
|
|
||||||
local option_line=""
|
|
||||||
local selected_label=""
|
|
||||||
local -a menu_options=()
|
|
||||||
local -a selected_labels=()
|
|
||||||
local -a gum_args=()
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
stack_name="${stack_dir##*/}"
|
|
||||||
status_text="$(printf "Stack: %s\n\nApps\nUse Space to toggle apps from apps.tsv. Press Enter to continue to branch selection per app.\nUse Ctrl+C to go back." "${stack_name}")"
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
while IFS= read -r option_line; do
|
|
||||||
if [ -z "${option_line}" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
menu_options+=("${option_line}")
|
|
||||||
done <<EOF
|
|
||||||
${options_lines}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ "${#menu_options[@]}" -eq 0 ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
gum_args=(
|
|
||||||
--no-limit
|
|
||||||
--height 14
|
|
||||||
--header "Apps"
|
|
||||||
--cursor.foreground 63
|
|
||||||
--selected.foreground 45
|
|
||||||
)
|
|
||||||
if [ -n "${selected_labels_csv}" ]; then
|
|
||||||
IFS=',' read -r -a selected_labels <<<"${selected_labels_csv}"
|
|
||||||
for selected_label in "${selected_labels[@]}"; do
|
|
||||||
trim_predefined_catalog_field selected_label "${selected_label}"
|
|
||||||
if [ -z "${selected_label}" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
gum_args+=(--selected "${selected_label}")
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
gum choose "${gum_args[@]}" "${menu_options[@]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt_single_host_env_value() {
|
|
||||||
local stack_dir="${1}"
|
|
||||||
local variable_name="${2}"
|
|
||||||
local guidance_text="${3}"
|
|
||||||
local placeholder="${4:-}"
|
|
||||||
local render_context="${5:-1}"
|
|
||||||
local input_feedback="${6:-}"
|
|
||||||
local stack_name=""
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
if [ "${render_context}" = "1" ]; then
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
stack_name="${stack_dir##*/}"
|
|
||||||
guidance_text="${guidance_text//\\n/$'\n'}"
|
|
||||||
status_text="$(printf "Stack: %s\n\nConfigure %s\n\n%s" "${stack_name}" "${variable_name}" "${guidance_text}")"
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "${input_feedback}" ]; then
|
|
||||||
gum style --foreground 214 "${input_feedback}" >&2
|
|
||||||
fi
|
|
||||||
|
|
||||||
gum input \
|
|
||||||
--header "${variable_name}" \
|
|
||||||
--prompt "value> " \
|
|
||||||
--placeholder "${placeholder}"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_split_services_examples() {
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
status_text="$(printf "Split services examples\n\n- DB in a separate stack/project.\n- Proxy in a separate stack/project.\n- One or more app stacks referencing shared infra.")"
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 7 \
|
|
||||||
--header "Split services" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"Use this topology" \
|
|
||||||
"Back to topology selection"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_abort_wizard_prompt() {
|
|
||||||
local stack_dir="${1}"
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
status_text="$(printf "Abort wizard\n\nStack directory:\n%s\n\nRollback created files before returning to main menu?" "${stack_dir}")"
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 8 \
|
|
||||||
--header "Abort options" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"Rollback files and return to main menu" \
|
|
||||||
"Keep files and return to main menu" \
|
|
||||||
"Back to topology selection"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_manage_stacks_menu() {
|
|
||||||
local setup_type="${1}"
|
|
||||||
shift
|
|
||||||
local stack_count="${#}"
|
|
||||||
local setup_label=""
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
setup_label="$(get_setup_display_label "${setup_type}")"
|
|
||||||
if [ "${stack_count}" -eq 1 ]; then
|
|
||||||
status_text="$(printf "Manage existing %s stacks\n\n1 stack found. Select a stack." "${setup_label}")"
|
|
||||||
else
|
|
||||||
status_text="$(printf "Manage existing %s stacks\n\n%s stacks found. Select a stack." "${setup_label}" "${stack_count}")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 14 \
|
|
||||||
--header "Existing stacks" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"$@" \
|
|
||||||
"Back" \
|
|
||||||
"Exit and close easy-docker"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_manage_stacks_placeholder() {
|
|
||||||
local setup_type="${1}"
|
|
||||||
local setup_label=""
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
setup_label="$(get_setup_display_label "${setup_type}")"
|
|
||||||
status_text="$(printf "Manage existing %s stacks\n\nNo stacks found in .easy-docker/stacks yet." "${setup_label}")"
|
|
||||||
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 7 \
|
|
||||||
--header "Manage stacks actions" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"Back" \
|
|
||||||
"Exit and close easy-docker"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_manage_stack_actions_menu() {
|
|
||||||
local stack_name="${1}"
|
|
||||||
local stack_dir="${2}"
|
|
||||||
local stack_runtime_status="${3:-Unknown}"
|
|
||||||
local menu_header=""
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
status_text="$(printf "Manage stack\n\nStack: %s\nDirectory: %s\nRuntime status: %s\n\nChoose an action for this stack." "${stack_name}" "${stack_dir}" "${stack_runtime_status}")"
|
|
||||||
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
menu_header="$(printf "Stack actions | %s" "${stack_runtime_status}")"
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 8 \
|
|
||||||
--header "${menu_header}" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"Apps" \
|
|
||||||
"Docker" \
|
|
||||||
"Start stack in Docker Compose" \
|
|
||||||
"Back" \
|
|
||||||
"Exit and close easy-docker"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_manage_stack_apps_menu() {
|
|
||||||
local stack_name="${1}"
|
|
||||||
local stack_dir="${2}"
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
status_text="$(printf "Manage stack apps\n\nStack: %s\nDirectory: %s\n\nChoose an app-related action for this stack." "${stack_name}" "${stack_dir}")"
|
|
||||||
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 8 \
|
|
||||||
--header "Stack apps actions" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"Regenerate apps.json from metadata" \
|
|
||||||
"Select apps and branches" \
|
|
||||||
"Back" \
|
|
||||||
"Exit and close easy-docker"
|
|
||||||
}
|
|
||||||
|
|
||||||
show_manage_stack_docker_menu() {
|
|
||||||
local stack_name="${1}"
|
|
||||||
local stack_dir="${2}"
|
|
||||||
local status_text=""
|
|
||||||
|
|
||||||
render_main_screen 1 >&2
|
|
||||||
|
|
||||||
status_text="$(printf "Manage stack docker\n\nStack: %s\nDirectory: %s\n\nChoose a docker-related action for this stack." "${stack_name}" "${stack_dir}")"
|
|
||||||
|
|
||||||
render_box_message "${status_text}" "0 2" >&2
|
|
||||||
|
|
||||||
gum choose \
|
|
||||||
--height 8 \
|
|
||||||
--header "Stack docker actions" \
|
|
||||||
--cursor.foreground 63 \
|
|
||||||
--selected.foreground 45 \
|
|
||||||
"Build custom image" \
|
|
||||||
"Generate docker compose from env" \
|
|
||||||
"Back" \
|
|
||||||
"Exit and close easy-docker"
|
|
||||||
}
|
|
||||||
|
|
|
||||||
142
scripts/easy-docker/lib/ui/screens/production/manage.sh
Executable file
142
scripts/easy-docker/lib/ui/screens/production/manage.sh
Executable file
|
|
@ -0,0 +1,142 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
show_manage_stacks_menu() {
|
||||||
|
local setup_type="${1}"
|
||||||
|
shift
|
||||||
|
local stack_count="${#}"
|
||||||
|
local setup_label=""
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
setup_label="$(get_setup_display_label "${setup_type}")"
|
||||||
|
if [ "${stack_count}" -eq 1 ]; then
|
||||||
|
status_text="$(printf "Manage existing %s stacks\n\n1 stack found. Select a stack." "${setup_label}")"
|
||||||
|
else
|
||||||
|
status_text="$(printf "Manage existing %s stacks\n\n%s stacks found. Select a stack." "${setup_label}" "${stack_count}")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 14 \
|
||||||
|
--header "Existing stacks" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"$@" \
|
||||||
|
"Back" \
|
||||||
|
"Exit and close easy-docker"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_manage_stacks_placeholder() {
|
||||||
|
local setup_type="${1}"
|
||||||
|
local setup_label=""
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
setup_label="$(get_setup_display_label "${setup_type}")"
|
||||||
|
status_text="$(printf "Manage existing %s stacks\n\nNo stacks found in .easy-docker/stacks yet." "${setup_label}")"
|
||||||
|
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 7 \
|
||||||
|
--header "Manage stacks actions" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"Back" \
|
||||||
|
"Exit and close easy-docker"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_manage_stack_actions_menu() {
|
||||||
|
local stack_name="${1}"
|
||||||
|
local stack_dir="${2}"
|
||||||
|
local stack_runtime_status="${3:-Unknown}"
|
||||||
|
local menu_header=""
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
status_text="$(printf "Manage stack\n\nStack: %s\nDirectory: %s\nRuntime status: %s\n\nChoose an action for this stack." "${stack_name}" "${stack_dir}" "${stack_runtime_status}")"
|
||||||
|
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
menu_header="$(printf "Stack actions | %s" "${stack_runtime_status}")"
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 8 \
|
||||||
|
--header "${menu_header}" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"Apps" \
|
||||||
|
"Docker" \
|
||||||
|
"Start stack in Docker Compose" \
|
||||||
|
"Back" \
|
||||||
|
"Exit and close easy-docker"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_manage_stack_apps_menu() {
|
||||||
|
local stack_name="${1}"
|
||||||
|
local stack_dir="${2}"
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
status_text="$(printf "Manage stack apps\n\nStack: %s\nDirectory: %s\n\nChoose an app-related action for this stack." "${stack_name}" "${stack_dir}")"
|
||||||
|
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 8 \
|
||||||
|
--header "Stack apps actions" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"Regenerate apps.json from metadata" \
|
||||||
|
"Select apps and branches" \
|
||||||
|
"Back" \
|
||||||
|
"Exit and close easy-docker"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_manage_stack_docker_menu() {
|
||||||
|
local stack_name="${1}"
|
||||||
|
local stack_dir="${2}"
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
status_text="$(printf "Manage stack docker\n\nStack: %s\nDirectory: %s\n\nChoose a docker-related action for this stack." "${stack_name}" "${stack_dir}")"
|
||||||
|
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 8 \
|
||||||
|
--header "Stack docker actions" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"Build custom image" \
|
||||||
|
"Generate docker compose from env" \
|
||||||
|
"Back" \
|
||||||
|
"Exit and close easy-docker"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_missing_custom_image_start_menu() {
|
||||||
|
local stack_name="${1}"
|
||||||
|
local stack_dir="${2}"
|
||||||
|
local image_ref="${3}"
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
status_text="$(printf "Custom image missing\n\nStack: %s\nDirectory: %s\nImage: %s\n\nBuild the custom image now before starting the stack?" "${stack_name}" "${stack_dir}" "${image_ref}")"
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 8 \
|
||||||
|
--header "Missing custom image" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"Build custom image now" \
|
||||||
|
"Back" \
|
||||||
|
"Exit and close easy-docker"
|
||||||
|
}
|
||||||
100
scripts/easy-docker/lib/ui/screens/production/setup.sh
Executable file
100
scripts/easy-docker/lib/ui/screens/production/setup.sh
Executable file
|
|
@ -0,0 +1,100 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
get_setup_display_label() {
|
||||||
|
local setup_type="${1}"
|
||||||
|
|
||||||
|
case "${setup_type}" in
|
||||||
|
development)
|
||||||
|
printf 'Development'
|
||||||
|
;;
|
||||||
|
production | *)
|
||||||
|
printf 'Production'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
show_setup_menu() {
|
||||||
|
local setup_type="${1}"
|
||||||
|
local setup_label=""
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
setup_label="$(get_setup_display_label "${setup_type}")"
|
||||||
|
status_text="$(printf "%s stack\n\nChoose whether to create a new stack or manage an existing one." "${setup_label}")"
|
||||||
|
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 8 \
|
||||||
|
--header "${setup_label} stack actions" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"Create new stack" \
|
||||||
|
"Manage existing stacks" \
|
||||||
|
"Back" \
|
||||||
|
"Exit and close easy-docker"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_production_setup_menu() {
|
||||||
|
show_setup_menu "production"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_development_setup_menu() {
|
||||||
|
show_setup_menu "development"
|
||||||
|
}
|
||||||
|
|
||||||
|
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_frappe_version_profile_menu() {
|
||||||
|
local stack_name="${1}"
|
||||||
|
local options_lines="${2:-}"
|
||||||
|
local selected_label="${3:-}"
|
||||||
|
local status_text=""
|
||||||
|
local option_line=""
|
||||||
|
local -a menu_options=()
|
||||||
|
local -a gum_args=()
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
status_text="$(printf "Create stack: %s\n\nSelect the Frappe branch profile from frappe.tsv.\nThis sets the stack default for branch suggestions." "${stack_name}")"
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
while IFS= read -r option_line; do
|
||||||
|
if [ -z "${option_line}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
menu_options+=("${option_line}")
|
||||||
|
done <<EOF
|
||||||
|
${options_lines}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ "${#menu_options[@]}" -eq 0 ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
gum_args=(
|
||||||
|
--height 10
|
||||||
|
--header "Frappe branch profile"
|
||||||
|
--cursor.foreground 63
|
||||||
|
--selected.foreground 45
|
||||||
|
)
|
||||||
|
if [ -n "${selected_label}" ]; then
|
||||||
|
gum_args+=(--selected "${selected_label}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
gum choose "${gum_args[@]}" "${menu_options[@]}" "Back"
|
||||||
|
}
|
||||||
206
scripts/easy-docker/lib/ui/screens/production/topology.sh
Executable file
206
scripts/easy-docker/lib/ui/screens/production/topology.sh
Executable file
|
|
@ -0,0 +1,206 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
show_stack_topology_menu() {
|
||||||
|
local stack_dir="${1}"
|
||||||
|
local stack_name=""
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
stack_name="${stack_dir##*/}"
|
||||||
|
status_text="$(printf "Stack created: %s\nDirectory: %s\n\nChoose the deployment topology.\n\n- Single-host: easiest setup on one server.\n- Split services: separate app and infra stacks for more control." "${stack_name}" "${stack_dir}")"
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 8 \
|
||||||
|
--header "Topology" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"Single-host (recommended)" \
|
||||||
|
"Split services" \
|
||||||
|
"Abort wizard to main menu"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_single_host_proxy_menu() {
|
||||||
|
local stack_dir="${1}"
|
||||||
|
local stack_name=""
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
stack_name="${stack_dir##*/}"
|
||||||
|
status_text="$(printf "Stack: %s\n\nSingle-host setup (step 1/3)\nChoose the proxy mode.\n\n- Traefik and nginx-proxy run inside compose.\n- Caddy is external and uses the no-proxy compose mode." "${stack_name}")"
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 11 \
|
||||||
|
--header "Single-host: proxy mode" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"Traefik (HTTP, built-in proxy)" \
|
||||||
|
"Traefik (HTTPS + Let's Encrypt)" \
|
||||||
|
"nginx-proxy (HTTP)" \
|
||||||
|
"nginx-proxy + acme-companion (HTTPS)" \
|
||||||
|
"Caddy (external reverse proxy)" \
|
||||||
|
"No reverse proxy (direct :8080)" \
|
||||||
|
"Back to topology selection"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_single_host_database_menu() {
|
||||||
|
local stack_dir="${1}"
|
||||||
|
local stack_name=""
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
stack_name="${stack_dir##*/}"
|
||||||
|
status_text="$(printf "Stack: %s\n\nSingle-host setup (step 2/3)\nChoose the database service." "${stack_name}")"
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 8 \
|
||||||
|
--header "Single-host: database" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"MariaDB (recommended)" \
|
||||||
|
"PostgreSQL" \
|
||||||
|
"Back to topology selection"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_single_host_redis_menu() {
|
||||||
|
local stack_dir="${1}"
|
||||||
|
local stack_name=""
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
stack_name="${stack_dir##*/}"
|
||||||
|
status_text="$(printf "Stack: %s\n\nSingle-host setup (step 3/3)\nChoose whether Redis services should be included." "${stack_name}")"
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 8 \
|
||||||
|
--header "Single-host: redis" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"Include Redis (recommended)" \
|
||||||
|
"Skip Redis (experienced users only)" \
|
||||||
|
"Back to topology selection"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_custom_modular_apps_multi_select() {
|
||||||
|
local stack_dir="${1}"
|
||||||
|
local options_lines="${2:-}"
|
||||||
|
local selected_labels_csv="${3:-}"
|
||||||
|
local stack_name=""
|
||||||
|
local status_text=""
|
||||||
|
local option_line=""
|
||||||
|
local selected_label=""
|
||||||
|
local -a menu_options=()
|
||||||
|
local -a selected_labels=()
|
||||||
|
local -a gum_args=()
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
stack_name="${stack_dir##*/}"
|
||||||
|
status_text="$(printf "Stack: %s\n\nApps\nUse Space to toggle apps from apps.tsv. Press Enter to continue to branch selection per app.\nUse Ctrl+C to go back." "${stack_name}")"
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
while IFS= read -r option_line; do
|
||||||
|
if [ -z "${option_line}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
menu_options+=("${option_line}")
|
||||||
|
done <<EOF
|
||||||
|
${options_lines}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ "${#menu_options[@]}" -eq 0 ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
gum_args=(
|
||||||
|
--no-limit
|
||||||
|
--height 14
|
||||||
|
--header "Apps"
|
||||||
|
--cursor.foreground 63
|
||||||
|
--selected.foreground 45
|
||||||
|
)
|
||||||
|
if [ -n "${selected_labels_csv}" ]; then
|
||||||
|
IFS=',' read -r -a selected_labels <<<"${selected_labels_csv}"
|
||||||
|
for selected_label in "${selected_labels[@]}"; do
|
||||||
|
trim_predefined_catalog_field selected_label "${selected_label}"
|
||||||
|
if [ -z "${selected_label}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
gum_args+=(--selected "${selected_label}")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
gum choose "${gum_args[@]}" "${menu_options[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_single_host_env_value() {
|
||||||
|
local stack_dir="${1}"
|
||||||
|
local variable_name="${2}"
|
||||||
|
local guidance_text="${3}"
|
||||||
|
local placeholder="${4:-}"
|
||||||
|
local render_context="${5:-1}"
|
||||||
|
local input_feedback="${6:-}"
|
||||||
|
local stack_name=""
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
if [ "${render_context}" = "1" ]; then
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
stack_name="${stack_dir##*/}"
|
||||||
|
guidance_text="${guidance_text//\\n/$'\n'}"
|
||||||
|
status_text="$(printf "Stack: %s\n\nConfigure %s\n\n%s" "${stack_name}" "${variable_name}" "${guidance_text}")"
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${input_feedback}" ]; then
|
||||||
|
gum style --foreground 214 "${input_feedback}" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
gum input \
|
||||||
|
--header "${variable_name}" \
|
||||||
|
--prompt "value> " \
|
||||||
|
--placeholder "${placeholder}"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_split_services_examples() {
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
status_text="$(printf "Split services examples\n\n- DB in a separate stack/project.\n- Proxy in a separate stack/project.\n- One or more app stacks referencing shared infra.")"
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 7 \
|
||||||
|
--header "Split services" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"Use this topology" \
|
||||||
|
"Back to topology selection"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_abort_wizard_prompt() {
|
||||||
|
local stack_dir="${1}"
|
||||||
|
local status_text=""
|
||||||
|
|
||||||
|
render_main_screen 1 >&2
|
||||||
|
|
||||||
|
status_text="$(printf "Abort wizard\n\nStack directory:\n%s\n\nRollback created files before returning to main menu?" "${stack_dir}")"
|
||||||
|
render_box_message "${status_text}" "0 2" >&2
|
||||||
|
|
||||||
|
gum choose \
|
||||||
|
--height 8 \
|
||||||
|
--header "Abort options" \
|
||||||
|
--cursor.foreground 63 \
|
||||||
|
--selected.foreground 45 \
|
||||||
|
"Rollback files and return to main menu" \
|
||||||
|
"Keep files and return to main menu" \
|
||||||
|
"Back to topology selection"
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue