diff --git a/.github/workflows/easy-docker.yml b/.github/workflows/easy-docker.yml new file mode 100644 index 00000000..270be5a7 --- /dev/null +++ b/.github/workflows/easy-docker.yml @@ -0,0 +1,38 @@ +name: Easy Docker Tests + +on: + push: + branches: + - main + paths: + - "easy-docker.sh" + - "scripts/easy-docker/**" + - "tests/easy-docker/**" + - ".github/workflows/easy-docker.yml" + pull_request: + branches: + - main + paths: + - "easy-docker.sh" + - "scripts/easy-docker/**" + - "tests/easy-docker/**" + - ".github/workflows/easy-docker.yml" + +jobs: + bats: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Install Bats + run: | + BATS_VERSION="v1.11.1" + curl -fsSL "https://github.com/bats-core/bats-core/archive/refs/tags/${BATS_VERSION}.tar.gz" -o bats-core.tar.gz + tar -xzf bats-core.tar.gz + sudo "./bats-core-${BATS_VERSION#v}/install.sh" /usr/local + + - name: Run easy-docker Bats tests + run: bats -p --recursive tests/easy-docker diff --git a/scripts/easy-docker/lib/app/options.sh b/scripts/easy-docker/lib/app/options.sh index 7b010ff7..54716723 100755 --- a/scripts/easy-docker/lib/app/options.sh +++ b/scripts/easy-docker/lib/app/options.sh @@ -12,13 +12,13 @@ USAGE parse_cli_options() { local result_var="${1}" - local disable_installation_fallback=0 + local disable_installation_fallback_value=0 shift while [ "$#" -gt 0 ]; do case "$1" in --no-installation-fallback) - disable_installation_fallback=1 + disable_installation_fallback_value=1 ;; -h | --help) print_usage @@ -33,6 +33,6 @@ parse_cli_options() { shift done - printf -v "${result_var}" "%s" "${disable_installation_fallback}" + printf -v "${result_var}" "%s" "${disable_installation_fallback_value}" return 0 } diff --git a/scripts/easy-docker/lib/app/wizard/common/core.sh b/scripts/easy-docker/lib/app/wizard/common/core.sh index c8de1606..54711793 100755 --- a/scripts/easy-docker/lib/app/wizard/common/core.sh +++ b/scripts/easy-docker/lib/app/wizard/common/core.sh @@ -2,6 +2,12 @@ get_easy_docker_repo_root() { local app_lib_dir="" + + if [ -n "${EASY_DOCKER_REPO_ROOT_OVERRIDE:-}" ]; then + printf '%s\n' "${EASY_DOCKER_REPO_ROOT_OVERRIDE}" + return 0 + fi + app_lib_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # core.sh lives in scripts/easy-docker/lib/app/wizard/common # so we need 6 levels up to reach repository root. @@ -55,14 +61,15 @@ create_stack_directory_with_metadata() { return 2 fi + if [ -z "${frappe_branch}" ]; then + return 1 + fi + if ! mkdir -p "${created_stack_dir}"; then return 1 fi created_at="$(get_current_utc_timestamp)" - if [ -z "${frappe_branch}" ]; then - return 1 - fi if ! cat >"${metadata_path}" <"${STUB_BIN}/${name}" + "${SYSTEM_CHMOD}" +x "${STUB_BIN}/${name}" +} + +write_passthrough_stub() { + local name="$1" + local target="$2" + + { + printf '#!%s\n' "${SYSTEM_BASH}" + printf 'exec "%s" "$@"\n' "${target}" + } >"${STUB_BIN}/${name}" + "${SYSTEM_CHMOD}" +x "${STUB_BIN}/${name}" +} + +@test "help prints usage and exits cleanly" { + run "${SYSTEM_ENV}" "PATH=${STUB_BIN}" "${SYSTEM_BASH}" "${MAIN_SCRIPT}" --help + + [ "${status}" -eq 0 ] + [[ "${output}" == *"Usage: bash easy-docker.sh [options]"* ]] + [[ "${output}" == *"--no-installation-fallback"* ]] +} + +@test "unknown option is rejected before startup" { + run "${SYSTEM_ENV}" "PATH=${STUB_BIN}" "${SYSTEM_BASH}" "${MAIN_SCRIPT}" --definitely-unknown + + [ "${status}" -eq 1 ] + [[ "${output}" == *"Unknown option: --definitely-unknown"* ]] + [[ "${output}" == *"Usage: bash easy-docker.sh [options]"* ]] +} + +@test "missing gum fails without interactive fallback when disabled" { + run "${SYSTEM_ENV}" "PATH=${STUB_BIN}" "${SYSTEM_BASH}" "${MAIN_SCRIPT}" --no-installation-fallback + + [ "${status}" -eq 1 ] + [[ "${output}" == *"gum is not installed. Trying package manager installation..."* ]] + [[ "${output}" == *"No supported package manager was found."* ]] + [[ "${output}" == *"Installation fallback is disabled."* ]] + [[ "${output}" == *"Install gum manually:"* ]] +} + +@test "missing docker stops after gum dependency succeeds" { + write_stub gum 'exit 0' + + run "${SYSTEM_ENV}" "PATH=${STUB_BIN}" "${SYSTEM_BASH}" "${MAIN_SCRIPT}" --no-installation-fallback + + [ "${status}" -eq 1 ] + [[ "${output}" == *"docker is not installed."* ]] + [[ "${output}" == *"Install Docker first:"* ]] +} diff --git a/tests/easy-docker/20_core_render.bats b/tests/easy-docker/20_core_render.bats new file mode 100755 index 00000000..c0df3908 --- /dev/null +++ b/tests/easy-docker/20_core_render.bats @@ -0,0 +1,179 @@ +#!/usr/bin/env bats + +load 'test_helper.bash' + +setup() { + easy_docker_test_begin + easy_docker_test_source_core_render_modules + unset ERPNEXT_VERSION + unset FRAPPE_BRANCH +} + +teardown() { + easy_docker_test_end +} + +@test "is_valid_stack_name accepts safe names" { + local name="" + + for name in alpha alpha-1 alpha_1 alpha.1; do + run is_valid_stack_name "${name}" + [ "${status}" -eq 0 ] + done +} + +@test "is_valid_stack_name rejects empty and unsafe names" { + local name="" + + for name in "" "bad name" "bad/name" "bad:name" "bad*name"; do + run is_valid_stack_name "${name}" + [ "${status}" -eq 1 ] + done +} + +@test "get_env_file_key_value parses exported and quoted values" { + local sandbox_root="" + local stack_dir="" + local env_file="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "env-parse")" + easy_docker_test_override_repo_root "${sandbox_root}" + stack_dir="$(easy_docker_test_stack_dir "env-parse")" + mkdir -p "${stack_dir}" + env_file="${stack_dir}/stack.env" + + cat >"${env_file}" <<'EOF' +# comment +export ERPNEXT_VERSION=15.9.0-test +STACK_NAME="My Stack" +IGNORED=value +EOF + + run get_env_file_key_value "${env_file}" ERPNEXT_VERSION + [ "${status}" -eq 0 ] + [ "${output}" = "15.9.0-test" ] + + run get_env_file_key_value "${env_file}" STACK_NAME + [ "${status}" -eq 0 ] + [ "${output}" = "My Stack" ] +} + +@test "get_stack_compose_project_name normalizes metadata stack names" { + local sandbox_root="" + local stack_dir="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "project-name")" + easy_docker_test_override_repo_root "${sandbox_root}" + stack_dir="$(easy_docker_test_stack_dir "project-name")" + mkdir -p "${stack_dir}" + + cat >"${stack_dir}/metadata.json" <<'EOF' +{ + "stack_name": "My Stack! 01" +} +EOF + + run get_stack_compose_project_name "${stack_dir}" + [ "${status}" -eq 0 ] + [ "${output}" = "easydocker-my-stack-01" ] +} + +@test "get_metadata_compose_files_lines returns compose file entries" { + local sandbox_root="" + local stack_dir="" + local expected="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "compose-lines")" + easy_docker_test_override_repo_root "${sandbox_root}" + stack_dir="$(easy_docker_test_stack_dir "compose-lines")" + mkdir -p "${stack_dir}" + + cat >"${stack_dir}/metadata.json" <<'EOF' +{ + "stack_name": "compose-lines", + "compose_files": [ + "compose.yaml", + "overrides/compose.proxy.yaml", + "overrides/compose.redis.yaml" + ] +} +EOF + + expected=$'compose.yaml\noverrides/compose.proxy.yaml\noverrides/compose.redis.yaml' + + run get_metadata_compose_files_lines "${stack_dir}/metadata.json" + [ "${status}" -eq 0 ] + [ "${output}" = "${expected}" ] +} + +@test "render_stack_compose_from_metadata writes generated compose with stubbed docker config" { + local sandbox_root="" + local stack_dir="" + local env_path="" + local generated_compose_path="" + local invocation_log="" + + easy_docker_test_install_docker_stub + + sandbox_root="$(easy_docker_test_create_repo_sandbox "render-smoke")" + easy_docker_test_override_repo_root "${sandbox_root}" + stack_dir="$(easy_docker_test_stack_dir "render-smoke")" + mkdir -p "${stack_dir}" + + cat >"${sandbox_root}/compose.yaml" <<'EOF' +services: + backend: + image: frappe/backend +EOF + + cat >"${sandbox_root}/overrides/compose.proxy.yaml" <<'EOF' +services: + frontend: + image: frappe/frontend +EOF + + cat >"${sandbox_root}/overrides/compose.redis.yaml" <<'EOF' +services: + redis-cache: + image: redis:7 +EOF + + cat >"${stack_dir}/metadata.json" <<'EOF' +{ + "stack_name": "My Stack! 01", + "compose_files": [ + "compose.yaml", + "overrides/compose.proxy.yaml", + "overrides/compose.redis.yaml" + ] +} +EOF + + env_path="${stack_dir}/My Stack! 01.env" + cat >"${env_path}" <<'EOF' +DB_HOST=localhost +EOF + + generated_compose_path="${stack_dir}/compose.generated.yaml" + invocation_log="${EASY_DOCKER_TEST_TMPDIR}/docker.invocations" + + export ERPNEXT_VERSION="15.9.0-test" + + run render_stack_compose_from_metadata "${stack_dir}" + [ "${status}" -eq 0 ] + [ -f "${generated_compose_path}" ] + [ ! -f "${generated_compose_path}.tmp" ] + + run cat "${generated_compose_path}" + [ "${status}" -eq 0 ] + [[ "${output}" == *"invocation=docker compose --project-name easydocker-my-stack-01 --env-file ${env_path}"* ]] + [[ "${output}" == *"-f ${sandbox_root}/compose.yaml"* ]] + [[ "${output}" == *"-f ${sandbox_root}/overrides/compose.proxy.yaml"* ]] + [[ "${output}" == *"-f ${sandbox_root}/overrides/compose.redis.yaml"* ]] + [[ "${output}" == *"erpnext=15.9.0-test"* ]] + + run cat "${invocation_log}" + [ "${status}" -eq 0 ] + [[ "${output}" == *"docker compose --project-name easydocker-my-stack-01 --env-file ${env_path} -f "* ]] + [[ "${output}" == *"config"* ]] +} diff --git a/tests/easy-docker/30_gum_ensure.bats b/tests/easy-docker/30_gum_ensure.bats new file mode 100755 index 00000000..cb91a6d5 --- /dev/null +++ b/tests/easy-docker/30_gum_ensure.bats @@ -0,0 +1,120 @@ +#!/usr/bin/env bats + +load 'test_helper.bash' + +setup() { + easy_docker_test_begin + easy_docker_test_source_gum_modules +} + +teardown() { + easy_docker_test_end +} + +@test "should_use_github_fallback rejects non-interactive terminals" { + local script_path="" + local repo_root="" + + repo_root="$(easy_docker_test_repo_root)" + script_path="${EASY_DOCKER_TEST_TMPDIR}/run-should-use-github-fallback" + easy_docker_test_write_executable "${script_path}" \ + "source \"${repo_root}/scripts/easy-docker/lib/install/gum/ensure.sh\"" \ + 'should_use_github_fallback' + + run "${script_path}" "${metadata_path}" + + result_stack_dir="" + if create_stack_directory_with_metadata result_stack_dir "duplicate-stack" "production" "version-15"; then + status_code=0 + else + status_code=$? + fi + [ "${status_code}" -eq 2 ] + [ "${result_stack_dir}" = "" ] + [ "$(cat "${metadata_path}")" = "original" ] +} + +@test "create_stack_directory_with_metadata does not leave a partial stack behind when frappe_branch is missing" { + local sandbox_root="" + local stack_dir="" + local result_stack_dir="" + local status_code="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "missing-frappe-branch")" + easy_docker_test_override_repo_root "${sandbox_root}" + stack_dir="$(easy_docker_test_stack_dir "missing-frappe-branch")" + + if create_stack_directory_with_metadata result_stack_dir "missing-frappe-branch" "production" ""; then + status_code=0 + else + status_code=$? + fi + + [ "${status_code}" -eq 1 ] + [ "${result_stack_dir}" = "" ] + [ ! -d "${stack_dir}" ] +} + +@test "rollback_stack_directory removes managed stack directories" { + local sandbox_root="" + local stack_dir="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "rollback-stack")" + easy_docker_test_override_repo_root "${sandbox_root}" + stack_dir="$(easy_docker_test_stack_dir "rollback-stack")" + mkdir -p "${stack_dir}/nested" + printf '%s\n' "payload" >"${stack_dir}/nested/file.txt" + + if ! rollback_stack_directory "${stack_dir}"; then + false + fi + [ ! -d "${stack_dir}" ] +} + +@test "rollback_stack_directory rejects paths outside the managed stacks tree" { + local sandbox_root="" + local outside_dir="" + local status_code="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "rollback-outside")" + easy_docker_test_override_repo_root "${sandbox_root}" + outside_dir="$(mktemp -d)" + + if rollback_stack_directory "${outside_dir}"; then + status_code=0 + else + status_code=$? + fi + [ "${status_code}" -eq 2 ] + [ -d "${outside_dir}" ] + + rm -rf "${outside_dir}" +} + +@test "get_stack_dir_by_name returns the matching stack directory and ignores junk entries" { + local sandbox_root="" + local stacks_dir="" + local matching_stack_dir="" + local junk_dir="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "stack-lookup")" + easy_docker_test_override_repo_root "${sandbox_root}" + stacks_dir="${sandbox_root}/.easy-docker/stacks" + + junk_dir="${stacks_dir}/not-a-stack" + matching_stack_dir="${stacks_dir}/target-stack" + + mkdir -p "${junk_dir}" "${matching_stack_dir}" + printf '%s\n' '{ "stack_name": "target-stack" }' >"${matching_stack_dir}/metadata.json" + + run get_stack_dir_by_name "target-stack" + [ "${status}" -eq 0 ] + [ "${output}" = "${matching_stack_dir}" ] +} + +@test "get_stack_dir_by_name fails when the stack is absent" { + local sandbox_root="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "stack-missing")" + easy_docker_test_override_repo_root "${sandbox_root}" + + run get_stack_dir_by_name "missing-stack" + [ "${status}" -eq 1 ] + [ -z "${output}" ] +} diff --git a/tests/easy-docker/60_compose_render_failures.bats b/tests/easy-docker/60_compose_render_failures.bats new file mode 100755 index 00000000..3845e041 --- /dev/null +++ b/tests/easy-docker/60_compose_render_failures.bats @@ -0,0 +1,207 @@ +#!/usr/bin/env bats + +load 'test_helper.bash' + +setup() { + easy_docker_test_begin + easy_docker_test_source_core_render_modules +} + +teardown() { + easy_docker_test_end +} + +write_docker_stub() { + local body="${1}" + + easy_docker_test_write_bin_command docker \ + 'set -euo pipefail' \ + "${body}" + easy_docker_test_prepend_bin_dir +} + +@test "render_stack_compose_from_metadata fails when metadata.json is missing" { + local sandbox_root="" + local stack_dir="" + local generated_compose_path="" + local docker_log="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "render-missing-metadata")" + easy_docker_test_override_repo_root "${sandbox_root}" + stack_dir="$(easy_docker_test_stack_dir "render-missing-metadata")" + mkdir -p "${stack_dir}" + + # shellcheck disable=SC2016 + write_docker_stub 'echo "docker should not have been called" >&2; exit 99' + + generated_compose_path="${stack_dir}/compose.generated.yaml" + docker_log="${EASY_DOCKER_TEST_TMPDIR}/docker.log" + + run render_stack_compose_from_metadata "${stack_dir}" + [ "${status}" -eq 1 ] + [ ! -f "${generated_compose_path}" ] + [ ! -f "${generated_compose_path}.tmp" ] + [ ! -f "${docker_log}" ] +} + +@test "render_stack_compose_from_metadata fails when the env file is missing" { + local sandbox_root="" + local stack_dir="" + local generated_compose_path="" + local docker_log="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "render-missing-env")" + easy_docker_test_override_repo_root "${sandbox_root}" + stack_dir="$(easy_docker_test_stack_dir "render-missing-env")" + mkdir -p "${stack_dir}" + + cat >"${stack_dir}/metadata.json" <<'EOF' +{ + "stack_name": "render-missing-env", + "compose_files": [ + "compose.yaml" + ] +} +EOF + + # shellcheck disable=SC2016 + write_docker_stub 'echo "docker should not have been called" >&2; exit 99' + + generated_compose_path="${stack_dir}/compose.generated.yaml" + docker_log="${EASY_DOCKER_TEST_TMPDIR}/docker.log" + + run render_stack_compose_from_metadata "${stack_dir}" + [ "${status}" -eq 1 ] + [ ! -f "${generated_compose_path}" ] + [ ! -f "${generated_compose_path}.tmp" ] + [ ! -f "${docker_log}" ] +} + +@test "render_stack_compose_from_metadata fails when compose_files are missing" { + local sandbox_root="" + local stack_dir="" + local generated_compose_path="" + local docker_log="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "render-missing-compose-files")" + easy_docker_test_override_repo_root "${sandbox_root}" + stack_dir="$(easy_docker_test_stack_dir "render-missing-compose-files")" + mkdir -p "${stack_dir}" + + cat >"${stack_dir}/metadata.json" <<'EOF' +{ + "stack_name": "render-missing-compose-files" +} +EOF + + cat >"${stack_dir}/render-missing-compose-files.env" <<'EOF' +ERPNEXT_VERSION=15.9.0-test +EOF + + # shellcheck disable=SC2016 + write_docker_stub 'echo "docker should not have been called" >&2; exit 99' + + generated_compose_path="${stack_dir}/compose.generated.yaml" + docker_log="${EASY_DOCKER_TEST_TMPDIR}/docker.log" + + run render_stack_compose_from_metadata "${stack_dir}" + [ "${status}" -eq 1 ] + [ ! -f "${generated_compose_path}" ] + [ ! -f "${generated_compose_path}.tmp" ] + [ ! -f "${docker_log}" ] +} + +@test "render_stack_compose_from_metadata fails when a referenced compose file is missing" { + local sandbox_root="" + local stack_dir="" + local generated_compose_path="" + local docker_log="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "render-missing-source")" + easy_docker_test_override_repo_root "${sandbox_root}" + stack_dir="$(easy_docker_test_stack_dir "render-missing-source")" + mkdir -p "${stack_dir}" + + cat >"${stack_dir}/metadata.json" <<'EOF' +{ + "stack_name": "render-missing-source", + "compose_files": [ + "compose.yaml", + "overrides/compose.proxy.yaml" + ] +} +EOF + + cat >"${stack_dir}/render-missing-source.env" <<'EOF' +ERPNEXT_VERSION=15.9.0-test +EOF + + cat >"${sandbox_root}/compose.yaml" <<'EOF' +services: + backend: + image: frappe/backend +EOF + + # shellcheck disable=SC2016 + write_docker_stub 'echo "docker should not have been called" >&2; exit 99' + + generated_compose_path="${stack_dir}/compose.generated.yaml" + docker_log="${EASY_DOCKER_TEST_TMPDIR}/docker.log" + + run render_stack_compose_from_metadata "${stack_dir}" + [ "${status}" -eq 1 ] + [ ! -f "${generated_compose_path}" ] + [ ! -f "${generated_compose_path}.tmp" ] + [ ! -f "${docker_log}" ] +} + +@test "render_stack_compose_from_metadata removes its temporary file after a docker config failure" { + local sandbox_root="" + local stack_dir="" + local generated_compose_path="" + local docker_log="" + + sandbox_root="$(easy_docker_test_create_repo_sandbox "render-docker-failure")" + easy_docker_test_override_repo_root "${sandbox_root}" + stack_dir="$(easy_docker_test_stack_dir "render-docker-failure")" + mkdir -p "${stack_dir}" + + cat >"${stack_dir}/metadata.json" <<'EOF' +{ + "stack_name": "render-docker-failure", + "compose_files": [ + "compose.yaml", + "overrides/compose.proxy.yaml" + ] +} +EOF + + cat >"${stack_dir}/render-docker-failure.env" <<'EOF' +ERPNEXT_VERSION=15.9.0-test +EOF + + cat >"${sandbox_root}/compose.yaml" <<'EOF' +services: + backend: + image: frappe/backend +EOF + mkdir -p "${sandbox_root}/overrides" + cat >"${sandbox_root}/overrides/compose.proxy.yaml" <<'EOF' +services: + frontend: + image: frappe/frontend +EOF + + # shellcheck disable=SC2016 + write_docker_stub 'printf "%s\n" "docker $*" >>"${EASY_DOCKER_TEST_TMPDIR}/docker.log"; if [ "${*: -1}" = "config" ]; then echo "simulated docker compose config failure" >&2; exit 23; fi; exit 0' + + generated_compose_path="${stack_dir}/compose.generated.yaml" + docker_log="${EASY_DOCKER_TEST_TMPDIR}/docker.log" + + run render_stack_compose_from_metadata "${stack_dir}" + [ "${status}" -eq 1 ] + [ ! -f "${generated_compose_path}" ] + [ ! -f "${generated_compose_path}.tmp" ] + [ -f "${docker_log}" ] + [ "$(cat "${docker_log}")" != "" ] +} diff --git a/tests/easy-docker/test_helper.bash b/tests/easy-docker/test_helper.bash new file mode 100755 index 00000000..4371f2aa --- /dev/null +++ b/tests/easy-docker/test_helper.bash @@ -0,0 +1,147 @@ +#!/usr/bin/env bash + +easy_docker_test_repo_root() { + local helper_dir="" + + helper_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + (cd "${helper_dir}/../.." && pwd) +} + +easy_docker_test_begin() { + EASY_DOCKER_TEST_TMPDIR="$(mktemp -d)" + export EASY_DOCKER_TEST_TMPDIR + unset EASY_DOCKER_REPO_ROOT_OVERRIDE +} + +easy_docker_test_end() { + if [ -n "${EASY_DOCKER_TEST_TMPDIR:-}" ] && [ -d "${EASY_DOCKER_TEST_TMPDIR}" ]; then + rm -rf "${EASY_DOCKER_TEST_TMPDIR}" + fi +} + +easy_docker_test_bin_dir() { + printf '%s/bin\n' "${EASY_DOCKER_TEST_TMPDIR}" +} + +easy_docker_test_write_executable() { + local target_path="${1}" + local system_bash="" + shift + + system_bash="$(command -v bash)" + mkdir -p "$(dirname "${target_path}")" + + { + printf '#!%s\n' "${system_bash}" + printf '%s\n' "$@" + } >"${target_path}" + chmod +x "${target_path}" +} + +easy_docker_test_write_bin_command() { + local command_name="${1}" + local target_path="" + shift + + target_path="$(easy_docker_test_bin_dir)/${command_name}" + easy_docker_test_write_executable "${target_path}" "$@" +} + +easy_docker_test_prepend_bin_dir() { + PATH="$(easy_docker_test_bin_dir):${PATH}" + export PATH +} + +easy_docker_test_source_common_modules() { + local repo_root="" + + repo_root="$(easy_docker_test_repo_root)" + + # shellcheck source=scripts/easy-docker/lib/core/commands.sh + source "${repo_root}/scripts/easy-docker/lib/core/commands.sh" + # shellcheck source=scripts/easy-docker/lib/core/messages.sh + source "${repo_root}/scripts/easy-docker/lib/core/messages.sh" +} + +easy_docker_test_source_core_render_modules() { + local repo_root="" + + repo_root="$(easy_docker_test_repo_root)" + + easy_docker_test_source_common_modules + + # shellcheck source=scripts/easy-docker/lib/app/wizard/common/core.sh + source "${repo_root}/scripts/easy-docker/lib/app/wizard/common/core.sh" + # shellcheck source=scripts/easy-docker/lib/app/wizard/common/compose/render.sh + source "${repo_root}/scripts/easy-docker/lib/app/wizard/common/compose/render.sh" +} + +easy_docker_test_source_docker_modules() { + local repo_root="" + + repo_root="$(easy_docker_test_repo_root)" + + easy_docker_test_source_common_modules + + # shellcheck source=scripts/easy-docker/lib/checks/docker.sh + source "${repo_root}/scripts/easy-docker/lib/checks/docker.sh" +} + +easy_docker_test_source_gum_modules() { + local repo_root="" + + repo_root="$(easy_docker_test_repo_root)" + + easy_docker_test_source_common_modules + + # shellcheck source=scripts/easy-docker/lib/install/gum/package_manager.sh + source "${repo_root}/scripts/easy-docker/lib/install/gum/package_manager.sh" + # shellcheck source=scripts/easy-docker/lib/install/gum/github_release.sh + source "${repo_root}/scripts/easy-docker/lib/install/gum/github_release.sh" + # shellcheck source=scripts/easy-docker/lib/install/gum/ensure.sh + source "${repo_root}/scripts/easy-docker/lib/install/gum/ensure.sh" +} + +easy_docker_test_create_repo_sandbox() { + local sandbox_name="${1}" + local sandbox_root="" + + sandbox_root="${EASY_DOCKER_TEST_TMPDIR}/repo-${sandbox_name}" + mkdir -p "${sandbox_root}/.easy-docker/stacks" "${sandbox_root}/overrides" + printf '%s\n' "${sandbox_root}" +} + +easy_docker_test_override_repo_root() { + EASY_DOCKER_REPO_ROOT_OVERRIDE="${1}" + export EASY_DOCKER_REPO_ROOT_OVERRIDE +} + +easy_docker_test_stack_dir() { + local stack_name="${1}" + + printf '%s/.easy-docker/stacks/%s\n' "${EASY_DOCKER_REPO_ROOT_OVERRIDE}" "${stack_name}" +} + +easy_docker_test_install_docker_stub() { + local log_file="" + + log_file="${EASY_DOCKER_TEST_TMPDIR}/docker.invocations" + + # shellcheck disable=SC2016 + easy_docker_test_write_bin_command docker \ + 'set -euo pipefail' \ + "log_file=\"${log_file}\"" \ + 'printf '"'"'%s\n'"'"' "docker $*" >>"${log_file}"' \ + 'if [ "${1:-}" != "compose" ]; then' \ + ' echo "unexpected docker subcommand: ${1:-}" >&2' \ + ' exit 64' \ + 'fi' \ + 'if [ "${!#}" != "config" ]; then' \ + ' echo "expected docker compose config invocation" >&2' \ + ' exit 65' \ + 'fi' \ + 'printf '"'"'invocation=%s\n'"'"' "docker $*"' \ + 'printf '"'"'erpnext=%s\n'"'"' "${ERPNEXT_VERSION:-}"' + + easy_docker_test_prepend_bin_dir +}