Add full banner headers to all scripts across the codebase
Every script now has the established header format: PURPOSE with ─────── separator, OPERATIONAL MODEL, DESIGN PRINCIPLES, OPERATIONAL SAFEGUARDS, CONFIGURATION, and RUNTIME MODES — structured with full ====== banner sections throughout. Orchestrators converted from compact ── inline format to full banners. Stale emby-fallback and dirty sync references removed from Plugin/user_script_plug-in.sh.
This commit is contained in:
@@ -16,6 +16,26 @@
|
||||
# stopped → leave, missing → skip. Container state is always respected.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Weekly Cadence
|
||||
# Weekly restarts target services that accumulate state on a slower schedule
|
||||
# than daily targets — less-critical containers that benefit from a periodic
|
||||
# clean start but do not need nightly intervention. Daily restarts handle
|
||||
# high-churn containers; weekly handles the longer-cycle ones.
|
||||
#
|
||||
# State Respect
|
||||
# Running containers are restarted. Stopped containers are left stopped — they
|
||||
# were intentionally halted and this script has no authority to override that
|
||||
# decision. This rule is consistent across the entire ecosystem.
|
||||
#
|
||||
# Dependency-Safe Ordering
|
||||
# Restarts follow the same dependency ordering used by docker_watchdog.sh.
|
||||
# Services that other containers depend on restart first. A dependent is never
|
||||
# restarted while its dependency is still coming up.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -28,6 +28,24 @@
|
||||
# 3. Trigger new search — finds a different release automatically
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Hands-Free Recovery
|
||||
# The script completes the full recovery cycle autonomously — blocklist, remove,
|
||||
# re-search. No operator decision required. A failed import at midnight resolves
|
||||
# itself before morning without any intervention.
|
||||
#
|
||||
# Age Gate Before Action
|
||||
# Items newer than ARR_IMPORT_RECOVERY_AGE are skipped. Arrs have their own
|
||||
# retry logic — acting immediately would race against it. The age gate gives
|
||||
# the arr time to self-resolve before this script escalates.
|
||||
#
|
||||
# Blocklist First
|
||||
# The bad release is blocklisted before removal and re-search. Without this,
|
||||
# the re-search can re-grab the same release that just failed.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -27,6 +27,25 @@
|
||||
# classification these would be deleted — breaking Lidarr and Emby display.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# API as Ground Truth
|
||||
# What Lidarr tracks is authoritative. Files not in the API response are
|
||||
# orphans — Lidarr has no record of them and they serve no purpose.
|
||||
# The script never infers ownership from directory structure alone.
|
||||
#
|
||||
# Age Gate Before Deletion
|
||||
# Files under LIDARR_ORPHAN_AGE are left alone regardless of tracked status.
|
||||
# Lidarr's import pipeline writes files before registering them — acting
|
||||
# immediately would delete files mid-import.
|
||||
#
|
||||
# Seven-Gate Safety Model
|
||||
# Multiple independent sanity checks must all pass before any file is touched.
|
||||
# No single check is trusted in isolation — a misconfigured path returning an
|
||||
# empty API response must not result in a wiped library.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -25,6 +25,25 @@
|
||||
# album/artist directories.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Additive Only
|
||||
# The script only adds missing files — it never overwrites existing artwork
|
||||
# or touches audio files. Re-running after a partial fetch completes exactly
|
||||
# where it left off with no side effects.
|
||||
#
|
||||
# Source Fallback Chain
|
||||
# Multiple sources are tried in order of quality preference. fanart.tv is
|
||||
# primary; fallbacks exist so partial coverage is better than none. A failed
|
||||
# primary never blocks the fallback from running.
|
||||
#
|
||||
# External API Courtesy
|
||||
# Rate limiting and parallel job caps prevent hammering fanart.tv and other
|
||||
# external APIs. Burst behaviour during large initial runs would risk
|
||||
# temporary blocks that break future scheduled fetches.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -43,6 +43,24 @@
|
||||
# Media profile also removes: *.iso *.lrc
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Pre-Scan Cleanup
|
||||
# Runs before arr cleanup scripts so orphan detection only encounters actual
|
||||
# media files. Scene debris and download artifacts would otherwise appear as
|
||||
# untracked files and inflate false-positive orphan counts.
|
||||
#
|
||||
# Profile Separation
|
||||
# Anime and media share different cleanup patterns because their content
|
||||
# differs. *.lrc (lyrics) and *.iso belong in media cleanup but not anime.
|
||||
# Separate profiles prevent cross-contamination of rules.
|
||||
#
|
||||
# Conservative by Default
|
||||
# Only explicitly listed patterns are removed. The script never guesses
|
||||
# at file intent — if a pattern is not in the list, the file is untouched.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -14,6 +14,24 @@
|
||||
# or unRAID environment resets after updates.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Daily Failsafe, Not Enforcer
|
||||
# Wrong ownership is a symptom of something else — a misconfigured container,
|
||||
# a manual copy, an rsync without --chown. This script corrects the symptom
|
||||
# daily rather than hunting the root cause. A persistent high correction count
|
||||
# is the signal to investigate the source.
|
||||
#
|
||||
# Runs First in the Window
|
||||
# Arr cleanup scripts depend on correct ownership to rename and delete files.
|
||||
# Permissions must be correct before cleanup runs — ordering is not optional.
|
||||
#
|
||||
# Separate Passes for Directories and Files
|
||||
# Directories need execute permission for traversal; files do not. Applying
|
||||
# the same mode to both is a common mistake this script avoids by design.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -33,6 +33,38 @@
|
||||
# The higher tick count wins.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Newest Timestamp Wins
|
||||
# No merge logic, no conflict resolution — the server with the most recent
|
||||
# LastPlayedDate is simply authoritative. Simple rules produce predictable
|
||||
# outcomes users can reason about.
|
||||
#
|
||||
# No Data Loss
|
||||
# The sync only pushes state forward — it never clears a Played flag or
|
||||
# resets a resume position to zero. A watch record on any server always
|
||||
# propagates outward, never disappears.
|
||||
#
|
||||
# Provider ID Matching
|
||||
# Items are matched by external IDs (IMDb, TVDB, MusicBrainz), not by
|
||||
# title or file path. This makes matching robust across library reorganisation,
|
||||
# renames, and multi-server path differences.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# PLAY_SYNC_ENABLED gate — exits cleanly when disabled; no partial runs
|
||||
# PARTNERSHIP gate — skips remote sync when partnership is inactive
|
||||
# Per-server reachability — unreachable servers are skipped individually;
|
||||
# one offline server does not abort the entire sync
|
||||
# User match required — a user missing from a server is skipped for that
|
||||
# server; no cross-account state pollution
|
||||
# acquire_lock — prevents concurrent runs from racing on the same
|
||||
# items during the 30-minute critical window
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION (host*.conf, aliased by detect_hosts)
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -60,27 +60,23 @@
|
||||
# Configure HOST*_LASTFM_API_KEY in host*.conf
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION (master.conf)
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# LIDARR_DISCOVERY_THRESHOLD — minimum score for Stage 1 seeds and Stage 2 adds (default: 70)
|
||||
# LIDARR_DISCOVERY_LOOKBACK_DAYS — Emby play history window in days (default: 7)
|
||||
# LIDARR_DISCOVERY_MIN_PLAYS — min plays to be evaluated in Stage 1 (default: 3)
|
||||
# LIDARR_DISCOVERY_MAX_ADDS — max seeds (Stage 1) and max adds (Stage 2) (default: 5)
|
||||
# LIDARR_DISCOVERY_USER_CAP_PCT — max % any one user contributes to play weight (default: 35)
|
||||
# LIDARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a Stage 2 reject (default: 30)
|
||||
# LIDARR_DISCOVERY_HISTORY — history/state file path
|
||||
# Playback as Intent Signal
|
||||
# What users actually listen to is a stronger signal than what they follow or
|
||||
# own. The scoring model weights demonstrated listening behaviour — recency,
|
||||
# play count, user breadth — over passive library membership.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
# Selective by Design
|
||||
# 0–5 adds per week is the target, not bulk imports. A high score threshold
|
||||
# combined with MAX_ADDS ensures only high-confidence recommendations are
|
||||
# acted on. Volume is not the goal — meaningful discovery is.
|
||||
#
|
||||
# playback_aware_lidarr_discovery.sh — normal run
|
||||
# playback_aware_lidarr_discovery.sh --dry-run — score and rank, no Lidarr changes
|
||||
# playback_aware_lidarr_discovery.sh --log — verbose output
|
||||
# playback_aware_lidarr_discovery.sh --status — show config and exit
|
||||
#
|
||||
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
|
||||
# Two-Stage Filtering
|
||||
# Stage 1 rejects weak seeds before they drive Stage 2. Low-quality seeds
|
||||
# produce low-quality similar-artist recommendations. Filtering at the seed
|
||||
# stage improves the entire output, not just the top of the list.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
@@ -110,6 +106,29 @@
|
||||
# runs. Safe to delete — next run starts fresh with no memory.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION (master.conf)
|
||||
# ==============================================================================================
|
||||
#
|
||||
# LIDARR_DISCOVERY_THRESHOLD — minimum score for Stage 1 seeds and Stage 2 adds (default: 70)
|
||||
# LIDARR_DISCOVERY_LOOKBACK_DAYS — Emby play history window in days (default: 7)
|
||||
# LIDARR_DISCOVERY_MIN_PLAYS — min plays to be evaluated in Stage 1 (default: 3)
|
||||
# LIDARR_DISCOVERY_MAX_ADDS — max seeds (Stage 1) and max adds (Stage 2) (default: 5)
|
||||
# LIDARR_DISCOVERY_USER_CAP_PCT — max % any one user contributes to play weight (default: 35)
|
||||
# LIDARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a Stage 2 reject (default: 30)
|
||||
# LIDARR_DISCOVERY_HISTORY — history/state file path
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# playback_aware_lidarr_discovery.sh — normal run
|
||||
# playback_aware_lidarr_discovery.sh --dry-run — score and rank, no Lidarr changes
|
||||
# playback_aware_lidarr_discovery.sh --log — verbose output
|
||||
# playback_aware_lidarr_discovery.sh --status — show config and exit
|
||||
#
|
||||
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
|
||||
@@ -59,29 +59,23 @@
|
||||
# Free key at: https://www.themoviedb.org/settings/api
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION (master.conf)
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# RADARR_DISCOVERY_THRESHOLD — minimum score to add a candidate (default: 52)
|
||||
# RADARR_DISCOVERY_LOOKBACK_DAYS — Emby watch history window in days (default: 30)
|
||||
# RADARR_DISCOVERY_MAX_SEEDS — max seed movies from Stage 1 (default: 5)
|
||||
# RADARR_DISCOVERY_MAX_ADDS — max movies to add per run (default: 5)
|
||||
# RADARR_DISCOVERY_MIN_VOTE_COUNT — min TMDB votes for a candidate (default: 100)
|
||||
# RADARR_DISCOVERY_MIN_RATING — min TMDB vote_average × 10 (default: 60 = 6.0/10)
|
||||
# RADARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a rejected movie (default: 60)
|
||||
# RADARR_DISCOVERY_SEED_LIBRARIES — Emby library names to draw seeds from (default: ("Movies"))
|
||||
# RADARR_DISCOVERY_HISTORY — history/state file path
|
||||
# Playback as Intent Signal
|
||||
# Recently watched movies are a stronger signal than what is in the library or
|
||||
# on watchlists. The scoring model weights demonstrated viewing behaviour —
|
||||
# recency, rating, vote confidence — over passive ownership.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
# Selective by Design
|
||||
# 0–5 adds per run is the target, not bulk imports. A score threshold combined
|
||||
# with MAX_ADDS ensures only high-confidence recommendations are acted on.
|
||||
# Volume is not the goal — meaningful discovery is.
|
||||
#
|
||||
# playback_aware_radarr_discovery.sh — normal run
|
||||
# playback_aware_radarr_discovery.sh --dry-run — score and rank, no Radarr changes
|
||||
# playback_aware_radarr_discovery.sh --log — verbose output
|
||||
# playback_aware_radarr_discovery.sh --status — show config and exit
|
||||
#
|
||||
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
|
||||
# Two-Stage Filtering
|
||||
# Stage 1 rejects weak seeds before they drive Stage 2. Low-quality or
|
||||
# low-confidence watched movies produce poor recommendations. Filtering at
|
||||
# the seed stage improves the entire output.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
@@ -111,6 +105,31 @@
|
||||
# runs. Safe to delete — next run starts fresh with no memory.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION (master.conf)
|
||||
# ==============================================================================================
|
||||
#
|
||||
# RADARR_DISCOVERY_THRESHOLD — minimum score to add a candidate (default: 52)
|
||||
# RADARR_DISCOVERY_LOOKBACK_DAYS — Emby watch history window in days (default: 30)
|
||||
# RADARR_DISCOVERY_MAX_SEEDS — max seed movies from Stage 1 (default: 5)
|
||||
# RADARR_DISCOVERY_MAX_ADDS — max movies to add per run (default: 5)
|
||||
# RADARR_DISCOVERY_MIN_VOTE_COUNT — min TMDB votes for a candidate (default: 100)
|
||||
# RADARR_DISCOVERY_MIN_RATING — min TMDB vote_average × 10 (default: 60 = 6.0/10)
|
||||
# RADARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a rejected movie (default: 60)
|
||||
# RADARR_DISCOVERY_SEED_LIBRARIES — Emby library names to draw seeds from (default: ("Movies"))
|
||||
# RADARR_DISCOVERY_HISTORY — history/state file path
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# playback_aware_radarr_discovery.sh — normal run
|
||||
# playback_aware_radarr_discovery.sh --dry-run — score and rank, no Radarr changes
|
||||
# playback_aware_radarr_discovery.sh --log — verbose output
|
||||
# playback_aware_radarr_discovery.sh --status — show config and exit
|
||||
#
|
||||
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
|
||||
@@ -66,31 +66,23 @@
|
||||
# Free key at: https://www.themoviedb.org/settings/api
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION (master.conf)
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# SONARR_DISCOVERY_THRESHOLD — minimum score to add a candidate (default: 52)
|
||||
# SONARR_DISCOVERY_LOOKBACK_DAYS — Emby watch history window in days (default: 14)
|
||||
# SONARR_DISCOVERY_MAX_SEEDS — max seed series from Stage 1 (default: 5)
|
||||
# SONARR_DISCOVERY_MAX_ADDS — max shows to add per run (default: 3)
|
||||
# SONARR_DISCOVERY_MIN_VOTE_COUNT — min TMDB votes for a candidate (default: 50)
|
||||
# SONARR_DISCOVERY_MIN_RATING — min TMDB vote_average × 10 (default: 65 = 6.5/10)
|
||||
# SONARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a rejected show (default: 60)
|
||||
# SONARR_DISCOVERY_USER_EPISODE_CAP — max episodes per user in seed scoring (default: 8)
|
||||
# SONARR_DISCOVERY_MONITOR_MODE — Sonarr monitor mode on add: "all" or "future" (default: "all")
|
||||
# SONARR_EMBY_LIBRARIES — Emby library names to draw seeds from
|
||||
# SONARR_DISCOVERY_HISTORY — history/state file path
|
||||
# Playback as Intent Signal
|
||||
# Recently watched episodes are a stronger signal than what is in the library.
|
||||
# User diversity across a series is weighted above a single user binge —
|
||||
# broad household interest is a better predictor of a good addition than
|
||||
# one person's session.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
# Selective by Design
|
||||
# 0–3 adds per run is the target. TV is a larger commitment than movies —
|
||||
# a lower MAX_ADDS cap reflects that. Volume is not the goal.
|
||||
#
|
||||
# playback_aware_sonarr_discovery.sh — normal run
|
||||
# playback_aware_sonarr_discovery.sh --dry-run — score and rank, no Sonarr changes
|
||||
# playback_aware_sonarr_discovery.sh --log — verbose output
|
||||
# playback_aware_sonarr_discovery.sh --status — show config and exit
|
||||
#
|
||||
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
|
||||
# Two-Stage Filtering
|
||||
# Stage 1 rejects weak seeds before they drive Stage 2. A poorly-watched
|
||||
# or niche series produces poor recommendations. Filtering at the seed
|
||||
# stage improves the entire output.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
@@ -125,6 +117,33 @@
|
||||
# runs. Safe to delete — next run starts fresh with no memory.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION (master.conf)
|
||||
# ==============================================================================================
|
||||
#
|
||||
# SONARR_DISCOVERY_THRESHOLD — minimum score to add a candidate (default: 52)
|
||||
# SONARR_DISCOVERY_LOOKBACK_DAYS — Emby watch history window in days (default: 14)
|
||||
# SONARR_DISCOVERY_MAX_SEEDS — max seed series from Stage 1 (default: 5)
|
||||
# SONARR_DISCOVERY_MAX_ADDS — max shows to add per run (default: 3)
|
||||
# SONARR_DISCOVERY_MIN_VOTE_COUNT — min TMDB votes for a candidate (default: 50)
|
||||
# SONARR_DISCOVERY_MIN_RATING — min TMDB vote_average × 10 (default: 65 = 6.5/10)
|
||||
# SONARR_DISCOVERY_REJECT_COOLDOWN — days before re-evaluating a rejected show (default: 60)
|
||||
# SONARR_DISCOVERY_USER_EPISODE_CAP — max episodes per user in seed scoring (default: 8)
|
||||
# SONARR_DISCOVERY_MONITOR_MODE — Sonarr monitor mode on add: "all" or "future" (default: "all")
|
||||
# SONARR_EMBY_LIBRARIES — Emby library names to draw seeds from
|
||||
# SONARR_DISCOVERY_HISTORY — history/state file path
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# playback_aware_sonarr_discovery.sh — normal run
|
||||
# playback_aware_sonarr_discovery.sh --dry-run — score and rank, no Sonarr changes
|
||||
# playback_aware_sonarr_discovery.sh --log — verbose output
|
||||
# playback_aware_sonarr_discovery.sh --status — show config and exit
|
||||
#
|
||||
# Recommended schedule: weekly (WEEKLY_MAINTENANCE_SCRIPTS in master.conf)
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
|
||||
@@ -31,6 +31,25 @@
|
||||
# Emby removes ghost entries immediately — no user-facing file-not-found errors.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# API as Ground Truth
|
||||
# What Radarr tracks is authoritative. Files not in the API response are
|
||||
# orphans — Radarr has no record of them and they serve no purpose.
|
||||
# The script never infers ownership from directory structure alone.
|
||||
#
|
||||
# Age Gate Before Deletion
|
||||
# Files under RADARR_ORPHAN_AGE are left alone regardless of tracked status.
|
||||
# Radarr's import pipeline writes files before registering them — acting
|
||||
# immediately would delete files mid-import.
|
||||
#
|
||||
# Emby Cleanup Is Part of the Job
|
||||
# Deleting a file without telling Emby leaves ghost entries that show as
|
||||
# broken items. Triggering the Emby clean is not optional — it completes
|
||||
# the deletion from the user's perspective.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -24,6 +24,25 @@
|
||||
# Per-deletion output is always visible — deletions are never silently swallowed.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Health Error Hygiene
|
||||
# status="deleted" entries can never be monitored or downloaded — they only
|
||||
# generate persistent health errors. Removing them is maintenance, not
|
||||
# data loss: the content never existed on disk for most of these entries.
|
||||
#
|
||||
# Conservative File Handling
|
||||
# Files are not deleted by default because most TMDb-removed entries are
|
||||
# announced-but-never-released films with no files. The --delete-files flag
|
||||
# is an explicit opt-in, not the default path.
|
||||
#
|
||||
# Exclusion List Prevents Re-add
|
||||
# Removed entries are added to Radarr's import exclusion list by default.
|
||||
# Without this, the same deleted entry can be re-added by lists or searches
|
||||
# and immediately generate the same health error again.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -31,6 +31,25 @@
|
||||
# Emby removes ghost entries immediately — no user-facing file-not-found errors.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# API as Ground Truth
|
||||
# What Sonarr tracks is authoritative. Files not in the API response are
|
||||
# orphans — Sonarr has no record of them and they serve no purpose.
|
||||
# The script never infers ownership from directory structure alone.
|
||||
#
|
||||
# Age Gate Before Deletion
|
||||
# Files under SONARR_ORPHAN_AGE are left alone regardless of tracked status.
|
||||
# Sonarr's import pipeline writes files before registering them — acting
|
||||
# immediately would delete files mid-import.
|
||||
#
|
||||
# Emby Cleanup Is Part of the Job
|
||||
# Deleting a file without telling Emby leaves ghost entries that show as
|
||||
# broken items. Triggering the Emby clean is not optional — it completes
|
||||
# the deletion from the user's perspective.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -23,6 +23,25 @@
|
||||
# Per-deletion output is always visible — deletions are never silently swallowed.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Health Error Hygiene
|
||||
# status="deleted" series can never be monitored or downloaded — they only
|
||||
# generate persistent health errors. Removing them is maintenance, not
|
||||
# data loss: most have no associated files.
|
||||
#
|
||||
# Conservative File Handling
|
||||
# Files are not deleted by default. A TVDB-removed series may still have
|
||||
# episodes on disk that the user wants to keep. The --delete-files flag
|
||||
# is an explicit opt-in, not the default path.
|
||||
#
|
||||
# Exclusion List Prevents Re-add
|
||||
# Removed entries are added to Sonarr's import exclusion list by default.
|
||||
# Without this, the same deleted series can be re-added by lists or searches
|
||||
# and immediately generate the same health error again.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -23,6 +23,67 @@
|
||||
# If WEBHOOK_PORT is 0: exits cleanly (disables the listener).
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# 1. Check WEBHOOK_PORT — exit cleanly if 0 (listener disabled)
|
||||
# 2. Check WEBHOOK_SECRET — generate and persist one if empty
|
||||
# 3. exec node webhook_listener.js — replaces this process; PID stays the same
|
||||
#
|
||||
# exec is intentional: array_started.sh tracks the PID of this script to check
|
||||
# whether the listener is running. exec preserves that PID across the hand-off
|
||||
# to node.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Node.js Over php -S
|
||||
# php -S on Unraid PHP 8.4 silently drops POST request bodies — webhooks arrive
|
||||
# empty and the handler has no payload to act on. Node.js handles POST bodies
|
||||
# correctly and has no equivalent silent-drop behaviour.
|
||||
#
|
||||
# Runs Outside nginx
|
||||
# The listener binds directly to WEBHOOK_PORT — no nginx proxy, no session auth.
|
||||
# The shared secret in the URL query string is the only gate. This keeps the
|
||||
# webhook path independent of the auth stack.
|
||||
#
|
||||
# exec Preserves PID
|
||||
# The script execs into node rather than forking it. array_started.sh stores the
|
||||
# PID of this script to check liveness — exec ensures that PID continues to
|
||||
# refer to the running node process after the hand-off.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# WEBHOOK_PORT=0 gate — exits cleanly before any setup if the listener is disabled
|
||||
# Secret auto-generate — WEBHOOK_SECRET generated via openssl rand if empty;
|
||||
# persisted to master.conf immediately so restarts reuse it
|
||||
# Shared secret gate — webhook URL must include ?key=<WEBHOOK_SECRET>;
|
||||
# requests without a valid key are rejected by the Node.js server
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# WEBHOOK_PORT — port the listener binds to; 0 = disabled
|
||||
# WEBHOOK_SECRET — shared secret for URL auth; auto-generated if empty
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# start_webhook_listener.sh
|
||||
# Started automatically at array start via ARRAY_START_SCRIPTS.
|
||||
# Exits immediately if WEBHOOK_PORT=0.
|
||||
#
|
||||
# To stop:
|
||||
# pkill -f webhook_listener.js
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
|
||||
@@ -15,7 +15,56 @@
|
||||
# begin searching — a search it will never win because we already have it.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# USAGE
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Closes the Propagation Window
|
||||
# arr_sync runs every 4 hours. Without this handler, a remote node that already
|
||||
# has the old version sees the upgrade tagged in arr_sync but the new file not
|
||||
# yet on disk, and initiates a redundant quality search — a search it will never
|
||||
# win because this host already has the file. Immediate push eliminates that window.
|
||||
#
|
||||
# Rescan as Ground Truth
|
||||
# Pushing the file is not enough — the remote arr must also be told the file
|
||||
# exists. Triggering a rescan makes the remote accept the pushed file as the
|
||||
# current version without starting a new search.
|
||||
#
|
||||
# Cache-First API Key Lookup
|
||||
# Remote arr API keys are read from conf if cached; otherwise fetched via SSH
|
||||
# from the remote's config.xml. This avoids storing secrets redundantly while
|
||||
# keeping API calls fast on hosts where the key is already known.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Arg validation — exits with usage message if arr_type or item_path missing
|
||||
# Path existence — exits if item_path is not a directory on disk
|
||||
# Tailscale resolution — skips a node if its Tailscale IP cannot be resolved
|
||||
# rsync exit check — rescan is only triggered if rsync succeeded; a failed
|
||||
# transfer does not cause the remote arr to scan a partial file
|
||||
# SSH fallback — if no cached API key, falls back to SSH to read config.xml
|
||||
# on the remote rather than failing the rescan step
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# host*.conf
|
||||
#
|
||||
# HOST* — host names used to discover remote nodes
|
||||
# HOST*_SONARR_API_KEY — cached API key for direct HTTP rescan (optional)
|
||||
# HOST*_RADARR_API_KEY — cached API key for direct HTTP rescan (optional)
|
||||
# HOST*_LIDARR_API_KEY — cached API key for direct HTTP rescan (optional)
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# SSH_KEY — SSH key path for rsync and SSH fallback
|
||||
# ARR_SYNC_CONNECT_TIMEOUT — SSH connect timeout in seconds
|
||||
# DOCKER_APPDATA_BASE — base path for reading arr config.xml on remote
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# upgrade_webhook_handler.sh <arr_type> <item_path>
|
||||
@@ -25,7 +74,7 @@
|
||||
# (series.path from Sonarr, movie.folderPath from Radarr,
|
||||
# artist.path from Lidarr)
|
||||
#
|
||||
# Called by webhook.php — not intended for direct invocation outside testing.
|
||||
# Called by webhook_listener.js — not intended for direct invocation outside testing.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
|
||||
@@ -18,6 +18,25 @@
|
||||
# configuration issue. Silent on clean runs.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# No Persistent State
|
||||
# Every report is generated fresh from the Emby API. No local database, no
|
||||
# incremental tracking. A missed run leaves no gap — the next run simply
|
||||
# covers its own window.
|
||||
#
|
||||
# Silent When Healthy
|
||||
# The report goes to Discord/notification as a summary. Transcode alerts are
|
||||
# the only proactive notification — high transcode ratios may indicate a
|
||||
# misconfigured client that needs attention before it becomes a performance issue.
|
||||
#
|
||||
# Section Independence
|
||||
# Each report section (sessions, library, activity, top content) guards its own
|
||||
# API calls. A failure in one section does not abort the others — the report
|
||||
# produces partial output rather than nothing.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -24,6 +24,36 @@
|
||||
# Safe to share with mesh members — contains no API keys, passwords, or SSH keys.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Read-Only, No Network Calls
|
||||
# All data comes from conf files — no SSH, no API calls, no pings. The output
|
||||
# is always instant and never fails due to a node being unreachable. This makes
|
||||
# it safe to run at any time without side effects.
|
||||
#
|
||||
# Scales Automatically
|
||||
# Iterates all defined HOST* vars rather than a hardcoded list. Adding a new
|
||||
# node to master.conf/host*.conf makes it appear in the output immediately.
|
||||
#
|
||||
# Safe to Share
|
||||
# Output contains only identity and coverage configuration — no API keys,
|
||||
# no passwords, no SSH keys. The report can be shared with other mesh members
|
||||
# without exposing secrets.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# No External Dependencies
|
||||
# Reads only from already-sourced conf files. No curl, no ssh, no docker —
|
||||
# nothing that can fail, hang, or require credentials.
|
||||
#
|
||||
# Empty Mesh Guard
|
||||
# collect_hosts() populates ALL_HOST_IDS — if no HOST* vars are defined the
|
||||
# output sections iterate over an empty array and exit cleanly.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -19,6 +19,25 @@
|
||||
# from master.conf if dynamix.cfg is not found.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Live Queries, No Persistent State
|
||||
# Every run queries smartctl directly — no cached attribute history, no trend
|
||||
# tracking. Each report is a snapshot of current drive health. This keeps the
|
||||
# script simple and the output always current.
|
||||
#
|
||||
# Threshold Parity With unRAID Dashboard
|
||||
# Temperature thresholds are read from dynamix.cfg — the same values unRAID
|
||||
# uses on its own dashboard. A consistent threshold means no conflicting alerts
|
||||
# between this script and the built-in unRAID warnings.
|
||||
#
|
||||
# Silent When Healthy
|
||||
# No output, no notification on a clean run. The absence of a report is the
|
||||
# confirmation that all drives passed. Noise from weekly healthy runs would
|
||||
# erode attention to the reports that matter.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -18,6 +18,24 @@
|
||||
# WebGUI slowdowns or timeouts under load.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Snapshot for Trend, Not Just Alert
|
||||
# Exhaustion events are rarely instant — they build over hours or days.
|
||||
# Logging every 6 hours builds a trend that weekly_health_digest.sh can
|
||||
# surface as a warning count, catching gradual pressure before it becomes
|
||||
# an outage.
|
||||
#
|
||||
# Bounded Log Size
|
||||
# Log entries are trimmed to TUNING_LOG_RETENTION days on every write.
|
||||
# The log never grows unbounded regardless of how long the server runs.
|
||||
#
|
||||
# Silent When Healthy
|
||||
# No output, no notification on a clean run. Threshold breach is the only
|
||||
# signal — routine snapshots below the threshold produce nothing.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -14,6 +14,24 @@
|
||||
# into a single digest. Reads only — writes nothing, changes nothing.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Aggregator, Not Generator
|
||||
# This script reads state files that other scripts maintain. It never produces
|
||||
# health data itself — it only presents what is already there. Each source
|
||||
# script remains responsible for its own state; this script is the envelope.
|
||||
#
|
||||
# Profile-Driven Notification
|
||||
# The cron schedule never changes. The DIGEST_PROFILE in master.conf controls
|
||||
# when notifications actually send — switching from daily noise to weekly
|
||||
# summaries is a one-line conf change, not a cron edit.
|
||||
#
|
||||
# Read-Only, No Side Effects
|
||||
# Writes nothing, changes nothing, triggers nothing. Safe to run at any time
|
||||
# for a health snapshot without affecting any running service or state file.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -15,6 +15,25 @@
|
||||
# comparison. In --dry-run mode, console only — nothing written to the log.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Informational, Not Interventional
|
||||
# This script reports — it does not act. system_watchdog.sh handles
|
||||
# threshold-based intervention. Keeping the roles separate means the report
|
||||
# is never suppressed by the same logic that triggers remediation.
|
||||
#
|
||||
# Week-Over-Week Comparison
|
||||
# Output is written to ZFS_REPORT_LOG so the same snapshot can be reviewed
|
||||
# across weeks. Memory pressure and ARC creep are slow — a single run is
|
||||
# rarely conclusive; the trend across weeks is what matters.
|
||||
#
|
||||
# Section Independence
|
||||
# Each of the five report sections guards its own data source. ZFS not
|
||||
# available, Docker not responding — those sections skip, the rest still run.
|
||||
# A partial report is more useful than no report.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -2,12 +2,18 @@
|
||||
# ==============================================================================================
|
||||
# ================================= Array Start Orchestrator ===================================
|
||||
# ==============================================================================================
|
||||
#
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Single entry point for array start — fired by the Varaverk plugin's
|
||||
# disks_mounted event hook (Plugin/unraid/event/disks_mounted/array_start_jobs).
|
||||
# Launches everything configured in ARRAY_START_SCRIPTS in master.conf.
|
||||
# This script exits after launching all scripts — the event hook sees it complete normally.
|
||||
#
|
||||
# ── WHAT IT LAUNCHES ──────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Configured in master.conf ARRAY_START_SCRIPTS — no changes to this script ever needed.
|
||||
# Current order (order matters — see below):
|
||||
#
|
||||
@@ -25,22 +31,30 @@
|
||||
# NOTE: watchdogs (docker, system, stability) are NOT launched here.
|
||||
# They run via watchdog_orchestrator.sh every 15 min (cron), not as daemons.
|
||||
#
|
||||
# ── WHY ORDER MATTERS ─────────────────────────────────────────────────────────────────────────
|
||||
# unraid_api_key_renew.sh — before anything else — self-heals API registry at boot
|
||||
# inotify_tuning.sh — must run BEFORE Code-Server and other containers start
|
||||
# containers that start with low inotify limits keep them ✅
|
||||
# docker_syslog_filter — must run BEFORE any container starts creating veth interfaces
|
||||
# ramdisk_setup.sh — must run BEFORE Emby starts transcoding
|
||||
# docker_network_connect — must run BEFORE watchdogs check container states
|
||||
# fallback.sh — last — needs everything else stable to make decisions
|
||||
#
|
||||
# ── ONE-SHOT vs CONTINUOUS DETECTION ─────────────────────────────────────────────────────────
|
||||
# Script is launched in background with bash script.sh &
|
||||
# After 1 second: if PID still alive → continuous (running in background)
|
||||
# if PID dead + exit 0 → one-shot completed successfully
|
||||
# if PID dead + exit N → failure
|
||||
#
|
||||
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Order Is Load-Bearing
|
||||
# inotify limits must be raised before containers start — containers that
|
||||
# start with low limits keep them. Ramdisk must exist before Emby starts.
|
||||
# Docker networks must be connected before watchdogs check container states.
|
||||
# fallback.sh goes last — it needs everything else stable to make decisions.
|
||||
#
|
||||
# Configuration Owns the List
|
||||
# ARRAY_START_SCRIPTS in master.conf is the only place scripts are added or
|
||||
# removed. This orchestrator never needs to be edited to change what runs —
|
||||
# one-shot vs continuous behaviour is auto-detected from the PID after launch.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Root check — all launched scripts require root
|
||||
# acquire_lock — prevents duplicate array start launches
|
||||
# detect_hosts() — MY_ID in notifications
|
||||
@@ -49,14 +63,30 @@
|
||||
# Full path on failure — shows exact path for debugging
|
||||
# notify on failures — alert if any script fails to launch
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# ARRAY_START_SCRIPTS — ordered list of scripts to launch at array start
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# array_started.sh — normal launch (called by Varaverk disks_mounted event hook)
|
||||
# array_started.sh --dry-run — show what would be launched without launching
|
||||
# array_started.sh --status — show configured scripts and their current state
|
||||
# array_started.sh --log — verbose output per script
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# array_started.sh
|
||||
# Normal launch — called by Varaverk disks_mounted event hook.
|
||||
#
|
||||
# array_started.sh --dry-run
|
||||
# Show what would be launched without launching.
|
||||
#
|
||||
# array_started.sh --status
|
||||
# Show configured scripts and their current state.
|
||||
#
|
||||
# array_started.sh --log
|
||||
# Verbose output per script.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -2,40 +2,74 @@
|
||||
# ==============================================================================================
|
||||
# ================================= Array Stop Orchestrator ====================================
|
||||
# ==============================================================================================
|
||||
# Planned shutdown orchestrator — stops all active processes cleanly before array maintenance.
|
||||
# Runs ARRAY_STOP_SCRIPTS from master.conf sequentially, each confirmed complete before next.
|
||||
#
|
||||
# ── EXECUTION ORDER ───────────────────────────────────────────────────────────────────────────
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Planned shutdown orchestrator — stops all active processes cleanly before
|
||||
# array maintenance. Runs ARRAY_STOP_SCRIPTS from master.conf sequentially,
|
||||
# each confirmed complete before the next starts.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# 1. user_scripts_stop.sh — kill background user scripts (prevents new operations)
|
||||
# 2. rsync_stop.sh --rsync-only — kill rsync; skip container recovery (handled in step 4)
|
||||
# 3. mover_stop.sh — stop mover after rsync (both write to same paths)
|
||||
# 4. docker_container_stop.sh — stop all containers one-by-one with verification
|
||||
#
|
||||
# ── WHY THIS ORDER ────────────────────────────────────────────────────────────────────────────
|
||||
# User scripts stopped first — they can spawn new rsync/docker operations mid-shutdown.
|
||||
# Rsync before mover — both write to the same paths; running together risks corruption.
|
||||
# Containers last — apps should stay available as long as possible during shutdown prep.
|
||||
# Unlike array_started.sh, all scripts run in the foreground. Each must complete
|
||||
# (pass or fail) before the next starts — a failed stop is noted but does not
|
||||
# prevent remaining steps from running.
|
||||
#
|
||||
# ── SEQUENTIAL vs BACKGROUND ─────────────────────────────────────────────────────────────────
|
||||
# Unlike array_started.sh, all scripts run in the foreground. Each must complete (pass or fail)
|
||||
# before the next starts — a failed stop is noted but does not prevent remaining steps.
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Order Is Load-Bearing
|
||||
# User scripts are stopped first — they can spawn new rsync or docker operations
|
||||
# mid-shutdown. Rsync stops before mover — both write to the same paths and
|
||||
# running together risks corruption. Containers stop last — apps should stay
|
||||
# available as long as possible during shutdown prep.
|
||||
#
|
||||
# Non-Fatal Steps
|
||||
# A failed stop step is logged and notified but does not abort the sequence.
|
||||
# Remaining scripts still run — a partial stop is better than a halted one.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
|
||||
# Root check — all stop scripts require root
|
||||
# acquire_lock — prevents concurrent array stop runs
|
||||
# detect_hosts() — MY_ID in notifications and logs
|
||||
# platform_require_cmd — notify validated before use
|
||||
# Non-fatal steps — a failed step is logged but remaining steps still run
|
||||
# notify on failures — alert if any stop script fails
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# ARRAY_STOP_SCRIPTS — ordered list of stop scripts to run
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# array_stopping.sh — run full stop sequence
|
||||
# array_stopping.sh --dry-run — preview without stopping anything
|
||||
# array_stopping.sh --status — show configured scripts and exit
|
||||
# array_stopping.sh --log — verbose output
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# array_stopping.sh
|
||||
# Run full stop sequence.
|
||||
#
|
||||
# array_stopping.sh --dry-run
|
||||
# Preview without stopping anything.
|
||||
#
|
||||
# array_stopping.sh --status
|
||||
# Show configured scripts and exit.
|
||||
#
|
||||
# array_stopping.sh --log
|
||||
# Verbose output.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -2,46 +2,81 @@
|
||||
# ==============================================================================================
|
||||
# ============================= Critical Sync Maintenance ======================================
|
||||
# ==============================================================================================
|
||||
# Orchestrator for time-sensitive syncs that run every 30 minutes.
|
||||
# Keeps the mirror current between the less frequent daily and weekly windows.
|
||||
#
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Orchestrator for time-sensitive syncs running every 30 minutes. Keeps the
|
||||
# mirror current between the less frequent daily and weekly windows.
|
||||
# Schedule: */30 * * * * (every 30 minutes via User Scripts plugin)
|
||||
#
|
||||
# ── EXECUTION ORDER ───────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# 1. Critical-Data rsync — auth stack, NPM config, certs (containers stopped both sides)
|
||||
# 2. CRITICAL_MAINTENANCE_SCRIPTS — play_state_sync + any other per-window scripts
|
||||
# 3. partnership --check — read both state files, detect changes, act accordingly
|
||||
#
|
||||
# ── WHY EVERY 30 MINUTES ──────────────────────────────────────────────────────────────────────
|
||||
# Auth stack changes (new users, proxy rules, certs) propagate within 30min ✅
|
||||
# Emby watch states stay in sync — mirror users see correct playback position ✅
|
||||
# Partnership state changes detected and acted on quickly ✅
|
||||
# Lock prevents: daily rsync doing Critical-Data mid-critical window ✅
|
||||
#
|
||||
# ── RSYNC GATE ────────────────────────────────────────────────────────────────────────────────
|
||||
# RSYNC GATE
|
||||
# RSYNC_ENABLED=false → skips all syncs (global gate)
|
||||
# CRITICAL_RSYNC_ENABLED=false → skips critical syncs only (per-orchestrator gate)
|
||||
# partnership --check always runs regardless — state check doesn't need rsync
|
||||
# partnership --check always runs regardless — state check doesn't need rsync.
|
||||
#
|
||||
# ── LOCK BEHAVIOUR ────────────────────────────────────────────────────────────────────────────
|
||||
# acquire_lock "strict" — if previous 30min run still going, skip this cycle entirely
|
||||
# Critical-Data taking > 30min is a problem worth knowing about
|
||||
# Strict mode prevents pile-up without waiting — log and move on ✅
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── SILENT WHEN HEALTHY ───────────────────────────────────────────────────────────────────────
|
||||
# Runs 48 times per day — clean runs must produce zero output ✅
|
||||
# Only failures and notable events produce visible output
|
||||
# Silent When Healthy
|
||||
# Runs 48 times per day — clean runs must produce zero output. Only failures
|
||||
# and notable events produce visible output.
|
||||
#
|
||||
# Auth-First Window
|
||||
# Auth stack changes (new users, proxy rules, certs) propagate within 30min.
|
||||
# Emby watch states stay in sync — mirror users see correct playback position.
|
||||
# Partnership state changes detected and acted on quickly.
|
||||
#
|
||||
# Strict Lock, Never Queue
|
||||
# acquire_lock "strict" — if the previous 30-min run is still going, skip
|
||||
# this cycle entirely. Critical-Data taking > 30min is a problem worth
|
||||
# knowing about. Strict mode prevents pile-up without waiting.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Root check — rsync and container stop/start require root
|
||||
# acquire_lock "strict" — no pile-up; skip cycle if prior run still active
|
||||
# detect_hosts() — MY_ID and REMOTE_ID for routing and logs
|
||||
# resolve_remote_ip — confirms remote reachability before any transfer
|
||||
# RSYNC_ENABLED gate — global kill switch respected before any rsync call
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# CRITICAL_RSYNC_ENABLED — enable/disable rsync section
|
||||
# CRITICAL_SYNC_SHARES — shares synced every 30min (HOST*_CRITICAL_SYNC_SHARES)
|
||||
# CRITICAL_MAINTENANCE_SCRIPTS — scripts run in critical window (optional)
|
||||
# PARTNERSHIP_ENABLED — enable/disable partnership check
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# critical_sync_maintenance.sh — normal run
|
||||
# critical_sync_maintenance.sh --dry-run — preview syncs without transferring
|
||||
# critical_sync_maintenance.sh --log — verbose per-share output
|
||||
# critical_sync_maintenance.sh --status — show configuration and exit
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# critical_sync_maintenance.sh
|
||||
# Normal run.
|
||||
#
|
||||
# critical_sync_maintenance.sh --dry-run
|
||||
# Preview syncs without transferring.
|
||||
#
|
||||
# critical_sync_maintenance.sh --log
|
||||
# Verbose per-share output.
|
||||
#
|
||||
# critical_sync_maintenance.sh --status
|
||||
# Show configuration and exit.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -2,10 +2,16 @@
|
||||
# ==============================================================================================
|
||||
# ============================= Daily Sync Maintenance =========================================
|
||||
# ==============================================================================================
|
||||
# Daily orchestrator — runs the full daily maintenance window in the correct order.
|
||||
# Schedule: 0 1 * * * (1am daily via User Scripts plugin)
|
||||
#
|
||||
# ── EXECUTION ORDER ───────────────────────────────────────────────────────────────────────────
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Daily maintenance window orchestrator — runs the full daily sequence in the
|
||||
# correct order. Schedule: 0 1 * * * (1am daily via User Scripts plugin)
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Pre-sync:
|
||||
# git_pull_execute.sh — pull latest scripts first, always
|
||||
#
|
||||
@@ -20,50 +26,77 @@
|
||||
#
|
||||
# Post-sync maintenance (DAILY_MAINTENANCE_SCRIPTS):
|
||||
# media_shares_permissions.sh — fix ownership before arr cleanup
|
||||
# media_cleaner.sh anime — remove junk from anime shares
|
||||
# media_cleaner.sh media — remove junk from media shares
|
||||
# lidarr_cleanup.sh — remove orphaned music files (local arr = truth)
|
||||
# sonarr_cleanup.sh — remove orphaned TV files (local arr = truth)
|
||||
# radarr_cleanup.sh — remove orphaned movie files (local arr = truth)
|
||||
# media_cleaner.sh anime/media — remove junk from media and anime shares
|
||||
# lidarr/sonarr/radarr_cleanup.sh — remove orphaned files (local arr = truth)
|
||||
# docker_daily_restart.sh — restart containers needing daily restart
|
||||
#
|
||||
# ── WHY ORDER MATTERS ─────────────────────────────────────────────────────────────────────────
|
||||
# git pull first — maintenance runs on latest code, not yesterday's
|
||||
# arr sync before rsync — all nodes track the same library before files are spread;
|
||||
# prevents remote arrs from searching for content already owned
|
||||
# rsync before cleanup — cleanup sees fully spread state, rsync has no --delete
|
||||
# permissions before arr cleanup — arrs need correct ownership to delete/rename
|
||||
# arr cleanup after permissions — clean ownership = successful orphan deletion
|
||||
# docker restart last — containers already processed by cleanup
|
||||
# DRIVE TEMP HANDLING (rsync.sh exit codes):
|
||||
# exit 1 = temp WARN → skip this share, continue to next
|
||||
# exit 2 = temp CRITICAL → abort ALL remaining syncs in this window
|
||||
#
|
||||
# ── HOST AWARENESS ────────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Order Is Load-Bearing
|
||||
# git pull first — maintenance runs on latest code, not yesterday's. Arr sync
|
||||
# before rsync — all nodes track the same library before files are spread,
|
||||
# preventing remote arrs from searching for content already owned. Rsync before
|
||||
# cleanup — cleanup sees fully spread state. Permissions before arr cleanup —
|
||||
# arrs need correct ownership to delete/rename. Docker restart last — containers
|
||||
# already processed by cleanup.
|
||||
#
|
||||
# Host-Aware Routing
|
||||
# Bidirectional — same script runs on both servers, correct direction automatic.
|
||||
# detect_hosts() aliases DAILY_SYNC_SHARES and PERSONAL_SHARES from HOST*_ vars.
|
||||
# No manual HOST1/HOST2 comparisons — MY_ID routes correctly on any server.
|
||||
# No manual HOST1/HOST2 comparisons needed.
|
||||
#
|
||||
# ── DRIVE TEMP HANDLING ───────────────────────────────────────────────────────────────────────
|
||||
# rsync.sh returns exit codes for temperature issues:
|
||||
# exit 1 = temp WARN — skip this share, continue to next
|
||||
# exit 2 = temp CRITICAL — abort ALL remaining syncs in this window
|
||||
# All other failures — skip share, continue to next
|
||||
# Silent When Healthy
|
||||
# Runs daily at 1am — clean runs produce minimal output. Each job logs silently
|
||||
# on success; failures surface to warn()/error(). Notify only on failure.
|
||||
#
|
||||
# ── SILENT WHEN HEALTHY ───────────────────────────────────────────────────────────────────────
|
||||
# Runs daily at 1am — clean run should produce minimal output.
|
||||
# Each job reports log() on success (silent), warn()/error() on failure (visible).
|
||||
# Summary always shown — gives window timing and share/job counts.
|
||||
# Notify only on failure — successful daily maintenance doesn't need notification.
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Root check — rsync and docker operations require root
|
||||
# acquire_lock — prevents concurrent daily windows
|
||||
# detect_hosts() — aliases correct per-host share lists
|
||||
# check_connectivity — verified before any rsync
|
||||
# check_remote_rootfs — aborts rsync if remote rootfs nearly full
|
||||
# Non-fatal jobs — a failed job logs and continues; remaining jobs still run
|
||||
# notify on failure — successful daily run produces no notification
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# host*.conf
|
||||
#
|
||||
# ── CONFIGURATION (master.conf + host*.conf) ───────────────────────────────────────────
|
||||
# HOST*_DAILY_SYNC_SHARES — shares pushed to mirror each day
|
||||
# HOST*_PERSONAL_SHARES — encrypted personal shares
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# DAILY_MAINTENANCE_SCRIPTS — maintenance jobs (permissions, cleanup, restart)
|
||||
# DAILY_RSYNC_ENABLED — enable/disable rsync section
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# daily_sync_maintenance.sh — normal run
|
||||
# daily_sync_maintenance.sh --dry-run — preview without syncing or changing
|
||||
# daily_sync_maintenance.sh --log — verbose per-share/per-job output
|
||||
# daily_sync_maintenance.sh --status — show configured shares and jobs
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# daily_sync_maintenance.sh
|
||||
# Normal run.
|
||||
#
|
||||
# daily_sync_maintenance.sh --dry-run
|
||||
# Preview without syncing or changing.
|
||||
#
|
||||
# daily_sync_maintenance.sh --log
|
||||
# Verbose per-share/per-job output.
|
||||
#
|
||||
# daily_sync_maintenance.sh --status
|
||||
# Show configured shares and jobs.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -2,56 +2,86 @@
|
||||
# ==============================================================================================
|
||||
# =========================== Intermediate Sync Maintenance ====================================
|
||||
# ==============================================================================================
|
||||
# 4-hour orchestrator — arr library reconciliation, artwork fetching, and optional rsync.
|
||||
# Schedule: 0 */4 * * * (every 4 hours)
|
||||
#
|
||||
# ── EXECUTION ORDER ───────────────────────────────────────────────────────────────────────────
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# 4-hour orchestrator — arr library reconciliation, artwork fetching, and
|
||||
# optional rsync. Schedule: 0 */4 * * * (every 4 hours)
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# 1. conf_sync.sh --pull-only — refresh partner conf cache in RAM (/tmp/.cache/vv/d/)
|
||||
# 2. arr_sync.sh — sync Lidarr/Sonarr/Radarr libraries across all nodes
|
||||
# 3. Rsync window (optional) — INTERMEDIATE_SYNC_SHARES, if any configured
|
||||
# 4. INTERMEDIATE_MAINTENANCE_SCRIPTS — artwork fetch and any future 4-hour jobs
|
||||
#
|
||||
# ── WHY A SEPARATE ORCHESTRATOR ───────────────────────────────────────────────────────────────
|
||||
# arr libraries need to converge more frequently than once a day. If a remote node adds
|
||||
# something at 2am, the next daily window is 23 hours away — remote arrs search for content
|
||||
# they don't know is already owned. Running every 4 hours closes that gap.
|
||||
# DRIVE TEMP HANDLING (rsync.sh exit codes):
|
||||
# exit 1 = temp WARN → skip this share, continue to next
|
||||
# exit 2 = temp CRITICAL → abort ALL remaining syncs in this window
|
||||
#
|
||||
# lidarr_missing_art.sh is idempotent — skips existing files, runs fast after initial fill.
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Closes the Library Gap
|
||||
# Arr libraries need to converge more frequently than once a day. If a remote
|
||||
# node adds something at 2am, the next daily window is 23 hours away — remote
|
||||
# arrs search for content they don't know is already owned. Running every 4
|
||||
# hours closes that gap.
|
||||
#
|
||||
# Idempotent Artwork
|
||||
# lidarr_missing_art.sh skips existing files and runs fast after initial fill.
|
||||
# Pairing it here means artwork catches up within 4 hours of a new album landing.
|
||||
#
|
||||
# Rsync is optional — INTERMEDIATE_SYNC_SHARES empty by default. Add shares to the config
|
||||
# if a subset of data needs mid-day propagation (e.g. watch state, metadata). Full media
|
||||
# share sync stays in the daily window.
|
||||
# Optional Rsync Layer
|
||||
# INTERMEDIATE_SYNC_SHARES is empty by default — the rsync step is skipped
|
||||
# entirely when nothing is configured. Add shares only if a subset of data
|
||||
# needs mid-day propagation. Full media share sync stays in the daily window.
|
||||
#
|
||||
# ── HOST AWARENESS ────────────────────────────────────────────────────────────────────────────
|
||||
# detect_hosts() sets MY_ID and aliases HOST*_INTERMEDIATE_SYNC_SHARES → INTERMEDIATE_SYNC_SHARES.
|
||||
# Each server can have a different set of mid-day shares — configure in host*.conf.
|
||||
# Each script in INTERMEDIATE_MAINTENANCE_SCRIPTS handles its own host logic.
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── DRIVE TEMP HANDLING ───────────────────────────────────────────────────────────────────────
|
||||
# Same as daily_sync_maintenance.sh:
|
||||
# exit 1 = temp WARN — skip this share, continue to next
|
||||
# exit 2 = temp CRITICAL — abort ALL remaining syncs in this window
|
||||
#
|
||||
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
|
||||
# Root check — scripts called here require root
|
||||
# acquire_lock — prevents concurrent intermediate windows
|
||||
# detect_hosts() — aliases correct per-host share lists
|
||||
# check_connectivity — verified before any rsync (skipped if no shares)
|
||||
# check_remote_rootfs — aborts rsync if remote rootfs nearly full
|
||||
# Non-fatal jobs — a failed arr_sync warns but does not block rsync or artwork fetch
|
||||
# Silent on success — runs 4x/day, only failures warrant notification
|
||||
#
|
||||
# ── CONFIGURATION ─────────────────────────────────────────────────────────────────────────────
|
||||
# host*.conf: HOST*_INTERMEDIATE_SYNC_SHARES — shares synced mid-day (empty = rsync skipped)
|
||||
# master.conf: INTERMEDIATE_RSYNC_ENABLED — enable/disable rsync section (default: true)
|
||||
# master.conf: INTERMEDIATE_MAINTENANCE_SCRIPTS — jobs run after rsync
|
||||
# master.conf: ARR_SYNC_ENABLED — toggle inside arr_sync.sh
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# host*.conf
|
||||
#
|
||||
# HOST*_INTERMEDIATE_SYNC_SHARES — shares synced mid-day (empty = rsync skipped)
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# INTERMEDIATE_RSYNC_ENABLED — enable/disable rsync section (default: true)
|
||||
# INTERMEDIATE_MAINTENANCE_SCRIPTS — jobs run after rsync
|
||||
# ARR_SYNC_ENABLED — toggle inside arr_sync.sh
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# intermediate_sync_maintenance.sh
|
||||
# Normal run.
|
||||
#
|
||||
# intermediate_sync_maintenance.sh --dry-run
|
||||
# Preview without changes.
|
||||
#
|
||||
# intermediate_sync_maintenance.sh --log
|
||||
# Verbose per-job output.
|
||||
#
|
||||
# intermediate_sync_maintenance.sh --status
|
||||
# Show configured shares/jobs and exit.
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# intermediate_sync_maintenance.sh — normal run
|
||||
# intermediate_sync_maintenance.sh --dry-run — preview without changes
|
||||
# intermediate_sync_maintenance.sh --log — verbose per-job output
|
||||
# intermediate_sync_maintenance.sh --status — show configured shares/jobs and exit
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -2,39 +2,86 @@
|
||||
# ==============================================================================================
|
||||
# ========================= Monthly Maintenance Orchestrator ===================================
|
||||
# ==============================================================================================
|
||||
# Uptime-triggered monthly maintenance — runs heavy tasks that need a stable, settled system.
|
||||
#
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Uptime-triggered monthly maintenance — runs heavy tasks that need a stable,
|
||||
# settled system. Schedule: 0 0 15 * * (15th of each month at midnight)
|
||||
# Fires only when BOTH gates pass:
|
||||
# 1. Server uptime >= MONTHLY_UPTIME_THRESHOLD_DAYS days
|
||||
# 2. Last run was >= MONTHLY_RUN_INTERVAL_DAYS days ago (or never run)
|
||||
#
|
||||
# ── WHY UPTIME-GATED ─────────────────────────────────────────────────────────────────────────
|
||||
# A scheduled reboot resets uptime. Monthly tasks (ZFS scrub, SMART long test) need a
|
||||
# stable, settled system — not one that just rebooted. Uptime-gating ensures maintenance
|
||||
# only runs after the server has been healthy for a full month, never immediately post-boot.
|
||||
# If uptime or interval gate is not met on the 15th, the run is skipped until next month.
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── HOW TO CALL ──────────────────────────────────────────────────────────────────────────────
|
||||
# Schedule: 0 0 15 * * (15th of each month at midnight)
|
||||
# Silent exit 0 when either gate is not met. Only outputs when maintenance actually fires.
|
||||
# Runs MONTHLY_MAINTENANCE_SCRIPTS sequentially when both gates pass.
|
||||
# Silent exit 0 when either gate is not met — only outputs when maintenance fires.
|
||||
# If uptime or interval gate is not met on the 15th, the run is skipped until
|
||||
# next month.
|
||||
#
|
||||
# ── STATE FILE ────────────────────────────────────────────────────────────────────────────────
|
||||
# MONTHLY_LAST_RUN_FILE — /boot/config — survives reboots, available before array starts.
|
||||
# Written after each run (pass or partial fail). Format: Unix timestamp.
|
||||
# A reboot does NOT reset the last-run state — the interval gate survives independently
|
||||
# of the uptime gate. Both must pass before maintenance fires again.
|
||||
# STATE FILE
|
||||
# MONTHLY_LAST_RUN_FILE lives on /boot/config — survives reboots, available
|
||||
# before the array starts. Written after each run (pass or partial fail).
|
||||
# Format: Unix timestamp. A reboot does NOT reset the last-run state — the
|
||||
# interval gate survives independently of the uptime gate.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Uptime Gate Ensures Stability
|
||||
# A scheduled reboot resets uptime. Monthly tasks (ZFS scrub, SMART long test)
|
||||
# need a stable, settled system — not one that just rebooted. Both gates must
|
||||
# pass before maintenance fires, ensuring the server has been healthy for a
|
||||
# full month.
|
||||
#
|
||||
# State Survives Reboots
|
||||
# MONTHLY_LAST_RUN_FILE is on /boot/config (USB flash), not on the array.
|
||||
# It is always available regardless of array state, so the interval gate is
|
||||
# never lost to a reboot.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Root check — ZFS scrub, SMART tests require root
|
||||
# acquire_lock — prevents concurrent monthly runs
|
||||
# detect_hosts() — MY_ID in notifications and logs
|
||||
# Uptime gate — MONTHLY_UPTIME_THRESHOLD_DAYS must be met
|
||||
# Interval gate — MONTHLY_RUN_INTERVAL_DAYS since last run must be met
|
||||
# --force flag — bypasses both gates for manual override
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# MONTHLY_MAINTENANCE_SCRIPTS — ordered list of scripts to run
|
||||
# MONTHLY_UPTIME_THRESHOLD_DAYS — minimum uptime in days before maintenance fires
|
||||
# MONTHLY_RUN_INTERVAL_DAYS — minimum days since last run before running again
|
||||
# MONTHLY_LAST_RUN_FILE — state file path — /boot/config, survives reboots
|
||||
# MONTHLY_LAST_RUN_FILE — state file path (/boot/config — survives reboots)
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# monthly_maintenance.sh
|
||||
# Normal run — uptime + interval gates enforced.
|
||||
#
|
||||
# monthly_maintenance.sh --dry-run
|
||||
# Preview gate state and scripts without running.
|
||||
#
|
||||
# monthly_maintenance.sh --status
|
||||
# Show gate state, last run, and configured scripts.
|
||||
#
|
||||
# monthly_maintenance.sh --force
|
||||
# Bypass uptime + interval gates (manual override).
|
||||
#
|
||||
# monthly_maintenance.sh --log
|
||||
# Verbose output.
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# monthly_maintenance.sh — normal run (uptime + interval gates enforced)
|
||||
# monthly_maintenance.sh --dry-run — preview gate state and scripts without running
|
||||
# monthly_maintenance.sh --status — show gate state, last run, and configured scripts
|
||||
# monthly_maintenance.sh --force — bypass uptime + interval gates (manual override)
|
||||
# monthly_maintenance.sh --log — verbose output
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -2,24 +2,71 @@
|
||||
# ==============================================================================================
|
||||
# ============================= Sunday Morning Coffee Report ===================================
|
||||
# ==============================================================================================
|
||||
# Weekly monitoring orchestrator — runs all Sunday monitor scripts in sequence.
|
||||
# Designed to be read over coffee Sunday morning while the system is fully caught up
|
||||
# from the 2:30am maintenance window.
|
||||
# Schedule: 0 7 * * 0 (7am Sunday — after weekly_sync_maintenance.sh finishes at ~3am)
|
||||
#
|
||||
# ── SCRIPTS (master.conf COFFEE_REPORT_SCRIPTS) ───────────────────────────────────────────────
|
||||
# Each script runs independently, logs to its own output, and notifies on findings.
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Weekly monitoring orchestrator — runs all Sunday monitor scripts in sequence.
|
||||
# Designed to be read over coffee while the system is fully caught up from the
|
||||
# 2:30am maintenance window.
|
||||
# Schedule: 0 7 * * 0 (7am Sunday — after weekly_sync_maintenance.sh finishes)
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Runs COFFEE_REPORT_SCRIPTS from master.conf in order. Each script runs
|
||||
# independently, produces its own output, and notifies on findings.
|
||||
# All scripts receive --dry-run and --log flags from this orchestrator when set.
|
||||
#
|
||||
# ── HOST AWARENESS ────────────────────────────────────────────────────────────────────────────
|
||||
# detect_hosts() sets MY_ID — used in banner and summary.
|
||||
# HOST1 primary: runs all scripts. HOST2: limited to host-aware scripts only.
|
||||
# HOST1 primary: runs all scripts.
|
||||
# HOST2: limited to host-aware scripts only — each script handles its own host logic.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Independent, Isolated Scripts
|
||||
# Each monitor script is fully self-contained — a failure in one does not
|
||||
# prevent the others from running. The orchestrator logs the failure and
|
||||
# continues to the next script.
|
||||
#
|
||||
# Runs After the Full Weekly Window
|
||||
# Scheduled 4+ hours after weekly_sync_maintenance.sh — the system is fully
|
||||
# synced and containers are back up before any monitoring reads run. Reports
|
||||
# reflect the settled post-maintenance state.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# detect_hosts() — MY_ID in banner and summary
|
||||
# Non-fatal steps — a failed script is logged; remaining scripts still run
|
||||
# Flag pass-through — --dry-run and --log forwarded to all child scripts
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# COFFEE_REPORT_SCRIPTS — ordered list of monitor scripts to run
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# sunday_morning_coffee_report.sh
|
||||
# Normal run.
|
||||
#
|
||||
# sunday_morning_coffee_report.sh --dry-run
|
||||
# Preview without any writes or notifications.
|
||||
#
|
||||
# sunday_morning_coffee_report.sh --log
|
||||
# Verbose per-script output.
|
||||
#
|
||||
# sunday_morning_coffee_report.sh --status
|
||||
# Show configured scripts and exit.
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# sunday_morning_coffee_report.sh — normal run
|
||||
# sunday_morning_coffee_report.sh --dry-run — preview without any writes or notifications
|
||||
# sunday_morning_coffee_report.sh --log — verbose per-script output
|
||||
# sunday_morning_coffee_report.sh --status — show configured scripts and exit
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -2,55 +2,82 @@
|
||||
# ==============================================================================================
|
||||
# ============================= Transcode Management ===========================================
|
||||
# ==============================================================================================
|
||||
# Orchestrator — runs transcode_cleanup.sh then transcode_manager.sh in the correct order.
|
||||
# Replace individual transcode_manager and transcode_cleanup cron entries with this.
|
||||
#
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Runs transcode_cleanup.sh then transcode_manager.sh in the correct order.
|
||||
# Replaces individual cron entries for each — this is the single cron entry.
|
||||
# Schedule: */7 * * * * (every 7 minutes via User Scripts plugin)
|
||||
#
|
||||
# ── WHY CLEANUP BEFORE MANAGER ────────────────────────────────────────────────────────────────
|
||||
# Cleanup runs first — removes stale segment files from ended sessions.
|
||||
# Manager runs after — threshold decisions based on real current usage post-cleanup.
|
||||
# Without this order, stale files inflate the ramdisk usage reading and trigger
|
||||
# unnecessary SSD flips even when active sessions would fit on the ramdisk.
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── WHAT EACH SCRIPT DOES ─────────────────────────────────────────────────────────────────────
|
||||
# transcode_cleanup.sh — removes aged segment files not open by any process
|
||||
# uses lsof for O(1) per-file active check (never per-file lsof)
|
||||
# also triggers flip-back to ramdisk after cleanup if recovered ✅
|
||||
# transcode_cleanup.sh
|
||||
# Removes aged segment files not open by any process. Uses lsof for O(1)
|
||||
# per-file active check. Triggers flip-back to ramdisk after cleanup if
|
||||
# the ramdisk usage has recovered.
|
||||
#
|
||||
# transcode_manager.sh — checks ramdisk usage against thresholds
|
||||
# flips symlink between ramdisk and SSD as needed
|
||||
# writes one entry to TRANSCODE_DAILY_LOG after each run
|
||||
# shows active Emby sessions with play method
|
||||
# transcode_manager.sh
|
||||
# Checks ramdisk usage against thresholds. Flips the symlink between ramdisk
|
||||
# and SSD as needed. Writes one entry to TRANSCODE_DAILY_LOG after each run.
|
||||
# Shows active Emby sessions with play method.
|
||||
#
|
||||
# ── DAILY LOG ─────────────────────────────────────────────────────────────────────────────────
|
||||
# transcode_manager.sh writes to TRANSCODE_DAILY_LOG after each run:
|
||||
# DAILY LOG (written by transcode_manager.sh, not this orchestrator):
|
||||
# Format: DATE|RAMDISK_USED_GB|FLIP_COUNT|RAM_SESSIONS|SSD_SESSIONS
|
||||
# This orchestrator does NOT write its own log — manager handles it ✅
|
||||
# Log trimmed to TRANSCODE_LOG_RETENTION days by manager on each write.
|
||||
# Trimmed to TRANSCODE_LOG_RETENTION days on each write.
|
||||
# Read by sunday_morning_coffee_report.sh and weekly_health_digest.sh.
|
||||
#
|
||||
# ── HOST AWARENESS ────────────────────────────────────────────────────────────────────────────
|
||||
# detect_hosts() sets MY_ID and aliases RAMDISK_PATH, TRANSCODE_SSD, RAMDISK_WARN_GB etc.
|
||||
# Each server manages its own transcode location independently.
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Cleanup First, Decide Later
|
||||
# Stale segment files from ended sessions inflate the ramdisk usage reading
|
||||
# and trigger unnecessary SSD flips even when active sessions would fit on
|
||||
# the ramdisk. Cleanup runs first so the manager measures real current usage.
|
||||
#
|
||||
# Delegated Logging
|
||||
# This orchestrator does not write its own log — transcode_manager.sh owns
|
||||
# the TRANSCODE_DAILY_LOG write. One log writer, one format, no duplication.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
|
||||
# Root check — mount and docker operations require root
|
||||
# acquire_lock — prevents concurrent 3-minute cycles overlapping
|
||||
# detect_hosts() — correct paths per host
|
||||
# acquire_lock — prevents concurrent 7-minute cycles overlapping
|
||||
# detect_hosts() — aliases RAMDISK_PATH, TRANSCODE_SSD, RAMDISK_WARN_GB per host
|
||||
# --dry-run — passed through to both child scripts
|
||||
# Exit code — worst exit code of both scripts returned
|
||||
# Exit code — worst exit code of both scripts returned to cron
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# TRANSCODE_DAILY_LOG — daily stats log (written by transcode_manager.sh)
|
||||
# TRANSCODE_LOG_RETENTION — days to keep (trimmed by manager)
|
||||
# TRANSCODE_STATE_FILE — current state (ramdisk_used, flip_count etc.)
|
||||
# All TRANSCODE_* threshold vars — see master.conf Transcode Manager section
|
||||
# TRANSCODE_STATE_FILE — current state (ramdisk_used, flip_count, etc.)
|
||||
# TRANSCODE_* threshold vars — see master.conf Transcode Manager section
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# transcode_management.sh
|
||||
# Normal run (every 7 minutes via cron).
|
||||
#
|
||||
# transcode_management.sh --dry-run
|
||||
# Preview without changes (passed to both child scripts).
|
||||
#
|
||||
# transcode_management.sh --status
|
||||
# Show configuration and current state.
|
||||
#
|
||||
# transcode_management.sh --log
|
||||
# Verbose output from both child scripts.
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# transcode_management.sh — normal run (every 7 minutes via cron)
|
||||
# transcode_management.sh --dry-run — preview without changes (passed to children)
|
||||
# transcode_management.sh --status — show configuration and current state
|
||||
# transcode_management.sh --log — verbose output from both child scripts
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -2,50 +2,86 @@
|
||||
# ==============================================================================================
|
||||
# ============================ Watchdog Orchestrator ===========================================
|
||||
# ==============================================================================================
|
||||
# Runs WATCHDOG_ORCHESTRATOR_SCRIPTS in order each cron cycle.
|
||||
#
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Runs WATCHDOG_ORCHESTRATOR_SCRIPTS in order each cron cycle. Replaces the
|
||||
# continuous loops previously embedded in individual watchdog scripts — those
|
||||
# are now single-pass; this orchestrator provides the cadence.
|
||||
# Schedule: */15 * * * * (every 15 minutes)
|
||||
#
|
||||
# ── EXECUTION ORDER ───────────────────────────────────────────────────────────────────────────
|
||||
# Driven by WATCHDOG_ORCHESTRATOR_SCRIPTS in master.conf — add, remove, or reorder there.
|
||||
# Default: resource_watchdog → docker_watchdog → system_watchdog → unraid_api_key_renew → stability_watchdog
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── WHY ORDER MATTERS ─────────────────────────────────────────────────────────────────────────
|
||||
# Resource Watchdog first — frees RAM and CPU before healing attempts container restarts.
|
||||
# Containers restarted into a resource-pressured system just fail again.
|
||||
# Docker Watchdog second — restarts with pressure already reduced, more likely to stabilise.
|
||||
# System Watchdog third — system component health after containers are healed.
|
||||
# API key renew fourth — self-heals unraid-api registry loss; check-first, silent when valid.
|
||||
# Stability Watchdog last — only reboots when all prior layers could not resolve the issue.
|
||||
# Order driven by WATCHDOG_ORCHESTRATOR_SCRIPTS in master.conf.
|
||||
# Default: resource_watchdog → docker_watchdog → system_watchdog →
|
||||
# unraid_api_key_renew → stability_watchdog
|
||||
#
|
||||
# ── ARRAY CHECK ───────────────────────────────────────────────────────────────────────────────
|
||||
# Exits immediately if /mnt/user is not mounted as shfs (array not started).
|
||||
# Watchdogs check Docker containers and storage — meaningless without the array.
|
||||
# Prevents false positives and unnecessary reboots when array is stopped or stopping.
|
||||
# ARRAY CHECK
|
||||
# Exits immediately if /mnt/user is not mounted as shfs. Watchdogs check
|
||||
# Docker containers and storage — meaningless without the array. Prevents
|
||||
# false positives and unnecessary reboots when array is stopped or stopping.
|
||||
#
|
||||
# ── STARTUP GRACE ─────────────────────────────────────────────────────────────────────────────
|
||||
# No action until system uptime >= WATCHDOG_STARTUP_GRACE seconds.
|
||||
# Prevents false positives from containers still starting at array launch.
|
||||
# Each sub-script enforces this independently — orchestrator exits early to avoid log noise.
|
||||
# STARTUP GRACE
|
||||
# No action until system uptime >= WATCHDOG_STARTUP_GRACE seconds. Prevents
|
||||
# false positives from containers still starting at array launch. Each
|
||||
# sub-script enforces this independently — orchestrator exits early to avoid
|
||||
# log noise.
|
||||
#
|
||||
# ── OVERLAP PROTECTION ────────────────────────────────────────────────────────────────────────
|
||||
# acquire_lock() — exits immediately if a prior cycle is still in progress.
|
||||
# Prevents pile-up when a cycle runs long (daemon restart attempt = 30s, etc.).
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── REPLACES ──────────────────────────────────────────────────────────────────────────────────
|
||||
# Continuous loops previously in system_watchdog.sh and docker_watchdog.sh.
|
||||
# Those scripts are now single-pass — this orchestrator provides the cadence.
|
||||
# Pressure Before Healing
|
||||
# Resource Watchdog runs first — it frees RAM and CPU before any container
|
||||
# restart is attempted. Containers restarted into a resource-pressured system
|
||||
# just fail again. Docker Watchdog restarts with pressure already reduced.
|
||||
# System Watchdog checks component health after containers are healed.
|
||||
# Stability Watchdog reboots only when all prior layers could not resolve the
|
||||
# issue. API key renew is check-first and silent when valid.
|
||||
#
|
||||
# Never Queue
|
||||
# acquire_lock exits immediately if a prior cycle is still running. Prevents
|
||||
# pile-up when a cycle runs long (daemon restart attempt = 30s, etc.).
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Root check — watchdog operations require root
|
||||
# acquire_lock — strict; no pile-up if prior cycle still active
|
||||
# detect_hosts() — MY_ID in logs and notifications
|
||||
# Array check — exits early if /mnt/user is not shfs-mounted
|
||||
# Startup grace — WATCHDOG_STARTUP_GRACE respected before any checks
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# WATCHDOG_ORCHESTRATOR_SCRIPTS — watchdogs to run, in order
|
||||
# WATCHDOG_STARTUP_GRACE — seconds after boot before checks activate
|
||||
# WATCHDOG_ORCHESTRATOR_HEARTBEAT — periodic heartbeat log toggle
|
||||
# WATCHDOG_ORCHESTRATOR_HEARTBEAT_HOURS — heartbeat interval in hours
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# watchdog_orchestrator.sh — normal run (called by cron every 15 minutes)
|
||||
# watchdog_orchestrator.sh --dry-run — pass --dry-run to all sub-scripts
|
||||
# watchdog_orchestrator.sh --status — show script paths and current grace state
|
||||
# watchdog_orchestrator.sh --log — verbose output from all sub-scripts
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# watchdog_orchestrator.sh
|
||||
# Normal run (called by cron every 15 minutes).
|
||||
#
|
||||
# watchdog_orchestrator.sh --dry-run
|
||||
# Pass --dry-run to all sub-scripts.
|
||||
#
|
||||
# watchdog_orchestrator.sh --status
|
||||
# Show script paths and current grace state.
|
||||
#
|
||||
# watchdog_orchestrator.sh --log
|
||||
# Verbose output from all sub-scripts.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -2,10 +2,16 @@
|
||||
# ==============================================================================================
|
||||
# ============================= Weekly Sync Maintenance ========================================
|
||||
# ==============================================================================================
|
||||
# Weekly maintenance window orchestrator — clean sync, container updates, weekly restarts.
|
||||
# Schedule: 30 2 * * 0 (Sunday 2:30am — before Sunday 7am coffee report)
|
||||
#
|
||||
# ── EXECUTION ORDER ───────────────────────────────────────────────────────────────────────────
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Weekly maintenance window orchestrator — clean Emby sync, container updates,
|
||||
# and weekly restarts. Schedule: 30 2 * * 0 (Sunday 2:30am)
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# 1. Stop local containers — Emby + auth stack stopped locally
|
||||
# 2. Stop remote containers — Emby + auth stack stopped remotely via SSH
|
||||
# 3. Pull updates locally — if WEEKLY_SYNC_UPDATES=true (zero extra downtime)
|
||||
@@ -15,44 +21,63 @@
|
||||
# 7. Start local containers — rebuild if new image pulled, docker start otherwise
|
||||
# 8. WEEKLY_MAINTENANCE_SCRIPTS — weekly restarts etc. (docker_weekly_restart.sh)
|
||||
#
|
||||
# ── WHY WEEKLY NOT NIGHTLY FOR EMBY ──────────────────────────────────────────────────────────
|
||||
# Emby builds a warm image cache on HOST2 throughout the week.
|
||||
# Syncing nightly resets cache — cold loads every morning for users.
|
||||
# Weekly sync: cache stays warm 6 days, resets Sunday night while users sleep.
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Weekly Cadence Preserves Cache
|
||||
# Emby builds a warm image cache on HOST2 throughout the week. Syncing nightly
|
||||
# resets that cache — cold loads every morning for users. Weekly sync keeps
|
||||
# the cache warm for 6 days, resets Sunday night while users sleep.
|
||||
# play_state_sync covers watch/resume state every 30 min between weekly syncs.
|
||||
#
|
||||
# ── CONTAINER UPDATES ─────────────────────────────────────────────────────────────────────────
|
||||
# Containers already stopped for sync — updates pull at zero extra downtime.
|
||||
# Both servers start on identical image versions after the window completes.
|
||||
# Toggle: WEEKLY_SYNC_UPDATES / WEEKLY_SYNC_UPDATES_REMOTE in master.conf
|
||||
# Zero-Downtime Updates
|
||||
# Containers are already stopped for the sync window — image pulls happen at
|
||||
# zero extra downtime. Both servers start on identical image versions after
|
||||
# the window completes.
|
||||
#
|
||||
# ── HOST AWARENESS ────────────────────────────────────────────────────────────────────────────
|
||||
# detect_hosts() sets MY_ID — used in banner, summary, and notifications.
|
||||
# WEEKLY_SYNC_SHARES and WEEKLY_MAINTENANCE_SCRIPTS configured in master.conf.
|
||||
# Same script runs correctly on both servers.
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
|
||||
# Root check — stop/start containers, rsync require root
|
||||
# Root check — stop/start containers and rsync require root
|
||||
# acquire_lock — prevents concurrent weekly windows
|
||||
# detect_hosts() — MY_ID in banner, summary, and notifications
|
||||
# check_connectivity — verifies remote before any remote operations
|
||||
# check_remote_rootfs — aborts if remote rootfs nearly full
|
||||
# check_remote_rootfs — aborts rsync if remote rootfs nearly full
|
||||
# DOCKER_TIMEOUT — all docker calls protected
|
||||
# SSH_TIMEOUT — all SSH calls protected
|
||||
# platform_require_cmd — notify validated before use
|
||||
# Silent on success — runs weekly, only failures warrant notification
|
||||
# Silent on success — runs weekly; only failures warrant notification
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# WEEKLY_SYNC_SHARES — shares synced during window
|
||||
# WEEKLY_MAINTENANCE_SCRIPTS — scripts run after sync
|
||||
# WEEKLY_SYNC_UPDATES — toggle local container updates
|
||||
# WEEKLY_SYNC_UPDATES_REMOTE — toggle remote container updates
|
||||
# WEEKLY_RSYNC_ENABLED — enable/disable rsync section
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# weekly_sync_maintenance.sh — normal run
|
||||
# weekly_sync_maintenance.sh --dry-run — preview without stopping containers or syncing
|
||||
# weekly_sync_maintenance.sh --log — verbose per-share/per-job output
|
||||
# weekly_sync_maintenance.sh --status — show configuration and exit
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# weekly_sync_maintenance.sh
|
||||
# Normal run.
|
||||
#
|
||||
# weekly_sync_maintenance.sh --dry-run
|
||||
# Preview without stopping containers or syncing.
|
||||
#
|
||||
# weekly_sync_maintenance.sh --log
|
||||
# Verbose per-share/per-job output.
|
||||
#
|
||||
# weekly_sync_maintenance.sh --status
|
||||
# Show configuration and exit.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
+8
-3
@@ -26,15 +26,20 @@
|
||||
# rebuild cron when the array mounts. Scripts stay on appdata (git clone).
|
||||
#
|
||||
# ==============================================================================================
|
||||
# USAGE
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
# ./build.sh # version = today's date (YYYY.MM.DD)
|
||||
# ./build.sh 2026.09.01 # explicit version
|
||||
#
|
||||
# ./build.sh
|
||||
# Build with today's date as version (YYYY.MM.DD).
|
||||
#
|
||||
# ./build.sh 2026.09.01
|
||||
# Build with an explicit version string.
|
||||
#
|
||||
# After building: commit Plugin/dist/<txz> + the updated .plg, then attach the
|
||||
# .txz to a GitHub release tagged <version> so the .plg URL resolves for
|
||||
# downloaders. (The .plg also works offline if the .txz is already cached on
|
||||
# flash with a matching SHA256.)
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -3,15 +3,25 @@
|
||||
# ====================== Partnership — Unraid Container Adapter ================================
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Sourced by partnership_onboard.sh and partnership_offboard.sh via:
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Platform adapter providing container deploy/cleanup functions for the Unraid
|
||||
# partnership system. Sourced (not executed) by partnership_onboard.sh and
|
||||
# partnership_offboard.sh via:
|
||||
# source "$SCRIPTS_ROOT/Plugin/$PLATFORM/Partnership/containers.sh"
|
||||
#
|
||||
# Provides container deploy/cleanup functions specific to the Unraid platform:
|
||||
# - Docker container deployment from Unraid CA XML templates
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Functions use variables from the calling script's scope (sourced, not exec'd):
|
||||
# Functions operate on variables from the calling script's scope:
|
||||
# MIRROR, MIRROR_IP, MIRROR_SSH_KEY, SSH_TIMEOUT, DRY_RUN, SCRIPTS_ROOT
|
||||
#
|
||||
# Capabilities:
|
||||
# - Docker container deployment from Unraid CA XML templates
|
||||
# - Remote GPU type detection (cached per session — one SSH call per onboard)
|
||||
# - Deployed stack tracking via _STACK_DEPLOYED / _STACK_FAILED counters
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
TEMPLATES_DIR="/boot/config/plugins/dockerMan/templates-user"
|
||||
|
||||
@@ -14,6 +14,29 @@
|
||||
# page always reflects the live key value.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Self-Healing at Boot
|
||||
# The unraid-api registry is ephemeral — OS updates and service restarts clear
|
||||
# it without warning. Running at every array start means the key is always
|
||||
# present after boot without any manual intervention.
|
||||
#
|
||||
# Conf Stays Current
|
||||
# HOST*_UNRAID_API_KEY in the local host conf is updated after every renewal.
|
||||
# The partnership page reads the conf — it always reflects the live key value
|
||||
# without a separate sync step.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# acquire_lock — prevents concurrent renewal attempts at boot
|
||||
# detect_hosts() — sets MY_ID to derive the correct conf var name
|
||||
# Conf file check — aborts before any writes if the host conf is missing
|
||||
# dry-run mode — shows what would happen without touching anything
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -15,7 +15,29 @@
|
||||
# Accepts --host=HOST2 to refresh a single host (used by the UI refresh button).
|
||||
#
|
||||
# ==============================================================================================
|
||||
# USAGE
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Cache-First, Never Live on Page Load
|
||||
# Remote arr APIs have non-trivial latency — calling them on every page view
|
||||
# would make the arrs page slow and fragile. Writing to /tmp/vv_cache/ on a
|
||||
# 2-hour schedule decouples page load time from network availability.
|
||||
#
|
||||
# Single-Host Refresh for UI
|
||||
# The UI refresh button passes --host=HOSTN to update one host without waiting
|
||||
# for the full 2-hour cycle. Keeps the cache fresh when a user requests it.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# acquire_lock — prevents concurrent cache write runs
|
||||
# detect_hosts() — MY_ID and partner host list
|
||||
# SSH reachability — skips a host cleanly if it cannot be reached
|
||||
# /tmp/vv_cache/ — auto-created if missing; cleared on reboot (intentional)
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# remote_arr_cache_writer.sh — refresh all remote hosts
|
||||
|
||||
@@ -24,6 +24,29 @@
|
||||
# varaverk.cron rebuilt via PHP (job paths regenerated for new SCRIPTS_DIR)
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Atomic Path Transition
|
||||
# All four files (varaverk.cfg, master.conf, host*.conf, varaverk.cron) are
|
||||
# updated in a single pass. A partial migration would leave cron entries
|
||||
# pointing at the wrong SCRIPTS_DIR — all or nothing.
|
||||
#
|
||||
# PHP Rebuilds Cron
|
||||
# Job paths in varaverk.cron are derived from SCRIPTS_DIR. Rather than
|
||||
# text-substituting the cron file, the script regenerates it via PHP using
|
||||
# the new SCRIPTS_DIR as the source of truth.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# acquire_lock — prevents concurrent migration attempts
|
||||
# dry-run mode — shows all changes without touching any file
|
||||
# --status mode — reports current mode without requiring a target
|
||||
# --to= required — refuses to run without an explicit target mode
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -2,19 +2,28 @@
|
||||
# ==============================================================================================
|
||||
# ================================= Unraid Platform Adapter ====================================
|
||||
# ==============================================================================================
|
||||
# Sourced by load_config.sh when PLATFORM=unraid.
|
||||
# Provides the platform_*() API — bash scripts call these instead of OS-specific commands.
|
||||
#
|
||||
# ── API CONTRACT ──────────────────────────────────────────────────────────────────────────────
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Platform abstraction layer for Unraid. Sourced by load_config.sh when
|
||||
# PLATFORM=unraid. Scripts call platform_*() functions instead of OS-specific
|
||||
# commands — the adapter isolates all OS-dependent logic in one place.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# API CONTRACT
|
||||
# Every function returns 0 on success / 1 on failure unless noted.
|
||||
# Functions that produce output write to stdout; callers capture with $().
|
||||
# No function calls exit — callers decide what failure means for their flow.
|
||||
#
|
||||
# ── ADDING A PLATFORM ─────────────────────────────────────────────────────────────────────────
|
||||
# Create Plugin/truenas/adapter.sh (or ubuntu/adapter.sh) implementing the same function names.
|
||||
# load_config.sh sources Plugin/$PLATFORM/adapter.sh — no other changes needed.
|
||||
# ADDING A PLATFORM
|
||||
# Create Plugin/truenas/adapter.sh (or ubuntu/adapter.sh) implementing the
|
||||
# same function names. load_config.sh sources Plugin/$PLATFORM/adapter.sh —
|
||||
# no other changes needed anywhere in the codebase.
|
||||
#
|
||||
# ── FUNCTIONS ─────────────────────────────────────────────────────────────────────────────────
|
||||
# FUNCTIONS
|
||||
# platform_require_cmd — verify a platform command exists and is executable
|
||||
# platform_storage_healthy — array mounted and shfs active on /mnt/user
|
||||
# platform_disk_states_path — path to the platform disk state file
|
||||
@@ -38,6 +47,7 @@
|
||||
# platform_rebuild_container — rebuild a container from its stored XML template
|
||||
# platform_push_conf — push master.conf to all listed hosts via WebGUI PHP
|
||||
# platform_push_setup_state — push wizard setup state to WebGUI PHP
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
+68
-10
@@ -1,19 +1,77 @@
|
||||
#!/bin/bash
|
||||
# Varaverk job runner — wraps script execution with JSON status tracking.
|
||||
# Called by /etc/cron.d/varaverk for every scheduled job.
|
||||
# ==============================================================================================
|
||||
# ============================= Job Runner =====================================================
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Usage: bash run_job.sh <job_id> <script_path> [flags...]
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Wraps every scheduled script execution with JSON status tracking and log
|
||||
# management. Called by /etc/cron.d/varaverk for every scheduled job. The PHP
|
||||
# dashboard polls the JSON files to show live job status without running scripts.
|
||||
#
|
||||
# Flags consumed by run_job.sh (stripped before passing to script):
|
||||
# --manual — marks a UI-triggered run; writes a sentinel on completion so
|
||||
# the next cron fire is suppressed if it falls within the job's
|
||||
# own cron interval (prevents double-firing after manual run).
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Writes: /var/log/varaverk/<id>.json — status, timestamps, exit code, pid
|
||||
# /var/log/varaverk/<id>.log — appended per run, trimmed to LOG_MAX_LINES
|
||||
# /var/log/varaverk/<id>.manual_ts — sentinel: epoch of last manual completion
|
||||
# Invocation: bash run_job.sh <job_id> <script_path> [flags...]
|
||||
#
|
||||
# Writes three files per job to /var/log/varaverk/:
|
||||
# <id>.json — status, timestamps, exit code, pid (polled by WebGUI)
|
||||
# <id>.log — appended per run, trimmed to LOG_MAX_LINES lines
|
||||
# <id>.manual_ts — sentinel: epoch of last manual completion (interval suppression)
|
||||
#
|
||||
# Status values: running → ok (exit 0) | warn (exit 1) | error (exit 2+)
|
||||
#
|
||||
# MANUAL FLAG
|
||||
# --manual marks a UI-triggered run. On completion, writes a manual_ts sentinel.
|
||||
# The next cron fire reads the sentinel and suppresses itself if the elapsed time
|
||||
# is within the job's own cron interval — prevents double-firing after a manual run.
|
||||
# Static schedules (e.g. "30 2 * * 0") are never suppressed — only */N intervals.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Suppress Double-Fire
|
||||
# When a user triggers a job from the UI, the next cron fire within the job's
|
||||
# own interval is skipped. A 30-minute cron job triggered at HH:14 won't fire
|
||||
# again at HH:30 — it waits for HH:44. Static schedules are never suppressed.
|
||||
#
|
||||
# Status as Ground Truth
|
||||
# The JSON file is overwritten atomically on every state change (start → end).
|
||||
# The WebGUI polls it directly — no additional IPC or database needed.
|
||||
#
|
||||
# Log Trim on Every Write
|
||||
# The log file is trimmed to LOG_MAX_LINES after every run. Never grows
|
||||
# unbounded regardless of how long the server runs or how often the job fires.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# --manual stripped — flag consumed here, never passed to the wrapped script
|
||||
# mkdir -p — log dir created if missing before any write
|
||||
# Log trim — tail -n LOG_MAX_LINES via tmp file + mv (atomic)
|
||||
# Sentinel cleanup — manual_ts removed after it's used or expired
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# LOG_DIR /var/log/varaverk (hardcoded — tmpfs on Unraid, cleared on reboot)
|
||||
# LOG_MAX_LINES 1000 (hardcoded — trim threshold per job log)
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# bash run_job.sh <job_id> <script_path> [script_flags...]
|
||||
# Normal cron-triggered run.
|
||||
#
|
||||
# bash run_job.sh <job_id> <script_path> --manual [script_flags...]
|
||||
# UI-triggered run. Suppresses next cron fire within the job's interval.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
JOB_ID="$1"
|
||||
SCRIPT="$2"
|
||||
|
||||
@@ -3,14 +3,21 @@
|
||||
# ============================= USER SCRIPTS MASTER TEMPLATE ===================================
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Paste this file into a User Script entry. Uncomment ONE script block and set the schedule.
|
||||
# Every script in the ecosystem is listed here — from the orchestrators that run it all,
|
||||
# down to the individual scripts you can run standalone for specific tasks.
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Paste this file into a User Script entry. Uncomment ONE script block and set
|
||||
# the schedule. Every script in the ecosystem is listed here — from the
|
||||
# orchestrators that run it all, down to the individual scripts you can run
|
||||
# standalone for specific tasks.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── HOW THIS ECOSYSTEM WORKS ──────────────────────────────────────────────────────────────────
|
||||
# When used as intended, only a handful of orchestrators need to be scheduled.
|
||||
# The orchestrators handle everything else in the correct order — they call child scripts,
|
||||
# manage timing and dependencies, track pass/fail, and send one notification per window.
|
||||
# The orchestrators handle everything else in the correct order — they call child
|
||||
# scripts, manage timing and dependencies, track pass/fail, and send one
|
||||
# notification per window.
|
||||
#
|
||||
# You do not need to schedule every script below. The orchestrators cover it all:
|
||||
#
|
||||
@@ -18,10 +25,9 @@
|
||||
# array_stopping.sh at array stop — graceful shutdown sequence
|
||||
# watchdog_orchestrator.sh every 15 min — resource + docker + system watchdog
|
||||
# transcode_management.sh every 7 min — cleanup then manager (order critical)
|
||||
# critical_sync_maintenance.sh every 30 min — auth + Emby dirty sync + partnership
|
||||
# critical_sync_maintenance.sh every 30 min — auth + play_state_sync + partnership
|
||||
# intermediate_sync_maintenance.sh every 4 hours — arr library sync + artwork fetch
|
||||
# daily_sync_maintenance.sh 1am daily — git + rsync + media + restart
|
||||
# rsync.sh --profile=emby-fallback every 30 min — Emby watch state dirty sync
|
||||
# weekly_sync_maintenance.sh 2:30am Sunday — clean sync + image updates
|
||||
# sunday_morning_coffee_report.sh 7am Sunday — full weekly digest
|
||||
# weekly_health_digest.sh 8am daily — profile-controlled health notification
|
||||
@@ -227,10 +233,9 @@
|
||||
# Auth stack to HOST2: NPM proxy rules, TLS certs, LLDAP user accounts,
|
||||
# Authelia config and policies. HOST2 auth always within 30min of HOST1.
|
||||
#
|
||||
# rsync Emby dirty sync:
|
||||
# Watch states, user activity, library delta — Emby stays running on both sides.
|
||||
# WAL/SHM files excluded (unsafe to copy mid-write). HOST2 Emby restarts after sync
|
||||
# to pick up config changes.
|
||||
# play_state_sync:
|
||||
# API-based watch state and resume position sync. Runs as a CRITICAL_MAINTENANCE_SCRIPT
|
||||
# in this window. No rsync — uses Emby API to sync per-user watch state directly.
|
||||
#
|
||||
# partnership --check:
|
||||
# Reads remote state file. Increments offline counter on failed sync.
|
||||
@@ -295,22 +300,6 @@
|
||||
# bash /boot/config/plugins/varaverk/Orchestrators/daily_sync_maintenance.sh
|
||||
|
||||
|
||||
# ── RSYNC EMBY FALLBACK ───────────────────────────────────────────────────────────────────────
|
||||
# Schedule: */30 * * * * (every 30 minutes)
|
||||
# Background: YES
|
||||
#
|
||||
# Keeps HOST2 Emby within 30 minutes of HOST1 on watch states and library changes.
|
||||
# Direct rsync.sh call (not an orchestrator). Emby stays running on both sides.
|
||||
#
|
||||
# Syncs: users.db, library.db, authentication.db, config/
|
||||
# Skips: *.wal *.shm (unsafe mid-write), transcodes/, logs/, cache/ (volatile/local only)
|
||||
# After: HOST2 Emby restarts to pick up any config changes from the sync.
|
||||
# Result: if HOST1 fails, users resume from at most 30 minutes stale.
|
||||
#
|
||||
# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
|
||||
# /mnt/user/Media_Server/Emby --profile=emby-fallback
|
||||
|
||||
|
||||
# ── WEEKLY SYNC MAINTENANCE ───────────────────────────────────────────────────────────────────
|
||||
# Schedule: 30 2 * * 0 (Sunday 2:30am — 4.5 hours before coffee report)
|
||||
# Background: YES
|
||||
@@ -524,7 +513,6 @@
|
||||
# important-data Postgres-NextCloud + delayed: NextCloud
|
||||
# arrs_stack Sonarr, Radarr, Lidarr, Prowlarr, Bazarr, Pinchflat
|
||||
# emby Emby both sides (weekly clean sync — both instances stopped)
|
||||
# emby-fallback nothing stopped (Emby stays running — dirty sync, WAL/SHM excluded)
|
||||
# [no profile] no containers stopped (media shares, plain data)
|
||||
#
|
||||
# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
|
||||
@@ -536,8 +524,6 @@
|
||||
# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
|
||||
# /mnt/user/Media_Server/Emby --profile=emby
|
||||
# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
|
||||
# /mnt/user/Media_Server/Emby --profile=emby-fallback
|
||||
# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
|
||||
# /mnt/user/appdata-Fallback/Gmer4Lfe
|
||||
# bash /boot/config/plugins/varaverk/Rsync/rsync.sh /mnt/user/Movies
|
||||
# bash /boot/config/plugins/varaverk/Rsync/rsync.sh /mnt/user/Tv_Shows
|
||||
@@ -1422,7 +1408,7 @@
|
||||
#
|
||||
# */30 * * * * every 30 minutes:
|
||||
# Orchestrators/critical_sync_maintenance.sh
|
||||
# Rsync/rsync.sh /mnt/user/Media_Server/Emby --profile=emby-fallback
|
||||
# └─ Critical-Data rsync → play_state_sync → partnership --check
|
||||
#
|
||||
# 0 */4 * * * every 4 hours:
|
||||
# Orchestrators/intermediate_sync_maintenance.sh
|
||||
|
||||
@@ -20,6 +20,30 @@
|
||||
# RAM → backup removed → fallback.sh runs with last-known-good partner vars
|
||||
#
|
||||
# Own conf is never in the backup — it's always on disk.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Remove After Use
|
||||
# The persistent backup is always removed at the end of the run — whether it
|
||||
# was used or not. Stale backups from a previous shutdown should never be left
|
||||
# behind as a permanent fallback; the backup is a single-boot safety net, not
|
||||
# a long-lived cache.
|
||||
#
|
||||
# Partner Only
|
||||
# Own conf is on disk and is always available regardless of array or partner
|
||||
# state. Only partner confs can be missing after a boot — only those are
|
||||
# restored.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# require_partnership — exits early if PARTNERSHIP_ENABLED=false
|
||||
# detect_hosts() — determines which conf files belong to partners vs self
|
||||
# No-backup guard — exits cleanly if PERSISTENT_CONF_CACHE doesn't exist
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -16,6 +16,28 @@
|
||||
#
|
||||
# Only partner confs are saved — own conf is always on disk.
|
||||
# Path adapts to storage mode: $SCRIPTS_DIR/.cache/vv/d (internal or appdata).
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Save at Shutdown, Not at Boot
|
||||
# The backup is created at array stop while the RAM cache is at its freshest.
|
||||
# By the next boot the partner may be unreachable — but the conf is already
|
||||
# preserved and available the moment conf_cache_restore.sh runs.
|
||||
#
|
||||
# Partner Only
|
||||
# Own conf is on disk — always present, never needs saving. Only partner confs
|
||||
# live in RAM and can be missing at the next boot.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# require_partnership — exits early if PARTNERSHIP_ENABLED=false
|
||||
# detect_hosts() — determines which confs to save (partner confs only)
|
||||
# No-cache guard — exits cleanly if RAM cache is empty or missing
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -25,6 +25,29 @@
|
||||
# works whether the remote is in internal or appdata storage mode.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# RAM-Only Credentials
|
||||
# Partner credentials and keys never touch disk on the receiving server.
|
||||
# /tmp/.cache/vv/d is tmpfs — cleared every reboot. This is intentional:
|
||||
# partner conf files are not discoverable on disk between boots.
|
||||
#
|
||||
# Pull Resolves Remote Path
|
||||
# The pull step reads the remote's varaverk.cfg to discover their SCRIPTS_DIR
|
||||
# before SSHing for the conf. Works whether the remote is in internal or
|
||||
# appdata storage mode — no hardcoded path assumptions about the remote.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# require_partnership — exits early if PARTNERSHIP_ENABLED=false
|
||||
# detect_hosts() — partner list for push/pull routing
|
||||
# acquire_lock — prevents concurrent sync runs
|
||||
# SSH reachability — partners that fail SSH are skipped, not fatal
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -17,16 +17,41 @@
|
||||
# Profile names are looked up by name from the API at runtime, so profile IDs
|
||||
# do not need to be hardcoded and work across hosts.
|
||||
#
|
||||
# ── USAGE ────────────────────────────────────────────────────────────────────
|
||||
# arr_profile_enforcer.sh [--dry-run] [--sonarr-only] [--radarr-only]
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Idempotent — only touches items whose current profile is wrong
|
||||
# API-only — no file system changes; profile ID looked up by name at runtime
|
||||
# --dry-run mode — reports what would change without applying any updates
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── CONFIGURATION ────────────────────────────────────────────────────────────
|
||||
# master.conf
|
||||
#
|
||||
# ARR_KIDS_PROFILE_NAME — profile name for kids/anime (default: "Kids shows")
|
||||
# ARR_SONARR_DEFAULT_PROFILE — default Sonarr profile name (default: "Any")
|
||||
# ARR_RADARR_DEFAULT_PROFILE — default Radarr profile name (default: "Any (mine)")
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# arr_profile_enforcer.sh
|
||||
# Enforce profiles in both Sonarr and Radarr.
|
||||
#
|
||||
# arr_profile_enforcer.sh --dry-run
|
||||
# Show which items would be updated without making changes.
|
||||
#
|
||||
# arr_profile_enforcer.sh --sonarr-only
|
||||
# Run only Sonarr enforcement.
|
||||
#
|
||||
# arr_profile_enforcer.sh --radarr-only
|
||||
# Run only Radarr enforcement.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
DRY_RUN=false
|
||||
RUN_SONARR=true
|
||||
|
||||
@@ -28,6 +28,32 @@
|
||||
# terminal or from array_started.sh.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Mode-Aware Paths
|
||||
# Storage mode is read from host*.conf before any symlink is created. Internal
|
||||
# mode (boot) requires no array — symlinks resolve immediately. Appdata mode
|
||||
# requires the array to be mounted before symlinks are useful.
|
||||
#
|
||||
# Migrate on First Run
|
||||
# If live data already exists at /root/.claude and the persistent store is
|
||||
# empty, the live data is moved to persistent storage on first run. Subsequent
|
||||
# runs only re-create the symlinks — migration is one-time.
|
||||
#
|
||||
# No common.sh Dependency
|
||||
# Runs before load_config.sh is available (early in ARRAY_START_SCRIPTS).
|
||||
# All logic is self-contained — no ecosystem functions used.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Conf probe — reads storage mode from Configurations/host*.conf directly
|
||||
# Migration guard — only migrates if persistent store is empty; never overwrites
|
||||
# Symlink-safe — removes existing symlink before re-creating; won't error on re-run
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
@@ -20,6 +20,25 @@
|
||||
# know all stopped containers are safe to delete.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Safe Default, Explicit Escalation
|
||||
# The default mode (dangling only) is always safe — running containers are
|
||||
# never affected. The --all mode requires deliberate opt-in and carries an
|
||||
# explicit caution in the description, because it removes stopped containers
|
||||
# that may be intentionally paused.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Root check — Docker prune operations require root
|
||||
# acquire_lock — prevents concurrent prune runs
|
||||
# --dry-run — shows what would be removed without taking any action
|
||||
# --status — lists current dangling images and stopped containers; no changes
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
|
||||
+40
-8
@@ -12,15 +12,47 @@
|
||||
# The listener (start_webhook_listener.sh) must be running before arrs will
|
||||
# actually deliver events, but this script can register the connection first.
|
||||
#
|
||||
# ── WHAT IT DOES ─────────────────────────────────────────────────────────────
|
||||
# 1. Generates WEBHOOK_SECRET in master.conf if empty
|
||||
# 2. Registers webhook in each local arr (Sonarr / Radarr / Lidarr)
|
||||
# 3. SSHes to remote host and runs itself there (unless --local-only)
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# ── USAGE ────────────────────────────────────────────────────────────────────
|
||||
# webhook_setup.sh — configure local + remote
|
||||
# webhook_setup.sh --local-only — local arrs only (used internally for SSH)
|
||||
# webhook_setup.sh --dry-run — show what would be registered
|
||||
# 1. Generate WEBHOOK_SECRET in master.conf if empty
|
||||
# 2. Register webhook in each local arr (Sonarr / Radarr / Lidarr)
|
||||
# 3. SSH to remote host and run itself there (unless --local-only)
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Idempotent Registration
|
||||
# Skips any arr that already has the webhook registered. Safe to re-run after
|
||||
# adding a new arr or after a conf change without creating duplicate entries.
|
||||
#
|
||||
# Self-Propagating
|
||||
# SSHes to the remote and runs itself with --local-only — one execution
|
||||
# configures both servers without a separate remote step.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Idempotent — skips arrs that already have the webhook registered
|
||||
# Secret auto-gen — WEBHOOK_SECRET generated if empty; never left blank
|
||||
# --local-only — used internally for SSH; prevents infinite recursion
|
||||
# --dry-run mode — shows what would be registered without making API calls
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# webhook_setup.sh
|
||||
# Configure local arrs, then SSH to remote and configure remote arrs.
|
||||
#
|
||||
# webhook_setup.sh --local-only
|
||||
# Local arrs only (used internally when called via SSH on the remote).
|
||||
#
|
||||
# webhook_setup.sh --dry-run
|
||||
# Show what would be registered without making any changes.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
|
||||
@@ -19,6 +19,31 @@
|
||||
#
|
||||
# Silent when remote is online and no backup exists (normal state).
|
||||
# No-op when FALLBACK_ENABLED=false or CONF_SYNC_ENABLED=false.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Online = No Backup Needed
|
||||
# When the remote is reachable, conf_sync.sh will pull fresh on the next boot.
|
||||
# The persistent backup is removed — a stale backup is worse than no backup
|
||||
# because it can mask a connectivity problem that conf_sync.sh would catch.
|
||||
#
|
||||
# Offline = Stay Ready
|
||||
# While the remote is down, the RAM cache is the best available copy of partner
|
||||
# vars. Refreshing the persistent backup every 15 minutes ensures it reflects
|
||||
# the last-known-good state, not an old copy from days earlier.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# require_partnership — exits early if PARTNERSHIP_ENABLED=false
|
||||
# FALLBACK_ENABLED gate — exits if fallback is disabled
|
||||
# CONF_SYNC_ENABLED gate — exits if conf sync is disabled
|
||||
# REMOTE_ID presence check — exits if partner identity is unset
|
||||
# --dry-run mode — shows what would happen without touching the backup
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -2,32 +2,68 @@
|
||||
# ==============================================================================================
|
||||
# ================================= System Watchdog ============================================
|
||||
# ==============================================================================================
|
||||
#
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Thin orchestrator — runs SYSTEM_WATCHDOG_SCRIPTS from master.conf sequentially.
|
||||
# Called by watchdog_orchestrator.sh each cycle. Covers system component health:
|
||||
# storage pool growth, runaway logs, WebGUI availability, and network connectivity.
|
||||
#
|
||||
# ── EXECUTION ORDER ───────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Driven by SYSTEM_WATCHDOG_SCRIPTS in master.conf — add, remove, or reorder there.
|
||||
# Default: storage_watchdog → webgui_watchdog → network_watchdog
|
||||
#
|
||||
# ── SEQUENTIAL EXECUTION ─────────────────────────────────────────────────────────────────────
|
||||
# All scripts run in the foreground. Each must complete before the next starts.
|
||||
# A failed script is logged but does not prevent remaining scripts from running.
|
||||
#
|
||||
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Configuration Owns the List
|
||||
# SYSTEM_WATCHDOG_SCRIPTS in master.conf is the only place scripts are added
|
||||
# or removed. This orchestrator never needs to be edited to change what runs.
|
||||
#
|
||||
# Non-Fatal Steps
|
||||
# A failed watchdog step is logged and noted in the summary, but the remaining
|
||||
# steps still execute. Partial coverage is better than a halted watchdog chain.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Root check — child scripts require root
|
||||
# acquire_lock — prevents concurrent system watchdog runs
|
||||
# detect_hosts() — MY_ID in notifications and logs
|
||||
# Non-fatal steps — a failed step is logged; remaining steps still run
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# SYSTEM_WATCHDOG_SCRIPTS — ordered list of system component watchdog scripts to run
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# system_watchdog.sh — run all system component watchdogs
|
||||
# system_watchdog.sh --dry-run — preview without running anything
|
||||
# system_watchdog.sh --status — show configured scripts and exit
|
||||
# system_watchdog.sh --log — verbose output
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# system_watchdog.sh
|
||||
# Run all system component watchdogs.
|
||||
#
|
||||
# system_watchdog.sh --dry-run
|
||||
# Preview without running anything.
|
||||
#
|
||||
# system_watchdog.sh --status
|
||||
# Show configured scripts and exit.
|
||||
#
|
||||
# system_watchdog.sh --log
|
||||
# Verbose output.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
+64
-25
@@ -2,35 +2,63 @@
|
||||
# ==============================================================================================
|
||||
# ================================= Git Pull & Execute =========================================
|
||||
# ==============================================================================================
|
||||
# Pulls the latest scripts from the Gitea repository via SSH.
|
||||
# Lives at the repo root — sources load_config.sh from the same directory.
|
||||
#
|
||||
# ── WHAT THIS SCRIPT DOES ─────────────────────────────────────────────────────────────────────
|
||||
# 1. Detects which server it's running on via detect_hosts() (MY_ID)
|
||||
# 2. Configures sparse checkout to exclude other servers' credential files
|
||||
# Each server only pulls its own host*.conf — never sees peer credentials
|
||||
# 3. Pulls or clones latest scripts from Gitea
|
||||
# 4. Sets executable permissions on all .sh files
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Pulls the latest scripts from the Gitea repository via SSH. Lives at the repo
|
||||
# root — sources load_config.sh from the same directory.
|
||||
#
|
||||
# ── SPARSE CHECKOUT ───────────────────────────────────────────────────────────────────────────
|
||||
# Sparse checkout ensures each server only receives its own host conf:
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# 1. detect_hosts() — identifies which server is running the pull (MY_ID)
|
||||
# 2. Configure sparse checkout — exclude other servers' credential files
|
||||
# 3. Pull or clone latest scripts from Gitea
|
||||
# 4. Set executable permissions on all .sh files
|
||||
#
|
||||
# SPARSE CHECKOUT
|
||||
# Each server only receives its own host conf — never peer credentials:
|
||||
# HOST1 pulls: master.conf + host1.conf + all scripts
|
||||
# HOST1 skips: host2.conf, host3.conf etc.
|
||||
# HOST2 pulls: master.conf + host2.conf + all scripts
|
||||
# HOST2 skips: host1.conf, host3.conf etc.
|
||||
# Adding a new server: create host3.conf in the repo — all existing servers
|
||||
# automatically exclude it on next pull; new server gets only its own conf.
|
||||
#
|
||||
# Adding a new server:
|
||||
# Create host3.conf in the repo
|
||||
# All existing servers automatically exclude it on next pull
|
||||
# New server gets only its own conf ✅
|
||||
#
|
||||
# ── GITEA LOCATION DETECTION ──────────────────────────────────────────────────────────────────
|
||||
# Detects where Gitea is running at runtime — works through fallback:
|
||||
# GITEA LOCATION DETECTION
|
||||
# Detected at runtime — works through fallback:
|
||||
# Gitea local → connects via local IP
|
||||
# Gitea remote → connects via Tailscale IP
|
||||
# Both fail → falls back to GITEA_DOMAIN if configured
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Credentials Stay Separated
|
||||
# Sparse checkout is configured per-server on every pull, not just at clone
|
||||
# time. Ensures newly added host confs are automatically excluded on all
|
||||
# existing servers without any manual intervention.
|
||||
#
|
||||
# Runtime Location Detection
|
||||
# Gitea's IP is never hardcoded — the script probes whether Gitea is local
|
||||
# or remote on every run. Handles Gitea container restarts, migrations, and
|
||||
# Tailscale address changes automatically.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL SAFEGUARDS
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Root check — git operations on /boot require root
|
||||
# acquire_lock — prevents concurrent pulls
|
||||
# detect_hosts() — MY_ID required to build correct sparse checkout rules
|
||||
# Docker check — Gitea container status probed before any SSH attempt
|
||||
#
|
||||
# ==============================================================================================
|
||||
# CONFIGURATION
|
||||
# ==============================================================================================
|
||||
#
|
||||
# master.conf
|
||||
#
|
||||
# GITEA_CONTAINER — Docker container name for Gitea
|
||||
# GITEA_REPO_PATH — repo path on Gitea (e.g. Varaverk/varaverk.git)
|
||||
# GITEA_DOMAIN — public domain fallback (optional)
|
||||
@@ -38,11 +66,22 @@
|
||||
# GITEA_SSH_KEY — SSH key for Gitea authentication
|
||||
# SSH_PORT — Gitea SSH port (often 221 or 222)
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# git_pull_execute.sh — normal pull
|
||||
# git_pull_execute.sh --dry-run — preview without making changes
|
||||
# git_pull_execute.sh --log — verbose output
|
||||
# git_pull_execute.sh --status — show config and exit
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# git_pull_execute.sh
|
||||
# Normal pull.
|
||||
#
|
||||
# git_pull_execute.sh --dry-run
|
||||
# Preview without making changes.
|
||||
#
|
||||
# git_pull_execute.sh --log
|
||||
# Verbose output.
|
||||
#
|
||||
# git_pull_execute.sh --status
|
||||
# Show config and exit.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
+27
-30
@@ -2,51 +2,48 @@
|
||||
# ==============================================================================================
|
||||
# ================================= CONFIGURATION LOADER =======================================
|
||||
# ==============================================================================================
|
||||
#
|
||||
# PURPOSE
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Single entry point for all configuration sourcing across the ecosystem.
|
||||
# Every script sources this file instead of sourcing master.conf files directly.
|
||||
# Every script sources this file instead of sourcing conf files directly.
|
||||
#
|
||||
# ── HOW IT WORKS ──────────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# OPERATIONAL MODEL
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Sourcing order (order is load-bearing):
|
||||
# 1. Detects OS platform → PLATFORM=unraid|truenas|unknown; exports SCRIPTS_DIR
|
||||
# 2. Sources master.conf (shared config — hostnames, thresholds, toggles, profiles, job lists)
|
||||
# 3. Auto-discovers and sources all host*.conf files in the same directory
|
||||
# 2. Sources master.conf — shared config: hostnames, thresholds, toggles, profiles, job lists
|
||||
# 3. Auto-discovers and sources all host*.conf files present in Configurations/
|
||||
# Each host conf extends the shared profile arrays and adds host-specific credentials
|
||||
# 4. Sources common.sh (shared functions — detect_hosts, logging, notifications etc.)
|
||||
# 5. Sources Plugin/<platform>/adapter.sh (platform_*() functions for OS-specific ops)
|
||||
#
|
||||
# ── WHY THIS EXISTS ───────────────────────────────────────────────────────────────────────────
|
||||
# Without this loader every script had to explicitly source each conf file:
|
||||
# source master.conf
|
||||
# source host1.conf
|
||||
# source host2.conf
|
||||
# source common.sh
|
||||
#
|
||||
# Adding a new server meant updating every script.
|
||||
# With this loader — add host3.conf to the git repo and every server
|
||||
# auto-discovers it on next git pull. Zero script changes required. Ever.
|
||||
#
|
||||
# ── ADDING A NEW SERVER ───────────────────────────────────────────────────────────────────────
|
||||
# 1. Create Configurations/host3.conf following the same structure as HOST1/HOST2
|
||||
# 2. Commit and push to git repo
|
||||
# 3. All servers pull it automatically — no other changes needed
|
||||
#
|
||||
# ── USAGE IN SCRIPTS ──────────────────────────────────────────────────────────────────────────
|
||||
# Replace the three source lines at the top of every script with:
|
||||
# 4. Sources common.sh — shared functions: detect_hosts, logging, notifications, etc.
|
||||
# 5. Sources Plugin/<platform>/adapter.sh — platform_*() functions for OS-specific ops
|
||||
#
|
||||
# USAGE IN SCRIPTS
|
||||
# Scripts in subdirectories (Rsync/, Docker_Essentials/ etc.):
|
||||
# SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# source "$SCRIPT_DIR/../load_config.sh"
|
||||
#
|
||||
# Scripts in subdirectories (Rsync/, Docker_Essentials/ etc.) use ../ to reach root.
|
||||
# Scripts in root directory use ./ instead:
|
||||
#
|
||||
# Scripts in the repo root:
|
||||
# SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# source "$SCRIPT_DIR/load_config.sh"
|
||||
#
|
||||
# ── SPARSE CHECKOUT NOTE ──────────────────────────────────────────────────────────────────────
|
||||
# SPARSE CHECKOUT NOTE
|
||||
# Sparse checkout controls which host*.conf files each server receives.
|
||||
# HOST1 only pulls Configurations/host1.conf — never HOST2's credentials.
|
||||
# HOST2 only pulls Configurations/host2.conf — never HOST1's credentials.
|
||||
# This loader sources whatever conf files ARE present — sparse checkout handles the rest.
|
||||
# Both servers pull all non-credential conf files (Configurations/master.conf, common.sh, load_config.sh).
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
# ==============================================================================================
|
||||
#
|
||||
# Zero-Script Expansion
|
||||
# Without this loader, adding a new server required updating every script to
|
||||
# source the new host conf. With this loader: create host3.conf in the repo,
|
||||
# commit and push — all servers auto-discover it on next git pull. No script
|
||||
# changes required.
|
||||
#
|
||||
# ==============================================================================================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user