#!/bin/bash # lib/loader.sh - common header loading and module sourcing utilities. # # Centralises the "public header" that every entry script used to repeat, and # provides guarded, consistent module sourcing so all modules are referenced by # their project-root-relative path (e.g. "modules/web/nginx.sh", "lib/color.sh"). # Guard set of already-sourced modules (prevents double-sourcing side effects, # e.g. init_*.sh which execute at top level). # Resolve the project root from this file's own location. This makes every # sourced path absolute and cwd-independent, fixing "No such file or directory" # when the entry script is invoked from a directory other than the project root. # readlink -f canonicalises to an absolute path (symlink/cwd safe); the fallback # keeps it working on systems without readlink -f. loader.sh always lives at # /lib/loader.sh, so = dirname(dirname(this_file)). _ONEINSTACK_THIS="$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || echo "${BASH_SOURCE[0]}")" _ONEINSTACK_LIB_DIR="$(cd "$(dirname "${_ONEINSTACK_THIS}")" && pwd)" oneinstack_dir="$(cd "${_ONEINSTACK_LIB_DIR}/.." && pwd)" export oneinstack_dir _LOADED_MODULES="" # Source a module file exactly once. # $1 = module path relative to the project root (e.g. "modules/web/nginx.sh") source_module() { local mod="$1" local path="${oneinstack_dir:-.}/${mod}" case ";${_LOADED_MODULES};" in *";${mod};"*) return 0 ;; esac [ -f "${path}" ] || { echo "${CFAILURE}ERROR: module not found: ${mod}${CEND}" >&2; return 1; } . "${path}" || return 1 _LOADED_MODULES="${_LOADED_MODULES};${mod}" } # Load the common header exactly as the legacy top of install.sh did: # config/versions.txt, config/options.conf, color, check_os, check_dir, download, get_char load_common() { . "${oneinstack_dir}/config/versions.txt" . "${oneinstack_dir}/config/options.conf" . "${oneinstack_dir}/lib/color.sh" . "${oneinstack_dir}/lib/check_os.sh" . "${oneinstack_dir}/lib/check_dir.sh" . "${oneinstack_dir}/lib/download.sh" . "${oneinstack_dir}/lib/get_char.sh" } # Entry-point wrapper used by the workflow (see install.sh / workflow.sh). load_configs() { load_common } # Run a module's install function. Mirrors the legacy pattern # . modules/X.sh; Func 2>&1 | tee -a runtime/install.log # so every call site stays consistent (and is trivially de-duplicated). # # A visible "installing" banner is printed up front so the user always knows # which component is being built/installed at any moment. This matters because # the install runs many long compilations (nginx, php, mysql, ...) back-to-back; # without a per-component header the screen just shows a wall of `make` output # and it is impossible to tell what is currently running (or whether it hung). # ${func} 2>&1 | tee -a ... keeps the real build output on screen AND in the log, # so nothing is silent. run_install() { local module="$1" func="$2" echo "" echo "${CMSG}================================================================${CEND}" echo "${CMSG}>> Installing: ${func}${CEND}" echo "${CMSG} (module: ${module})${CEND}" echo "${CMSG}================================================================${CEND}" . "${module}" || { echo "${CFAILURE}ERROR: failed to load ${module}${CEND}" >&2; return 1; } ${func} 2>&1 | tee -a "${oneinstack_dir}/runtime/install.log" }