Read scheduler children from master.conf arrays

Orchestrators using dynamic dispatch (array_started, watchdog_orchestrator,
daily/weekly/etc maintenance) iterate \${VARNAME_SCRIPTS[@]} arrays defined
in master.conf. The previous regex only caught static hardcoded paths.

Two-strategy approach, merged and deduped:
1. Static: \$VAR/../Category/script.sh and \$VAR/Category/script.sh patterns
2. Dynamic: detect \${VARNAME[@]} references in the orch, parse that array
   from master.conf — handles inline args and skips commented entries

Changes to master.conf arrays are reflected immediately on next page load.
This commit is contained in:
Gmer4Lfe
2026-05-23 17:08:52 -04:00
parent b00918e080
commit 0197eab7a5
@@ -79,31 +79,37 @@ function vv_job_tree(): array {
return $orchs;
}
// Parse a bash array from master.conf content and return its script paths.
// Handles entries with inline args ("script.sh --flag") and skips commented lines (#"...").
function vv_parse_conf_array(string $conf, string $varName): array {
if (!preg_match('/^\s*' . preg_quote($varName, '/') . '\s*=\s*\((.*?)\)/ms', $conf, $m)) {
return [];
}
$scripts = [];
preg_match_all('/^\s*(?!#)"([^"]+)"/m', $m[1], $entries);
foreach ($entries[1] as $entry) {
$parts = preg_split('/\s+/', trim($entry));
$path = $parts[0] ?? '';
if (substr($path, -3) === '.sh') $scripts[] = $path;
}
return $scripts;
}
// Parse an orchestrator script to find which child scripts it calls.
// Handles two common patterns:
// $SCRIPT_DIR/../Category/script.sh (relative via variable)
// $SCRIPTS_ROOT/Category/script.sh (absolute root via variable)
// Root-level files (load_config.sh, etc.) are excluded — must be in a subdirectory.
// Validates each candidate against the filesystem to filter false positives.
// Two strategies, merged and deduped:
// 1. Static paths: $SCRIPT_DIR/../Category/script.sh or $SCRIPTS_ROOT/Category/script.sh
// 2. master.conf arrays: detects ${VARNAME[@]} iteration and reads the array from master.conf
// Root-level files (load_config.sh etc.) excluded — must be in a subdirectory.
// Each candidate validated against the filesystem.
function vv_script_children(string $orchPath, array $schedule): array {
$scriptsDir = SCRIPTS_DIR;
$content = file_get_contents($orchPath) ?: '';
$children = [];
$seen = [];
// Match $ANY_VAR/../Category/script.sh or $ANY_VAR/Category/script.sh
// Capture only the Category/script.sh portion (requires at least one subdirectory)
preg_match_all(
'/\$[A-Z_]+\/(?:\.\.\/)?([A-Za-z][A-Za-z0-9_.\-]*\/[A-Za-z0-9_.\-]+\.sh)/',
$content,
$m
);
foreach ($m[1] as $rel) {
if (isset($seen[$rel])) continue;
if (!file_exists("$scriptsDir/$rel")) continue;
$addChild = function(string $rel) use ($scriptsDir, $schedule, &$children, &$seen) {
if (isset($seen[$rel]) || !file_exists("$scriptsDir/$rel")) return;
$seen[$rel] = true;
$entry = $schedule[$rel] ?? ['enabled' => false, 'cron' => ''];
$children[] = [
'id' => $rel,
@@ -112,6 +118,23 @@ function vv_script_children(string $orchPath, array $schedule): array {
'enabled' => (bool)($entry['enabled'] ?? false),
'cron' => $entry['cron'] ?? '',
];
};
// Strategy 1: static variable paths ($VAR/../Category/script.sh or $VAR/Category/script.sh)
preg_match_all(
'/\$[A-Z_]+\/(?:\.\.\/)?([A-Za-z][A-Za-z0-9_.\-]*\/[A-Za-z0-9_.\-]+\.sh)/',
$content, $m
);
foreach ($m[1] as $rel) $addChild($rel);
// Strategy 2: master.conf arrays — find every ${VARNAME_SCRIPTS[@]} the orch iterates
preg_match_all('/\$\{([A-Z_]+_SCRIPTS)\[@\]\}/', $content, $refs);
if (!empty($refs[1])) {
$confRaw = file_get_contents(CONF_DIR . '/master.conf') ?: '';
foreach (array_unique($refs[1]) as $varName) {
foreach (vv_parse_conf_array($confRaw, $varName) as $rel) $addChild($rel);
}
}
return $children;
}