mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-02-18 00:17:39 +01:00
Implements a comprehensive script improvement infrastructure to reduce code duplication, improve maintainability, and enable easier testing of installer scripts. ## New Infrastructure ### Shared Library System (scripts/lib/) - common.sh: Core utilities (logging, sudo, dry-run, cleanup management) - systemd.sh: Service management helpers with container-safe systemctl - http.sh: HTTP/download helpers with curl/wget fallback and retry logic - README.md: Complete API documentation for all library functions ### Bundler System - scripts/bundle.sh: Concatenates library modules into single-file installers - scripts/bundle.manifest: Defines bundling configuration for distributables - Enables both modular development and curl|bash distribution ### Test Infrastructure - scripts/tests/run.sh: Test harness for running all smoke tests - scripts/tests/test-common-lib.sh: Common library validation (5 tests) - scripts/tests/test-docker-agent-v2.sh: Installer smoke tests (4 tests) - scripts/tests/integration/: Container-based integration tests (5 scenarios) - All tests passing ✓ ## Refactored Installer ### install-docker-agent-v2.sh - Reduced from 1098 to 563 lines (48% code reduction) - Uses shared libraries for all common operations - NEW: --dry-run flag support - Maintains 100% backward compatibility with original - Fully tested with smoke and integration tests ### Key Improvements - Sudo escalation: 100+ lines → 1 function call - Download logic: 51 lines → 1 function call - Service creation: 33 lines → 2 function calls - Logging: Standardized across all operations - Error handling: Improved with common library ## Documentation ### Rollout Strategy (docs/installer-v2-rollout.md) - 3-phase rollout plan (Alpha → Beta → GA) - Feature flag mechanism for gradual deployment - Testing checklist and success metrics - Rollback procedures and communication plan ### Developer Guides - docs/script-library-guide.md: Complete library usage guide - docs/CONTRIBUTING-SCRIPTS.md: Contribution workflow - docs/installer-v2-quickref.md: Quick reference for operators ## Metrics - Code reduction: 48% (1098 → 563 lines) - Reusable functions: 0 → 30+ - Test coverage: 0 → 8 test scenarios - Documentation: 0 → 5 comprehensive guides ## Testing All tests passing: - Smoke tests: 2/2 passed (8 test cases) - Integration tests: 5/5 scenarios passed - Bundled output: Syntax validated, dry-run tested ## Next Steps This lays the foundation for migrating other installers (install.sh, install-sensor-proxy.sh) to use the same pattern, reducing overall maintenance burden and improving code quality across the project.
113 lines
2.7 KiB
Bash
Executable File
113 lines
2.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Bundle modular installer scripts into distributable single files.
|
|
# Reads scripts/bundle.manifest for bundling instructions.
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
MANIFEST_PATH="${ROOT_DIR}/scripts/bundle.manifest"
|
|
HEADER_FMT='# === Begin: %s ==='
|
|
FOOTER_FMT='# === End: %s ==='
|
|
|
|
main() {
|
|
[[ -f "${MANIFEST_PATH}" ]] || {
|
|
echo "Missing manifest: ${MANIFEST_PATH}" >&2
|
|
exit 1
|
|
}
|
|
|
|
local current_output=""
|
|
local -a buffer=()
|
|
local found_output=false
|
|
|
|
while IFS= read -r line || [[ -n "${line}" ]]; do
|
|
# Skip blank lines and comments.
|
|
[[ -z "${line}" || "${line}" =~ ^[[:space:]]*# ]] && continue
|
|
|
|
if [[ "${line}" =~ ^output:[[:space:]]+(.+) ]]; then
|
|
flush_buffer "${current_output}" buffer
|
|
buffer=()
|
|
current_output="$(normalize_path "${BASH_REMATCH[1]}")"
|
|
mkdir -p "$(dirname "${current_output}")"
|
|
found_output=true
|
|
continue
|
|
fi
|
|
|
|
[[ -n "${current_output}" ]] || {
|
|
echo "Encountered source before output directive: ${line}" >&2
|
|
exit 1
|
|
}
|
|
|
|
local source_path
|
|
source_path="$(normalize_path "${line}")"
|
|
|
|
if [[ ! -f "${source_path}" ]]; then
|
|
echo "Source file missing: ${source_path}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
buffer+=("$(printf "${HEADER_FMT}" "$(relative_path "${source_path}")")")
|
|
buffer+=("$(<"${source_path}")")
|
|
buffer+=("$(printf "${FOOTER_FMT}" "$(relative_path "${source_path}")")"$'\n')
|
|
done < "${MANIFEST_PATH}"
|
|
|
|
flush_buffer "${current_output}" buffer
|
|
|
|
if [[ "${found_output}" == false ]]; then
|
|
echo "No output directive found in ${MANIFEST_PATH}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Bundling complete."
|
|
}
|
|
|
|
# flush_buffer <output_path> <buffer_array>
|
|
flush_buffer() {
|
|
local target="$1"
|
|
shift
|
|
[[ $# -gt 0 ]] || return 0
|
|
local -n buf_ref=$1
|
|
|
|
if [[ -z "${target}" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
{
|
|
echo "# Generated file. Do not edit."
|
|
printf '# Bundled on: %s\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
printf '# Manifest: %s\n\n' "$(relative_path "${MANIFEST_PATH}")"
|
|
printf '%s\n' "${buf_ref[@]}"
|
|
} > "${target}.tmp"
|
|
|
|
mv "${target}.tmp" "${target}"
|
|
if [[ "${target}" == *.sh ]]; then
|
|
chmod +x "${target}"
|
|
fi
|
|
echo "Wrote $(relative_path "${target}")"
|
|
}
|
|
|
|
# normalize_path <relative_or_absolute_path>
|
|
normalize_path() {
|
|
local path="$1"
|
|
if [[ "${path}" == /* ]]; then
|
|
printf '%s\n' "${path}"
|
|
else
|
|
printf '%s\n' "${ROOT_DIR}/${path}"
|
|
fi
|
|
}
|
|
|
|
# relative_path <absolute_path>
|
|
relative_path() {
|
|
local path="$1"
|
|
python3 - "$ROOT_DIR" "$path" <<'PY'
|
|
import os
|
|
import sys
|
|
|
|
root = os.path.abspath(sys.argv[1])
|
|
target = os.path.abspath(sys.argv[2])
|
|
print(os.path.relpath(target, root))
|
|
PY
|
|
}
|
|
|
|
main "$@"
|