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.
5.0 KiB
Pulse Script Library
The scripts/lib directory houses shared Bash modules used by Pulse installation and maintenance scripts. The goal is to keep production installers modular and testable while still supporting bundled single-file artifacts for curl-based distribution.
Modules & Namespaces
- Each module lives in
scripts/lib/<module>.sh. - Public functions use the
module::functionnamespace (common::log_info,proxmox::get_nodes, etc.). - Internal helpers can be marked
localor use amodule::__helperprefix. - Scripts should only rely on documented
module::APIs.
Using the Library
Development Mode
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/lib" && pwd)"
if [[ -f "${LIB_DIR}/common.sh" ]]; then
# shellcheck disable=SC1090
source "${LIB_DIR}/common.sh"
fi
common::init "$@"
common::log_info "Starting installer..."
# Acquire a temp directory (automatically cleaned on exit)
common::temp_dir TMP_DIR --prefix pulse-install-
common::log_info "Working in ${TMP_DIR}"
Bundled Mode
Bundled scripts have the library concatenated into the file during the release process. The source guard remains but is a no-op because all functions are already defined.
Environment Variables
| Variable | Description |
|---|---|
PULSE_DEBUG |
When set to 1, forces debug logging and enables additional diagnostics. |
PULSE_LOG_LEVEL |
Sets log level (debug, info, warn, error). Defaults to info. |
PULSE_NO_COLOR |
When 1, disables ANSI colors (used for non-TTY or forced monochrome). |
PULSE_SUDO_CMD |
Overrides the sudo command (e.g., /usr/bin/doas). |
PULSE_FORCE_INTERACTIVE |
Forces interactive behavior (treats script as running in a TTY). |
common.sh API Reference
common::init "$@"— Initializes logging, traps, and script metadata. Call once at script start.common::log_info "msg"— Logs informational messages to stdout.common::log_warn "msg"— Logs warnings to stderr.common::log_error "msg"— Logs errors to stderr.common::log_debug "msg"— Logs debug output (requires log leveldebug).common::fail "msg" [--code N]— Logs error and exits with optional status code.common::require_command cmd...— Verifies required commands exist; exits on missing dependencies.common::is_interactive— Returns success when stdin/stdout are TTYs or forced interactive.common::ensure_root [--allow-sudo] [--args "${COMMON__ORIGINAL_ARGS[@]}"]— Ensures root privileges, optionally re-executing via sudo.common::sudo_exec command...— Executes command with sudo, printing guidance if sudo is unavailable.common::run [--label desc] [--retries N] [--backoff "1 2"] -- cmd...— Executes a command with optional retries and dry-run support.common::run_capture [--label desc] -- cmd...— Runs a command and prints captured stdout; honors dry-run.common::temp_dir VAR [--prefix name]— Creates a temporary directory, assigns it toVAR, and registers cleanup (avoid command substitution so handlers persist).common::cleanup_push "description" "command"— Registers cleanup/rollback handler (LIFO order).common::cleanup_run— Executes registered cleanup handlers; automatically called on exit.common::set_dry_run true|false— Enables or disables dry-run mode for command wrappers.common::is_dry_run— Returns success when dry-run mode is active.
Bundling Workflow
- Define modules under
scripts/lib/. - Reference them from scripts using the development source guard.
- Update
scripts/bundle.manifestto list output artifacts and module order. - Run
scripts/bundle.shto generate bundled files underdist/. - Distribute bundled files (e.g., replace production installer).
Headers inserted during bundling (# === Begin: ... ===) mark module boundaries and aid debugging. The generated file includes provenance metadata with timestamp and manifest path.
-
systemd.sh— safe wrappers aroundsystemctl, unit creation helpers, and service lifecycle utilities. -
http.sh— download helpers, API call wrappers with retries, and GitHub release discovery. -
systemd::safe_systemctl args...— Runsystemctlwith timeout protection (container-friendly). -
systemd::service_exists name/systemd::detect_service_name …— Inspect available unit files. -
systemd::create_service path [mode]— Write a unit file from stdin (respects dry-run). -
systemd::enable_and_start name/systemd::restart name— Common service workflows. -
http::download --url URL --output FILE [...]— Robust curl/wget download helper with retries. -
http::api_call --url URL [...]— Token-authenticated API invocation; prints response body. -
http::get_github_latest_release owner/repo— Fetch latest GitHub release tag. -
http::parse_bool value— Normalize truthy/falsy strings.