Varaverk: arrange mode, folder management, rsync standalone, layout fixes
Scheduler UI: - Arrange mode: drag scripts between orchs and reorder within arrays; right panel shows unassigned script pool; Save Arrangement commits to master.conf - + Folder: named collapsible subfolders for Custom Scripts stored in schedule.json - Rsync children: hide Run/Dry Run/Log/location when orch is ON; show standalone location + cron controls when orch is OFF; cron only fires when both filled - Non-conf-managed children (transcode): toggles now show enabled when orch is on - Right panel height sync: fix ResizeObserver feedback loop via align-self:flex-start on left panel and left.offsetHeight in vvFitRight - How do I use this: updated to cover arrange, folders, rsync standalone, transcode New API endpoints: - board.php, clearlock.php, movescript.php, rawconf.php, readscript.php - reorderarray.php, rsync_standalone.php, savefolders.php run.php / dryrun.php: accept optional --location= arg for standalone rsync calls
This commit is contained in:
@@ -82,6 +82,24 @@ function vv_cron_rebuild(array $schedule): bool {
|
||||
}
|
||||
$lines[] = "";
|
||||
|
||||
// Standalone rsync entries: fire when orch is disabled but location + cron are both configured.
|
||||
$scriptsDir = SCRIPTS_DIR;
|
||||
foreach ($schedule as $key => $entry) {
|
||||
if (!str_starts_with((string)$key, '__rsync_')) continue;
|
||||
$orchId = $entry['orch_id'] ?? '';
|
||||
$location = $entry['location'] ?? '';
|
||||
$cron = $entry['cron'] ?? '';
|
||||
if (!$orchId || !$location || !$cron) continue;
|
||||
// Skip if orch is still enabled
|
||||
if (!empty($schedule[$orchId]['enabled'])) continue;
|
||||
$rsyncScript = "$scriptsDir/Rsync/rsync.sh";
|
||||
if (!file_exists($rsyncScript)) continue;
|
||||
$locArg = escapeshellarg('--location=' . $location);
|
||||
$logFlag = !empty($entry['log_enabled']) ? ' --log' : '';
|
||||
$lines[] = "$cron bash \"$runner\" \"Rsync/rsync.sh\" \"$rsyncScript\" $locArg$logFlag";
|
||||
}
|
||||
$lines[] = "";
|
||||
|
||||
// Write to the plugin cron file; update_cron merges all plugin *.cron files into /etc/cron.d/root.
|
||||
if (file_put_contents(CRON_FILE, implode("\n", $lines)) === false) return false;
|
||||
exec('/usr/local/sbin/update_cron');
|
||||
@@ -210,6 +228,59 @@ function vv_custom_scripts(): array {
|
||||
return $scripts;
|
||||
}
|
||||
|
||||
// Load rsync standalone config (location + cron) for a flag name from schedule.json.
|
||||
function vv_rsync_standalone(string $flagName): array {
|
||||
$s = vv_schedule_load();
|
||||
$r = $s['__rsync_' . $flagName] ?? [];
|
||||
return [
|
||||
'location' => (string)($r['location'] ?? ''),
|
||||
'cron' => (string)($r['cron'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
// Extract *_SCRIPTS array variable names that an orchestrator iterates over.
|
||||
function vv_orch_conf_arrays(string $orchPath): array {
|
||||
$content = file_get_contents($orchPath) ?: '';
|
||||
preg_match_all('/\$\{([A-Z_]+_SCRIPTS)\[@\]\}/', $content, $refs);
|
||||
return array_unique($refs[1] ?? []);
|
||||
}
|
||||
|
||||
// Return .sh scripts that exist in SCRIPTS_DIR but are not referenced in any
|
||||
// master.conf *_SCRIPTS array and are not orchestrators or custom scripts.
|
||||
function vv_script_library(): array {
|
||||
$scriptsDir = SCRIPTS_DIR;
|
||||
$confMap = vv_conf_script_map();
|
||||
$orchIds = [];
|
||||
foreach (glob("$scriptsDir/Orchestrators/*.sh") ?: [] as $p) {
|
||||
$orchIds[] = 'Orchestrators/' . basename($p);
|
||||
}
|
||||
$exclude = ['Plugin', '.git', 'Orchestrators', 'Custom', 'Configurations'];
|
||||
$library = [];
|
||||
try {
|
||||
$ri = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($scriptsDir, RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
$base = rtrim($scriptsDir, '/') . '/';
|
||||
foreach ($ri as $rf) {
|
||||
if (!$rf->isFile() || strtolower($rf->getExtension()) !== 'sh') continue;
|
||||
$rel = ltrim(str_replace($base, '', $rf->getPathname()), '/');
|
||||
$parts = explode('/', $rel);
|
||||
if (count($parts) < 2 || in_array($parts[0], $exclude)) continue;
|
||||
if (in_array($rel, $orchIds) || isset($confMap[$rel])) continue;
|
||||
$library[] = ['id' => $rel, 'label' => basename($rel, '.sh')];
|
||||
}
|
||||
} catch (Exception $e) {}
|
||||
usort($library, fn($a, $b) => strcmp($a['id'], $b['id']));
|
||||
return $library;
|
||||
}
|
||||
|
||||
// Load custom-script folder assignments from schedule.json (__folders key).
|
||||
function vv_folders_load(): array {
|
||||
$s = vv_schedule_load();
|
||||
$f = $s['__folders'] ?? [];
|
||||
return is_array($f) ? $f : [];
|
||||
}
|
||||
|
||||
// Walk the scripts repo and return the job tree:
|
||||
// hardcoded array-event entries first, then cron-scheduled orchestrators
|
||||
function vv_job_tree(): array {
|
||||
@@ -260,6 +331,7 @@ function vv_job_tree(): array {
|
||||
'suggested_cron' => $suggested['cron'],
|
||||
'suggested_label' => $suggested['label'],
|
||||
'children' => vv_script_children($path, $schedule),
|
||||
'conf_arrays' => vv_orch_conf_arrays($path),
|
||||
];
|
||||
}
|
||||
return $orchs;
|
||||
@@ -411,6 +483,7 @@ function vv_script_children(string $orchPath, array $schedule): array {
|
||||
'log_enabled' => (bool)($entry['log_enabled'] ?? false),
|
||||
'conf_managed' => $conf['managed'],
|
||||
'conf_enabled' => $conf['enabled'], // null if not in any *_SCRIPTS array
|
||||
'conf_array' => $conf['array'],
|
||||
'suggested_cron' => $suggested['cron'],
|
||||
'suggested_label' => $suggested['label'],
|
||||
];
|
||||
@@ -447,3 +520,63 @@ function vv_script_children(string $orchPath, array $schedule): array {
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
// Extract the comment header block from a bash script (shebang + all leading comment lines).
|
||||
// Returns raw lines with # markers intact.
|
||||
function vv_script_header(string $path): string {
|
||||
if (!file_exists($path)) return '';
|
||||
$lines = array_slice(file($path) ?: [], 0, 80);
|
||||
$out = [];
|
||||
foreach ($lines as $line) {
|
||||
$t = rtrim($line);
|
||||
if (str_starts_with($t, '#') || ($out === [] && str_starts_with($t, '#!'))) {
|
||||
$out[] = $t;
|
||||
} elseif ($t === '' && !empty($out)) {
|
||||
$out[] = $t; // allow blank lines within header
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Trim trailing blank lines
|
||||
while (!empty($out) && trim(end($out)) === '') array_pop($out);
|
||||
return implode("\n", $out);
|
||||
}
|
||||
|
||||
// Strip the leading # marker from each line of a script header for cleaner display.
|
||||
// Also drops the shebang line (#!/bin/bash) since it's not informative in this context.
|
||||
function vv_script_header_clean(string $path): string {
|
||||
$raw = vv_script_header($path);
|
||||
if (!$raw) return '';
|
||||
$lines = explode("\n", $raw);
|
||||
$out = [];
|
||||
foreach ($lines as $line) {
|
||||
if (str_starts_with($line, '#!')) continue; // shebang — not useful in header display
|
||||
$out[] = preg_replace('/^#\s?/', '', $line); // strip # and optional space
|
||||
}
|
||||
while (!empty($out) && trim(end($out)) === '') array_pop($out);
|
||||
return implode("\n", $out);
|
||||
}
|
||||
|
||||
// Read a named section from a markdown file.
|
||||
// Calls $matcher(heading, isIntro) where isIntro=true for content before the first heading.
|
||||
// Returns the first matching section body, capped at $maxChars.
|
||||
function vv_readme_section(string $readmePath, callable $matcher, int $maxChars = 3000): string {
|
||||
if (!file_exists($readmePath)) return '';
|
||||
$content = file_get_contents($readmePath) ?: '';
|
||||
$parts = preg_split('/^(#{1,4}[^\n]*)/m', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$heading = '';
|
||||
$isIntro = true;
|
||||
foreach ($parts as $i => $part) {
|
||||
if ($i % 2 === 1) {
|
||||
$heading = trim(preg_replace('/^#{1,4}\s*/', '', $part));
|
||||
$isIntro = false;
|
||||
continue;
|
||||
}
|
||||
$body = trim($part);
|
||||
if ($body === '') continue;
|
||||
if ($matcher($heading, $isIntro)) {
|
||||
return strlen($body) > $maxChars ? substr($body, 0, $maxChars) . "\n[…]" : $body;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user