update updater

This commit is contained in:
Frank Harris 2026-06-06 12:21:56 -05:00
parent cc5f7bb90c
commit d8c66c4c49
2 changed files with 31 additions and 8 deletions

View file

@ -163,9 +163,17 @@ return [
function gsp_validate_update_settings(array $cfg) function gsp_validate_update_settings(array $cfg)
{ {
$errors = []; $errors = [];
if (!preg_match('/^https?:\/\/[^ \t\r\n]+\.git$/i', (string)$cfg['repo_url']) $repo_source = trim((string)$cfg['repo_url']);
&& !preg_match('/^(?:ssh:\/\/|git@)[^ \t\r\n]+$/i', (string)$cfg['repo_url'])) { $is_git_url = preg_match('/^https?:\/\/[^ \t\r\n]+(?:\.git)?$/i', $repo_source)
$errors[] = 'Repository URL must be an http(s), ssh, or git@ URL.'; || preg_match('/^(?:ssh:\/\/|git@)[^ \t\r\n]+$/i', $repo_source);
$is_local_path = preg_match('/^\//', $repo_source)
&& strpos($repo_source, "\0") === false
&& strpos($repo_source, '..') === false;
if (!$is_git_url && !$is_local_path) {
$errors[] = 'Repository source must be an http(s), ssh, git@ URL, or a safe absolute local path.';
}
if ($is_local_path && !is_dir($repo_source)) {
$errors[] = 'Repository local path does not exist or is not a directory: ' . $repo_source;
} }
if (!preg_match('/^[A-Za-z0-9._\/-]{1,128}$/', (string)$cfg['branch'])) { if (!preg_match('/^[A-Za-z0-9._\/-]{1,128}$/', (string)$cfg['branch'])) {
$errors[] = 'Branch/channel contains invalid characters.'; $errors[] = 'Branch/channel contains invalid characters.';
@ -1020,14 +1028,22 @@ $temp_dir = sys_get_temp_dir() . '/gsp_git_' . time() . '_' . mt_rand(1000, 9999
if (!@mkdir($temp_dir, 0750, true)) { if (!@mkdir($temp_dir, 0750, true)) {
return ['success' => false, 'error' => 'Cannot create temporary git checkout directory.']; return ['success' => false, 'error' => 'Cannot create temporary git checkout directory.'];
} }
if (!function_exists('exec')) {
gsp_rmdir_recursive($temp_dir);
return ['success' => false, 'error' => 'PHP exec() is disabled, so the updater cannot run git clone.'];
}
$out = []; $out = [];
$ret = 0; $ret = 0;
$cmd = 'git clone --depth 1 --branch ' . escapeshellarg($branch) . ' ' . escapeshellarg($repo_url) . ' ' . escapeshellarg($temp_dir) . ' 2>&1'; $cmd = 'git clone --depth 1 --branch ' . escapeshellarg($branch) . ' ' . escapeshellarg($repo_url) . ' ' . escapeshellarg($temp_dir) . ' 2>&1';
gsp_update_log('Starting configured git checkout from ' . $repo_url . ' branch ' . $branch);
exec($cmd, $out, $ret); exec($cmd, $out, $ret);
if ($ret !== 0) { if ($ret !== 0) {
gsp_rmdir_recursive($temp_dir); gsp_rmdir_recursive($temp_dir);
return ['success' => false, 'error' => 'git clone failed: ' . implode(' | ', array_slice($out, -20))]; $tail = implode(' | ', array_slice($out, -20));
gsp_update_log('Configured git checkout failed from ' . $repo_url . ' branch ' . $branch . ': ' . $tail);
return ['success' => false, 'error' => 'git clone failed for repository source "' . $repo_url . '" branch "' . $branch . '": ' . $tail];
} }
gsp_update_log('Configured git checkout completed from ' . $repo_url . ' branch ' . $branch);
return ['success' => true, 'temp_dir' => $temp_dir, 'source_root' => $temp_dir, 'output' => implode("\n", $out)]; return ['success' => true, 'temp_dir' => $temp_dir, 'source_root' => $temp_dir, 'output' => implode("\n", $out)];
} }
@ -2081,7 +2097,7 @@ echo "<h3>Repository Settings</h3>\n";
echo "<form method='POST'>\n"; echo "<form method='POST'>\n";
echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>\n"; echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>\n";
echo "<table class='center'>\n"; echo "<table class='center'>\n";
echo "<tr><td><strong>Repository URL</strong></td><td><input type='text' name='gsp_update_repo_url' value='" . htmlspecialchars($update_cfg['repo_url'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n"; echo "<tr><td><strong>Repository URL / Path</strong></td><td><input type='text' name='gsp_update_repo_url' value='" . htmlspecialchars($update_cfg['repo_url'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
echo "<tr><td><strong>Branch / Channel</strong></td><td><input type='text' name='gsp_update_branch' value='" . htmlspecialchars($update_cfg['branch'], ENT_QUOTES, 'UTF-8') . "' size='40'></td></tr>\n"; echo "<tr><td><strong>Branch / Channel</strong></td><td><input type='text' name='gsp_update_branch' value='" . htmlspecialchars($update_cfg['branch'], ENT_QUOTES, 'UTF-8') . "' size='40'></td></tr>\n";
echo "<tr><td><strong>Repository Root</strong></td><td><input type='text' name='gsp_update_repo_root' value='" . htmlspecialchars($update_cfg['repo_root'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n"; echo "<tr><td><strong>Repository Root</strong></td><td><input type='text' name='gsp_update_repo_root' value='" . htmlspecialchars($update_cfg['repo_root'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
echo "<tr><td><strong>Panel Path</strong></td><td><input type='text' name='gsp_update_panel_path' value='" . htmlspecialchars($update_cfg['panel_path'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n"; echo "<tr><td><strong>Panel Path</strong></td><td><input type='text' name='gsp_update_panel_path' value='" . htmlspecialchars($update_cfg['panel_path'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";

View file

@ -22,7 +22,7 @@ The update page is intentionally simple:
The admin page stores these settings in the Panel settings table: The admin page stores these settings in the Panel settings table:
- `gsp_update_repo_url` - `gsp_update_repo_url` for the repository source. This may be a Git URL or a safe absolute local repository path.
- `gsp_update_branch` - `gsp_update_branch`
- `gsp_update_repo_root` - `gsp_update_repo_root`
- `gsp_update_panel_path` - `gsp_update_panel_path`
@ -30,7 +30,7 @@ The admin page stores these settings in the Panel settings table:
Defaults: Defaults:
- Repository URL: `http://forge.runlevelsystems.com/dev/GSP.git` - Repository URL / Path: `http://forge.runlevelsystems.com/dev/GSP.git`
- Branch: `Panel-unstable` - Branch: `Panel-unstable`
- Repository Root: `/var/www/html/GSP` - Repository Root: `/var/www/html/GSP`
- Panel Path: `/var/www/html/GSP/Panel` - Panel Path: `/var/www/html/GSP/Panel`
@ -48,7 +48,7 @@ Important implementation note:
2. Validate repository URL, branch, repo root, and Panel path. 2. Validate repository URL, branch, repo root, and Panel path.
3. Run preflight against the configured paths. 3. Run preflight against the configured paths.
4. Create a backup when enabled. 4. Create a backup when enabled.
5. Clone the configured repository branch into a temporary checkout. 5. Clone the configured repository source and branch into a temporary checkout.
6. Sync files into the configured root/Panel paths. 6. Sync files into the configured root/Panel paths.
7. Preserve `Panel/includes/config.inc.php`. 7. Preserve `Panel/includes/config.inc.php`.
8. Run module updates/post-update hooks. 8. Run module updates/post-update hooks.
@ -64,3 +64,10 @@ The old repeated SSL vhost disable buttons are not part of the primary update pa
- The updater still contains legacy GitHub release helper code that is no longer rendered by the simplified primary UI. - The updater still contains legacy GitHub release helper code that is no longer rendered by the simplified primary UI.
- Real production testing should confirm file ownership and web-server permissions on `/var/www/html/GSP`. - Real production testing should confirm file ownership and web-server permissions on `/var/www/html/GSP`.
## Repair Notes
- The update page fatal `Call to undefined function gsp_update_settings()` means the deployed `Panel/modules/administration/panel_update.php` is missing the top-level helper or is not the current repository copy.
- `Panel/modules/update/update.php` only loads `Panel/modules/administration/panel_update.php` and calls `gsp_panel_update_section()`.
- The configured update action uses `git clone --depth 1 --branch <configured branch> <configured repository source> <temporary checkout>`.
- Clone failures are logged to `logs/update_trace.log` with the configured repository source and branch.