Files
Pulse/scripts/lib
rcourtman 0fcfad3dc5 feat: add shared script library system and refactor docker-agent installer
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.
2025-10-20 15:13:38 +00:00
..

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::function namespace (common::log_info, proxmox::get_nodes, etc.).
  • Internal helpers can be marked local or use a module::__helper prefix.
  • 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 level debug).
  • 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 to VAR, 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

  1. Define modules under scripts/lib/.
  2. Reference them from scripts using the development source guard.
  3. Update scripts/bundle.manifest to list output artifacts and module order.
  4. Run scripts/bundle.sh to generate bundled files under dist/.
  5. 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 around systemctl, unit creation helpers, and service lifecycle utilities.

  • http.sh — download helpers, API call wrappers with retries, and GitHub release discovery.

  • systemd::safe_systemctl args... — Run systemctl with 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.