more improvements to safe upgrade

This commit is contained in:
Andy Miller
2025-10-17 10:02:22 -06:00
parent 78fc74a77e
commit 654c2bb9c4
4 changed files with 146 additions and 20 deletions

View File

@@ -290,6 +290,7 @@ class SafeUpgradeManager
$result = [ $result = [
'job' => $manifest ?: null, 'job' => $manifest ?: null,
'progress' => $progress, 'progress' => $progress,
'context' => $this->buildStatusContext(),
]; ];
$this->clearJobContext(); $this->clearJobContext();
@@ -417,6 +418,7 @@ class SafeUpgradeManager
'log' => $logPath, 'log' => $logPath,
'progress' => $this->getProgress(), 'progress' => $this->getProgress(),
'job' => $this->readManifest(), 'job' => $this->readManifest(),
'context' => $this->buildStatusContext(),
]; ];
} }
@@ -553,6 +555,7 @@ class SafeUpgradeManager
'status' => 'noop', 'status' => 'noop',
'version' => $localVersion, 'version' => $localVersion,
'message' => 'Grav is already up to date.', 'message' => 'Grav is already up to date.',
'context' => $this->buildStatusContext(),
]; ];
} }
@@ -647,6 +650,7 @@ class SafeUpgradeManager
'version' => $remoteVersion, 'version' => $remoteVersion,
'manifest' => $manifest, 'manifest' => $manifest,
'previous_version' => $localVersion, 'previous_version' => $localVersion,
'context' => $this->buildStatusContext(),
]; ];
} }
@@ -896,6 +900,7 @@ class SafeUpgradeManager
'status' => 'finalized', 'status' => 'finalized',
'version' => $localVersion, 'version' => $localVersion,
'message' => 'Post-install scripts completed.', 'message' => 'Post-install scripts completed.',
'context' => $this->buildStatusContext(),
]; ];
} }
@@ -1011,9 +1016,31 @@ class SafeUpgradeManager
return [ return [
'status' => 'error', 'status' => 'error',
'message' => $message, 'message' => $message,
'context' => $this->buildStatusContext(),
] + $extra; ] + $extra;
} }
protected function buildStatusContext(): ?string
{
$context = [];
if ($this->jobManifestPath) {
$context['manifest'] = $this->jobManifestPath;
}
if ($this->progressPath) {
$context['progress'] = $this->progressPath;
}
if (!$context) {
return null;
}
$encoded = json_encode($context);
return $encoded === false ? null : base64_encode($encoded);
}
protected function ensureExecutablePermissions(): void protected function ensureExecutablePermissions(): void
{ {
$executables = [ $executables = [

View File

@@ -33,11 +33,73 @@ $readJson = static function (string $path): ?array {
$progress = null; $progress = null;
$manifest = null; $manifest = null;
$manifestPath = null;
$progressPath = null;
$normalizeDir = static function (string $path): string {
$normalized = str_replace('\\', '/', $path);
return rtrim($normalized, '/');
};
$jobsDirNormalized = $normalizeDir($jobsDir);
$userDataDirNormalized = $normalizeDir(dirname($jobsDir));
$contextParam = $_GET['context'] ?? '';
if ($contextParam !== '') {
$decodedRaw = base64_decode(strtr($contextParam, ' ', '+'), true);
if ($decodedRaw !== false) {
$decoded = json_decode($decodedRaw, true);
if (is_array($decoded)) {
$validatePath = static function (string $candidate) use ($normalizeDir, $jobsDirNormalized, $userDataDirNormalized) {
$candidate = str_replace('\\', '/', $candidate);
$directory = dirname($candidate);
$real = realpath($directory);
if ($real === false) {
return null;
}
$real = $normalizeDir($real);
if (strpos($real, $jobsDirNormalized) !== 0 && strpos($real, $userDataDirNormalized) !== 0) {
return null;
}
return $candidate;
};
if (!empty($decoded['manifest'])) {
$candidate = $validatePath((string)$decoded['manifest']);
if ($candidate) {
$manifestPath = $candidate;
if (is_file($candidate)) {
$manifest = $readJson($candidate);
}
}
}
if (!empty($decoded['progress'])) {
$candidate = $validatePath((string)$decoded['progress']);
if ($candidate) {
$progressPath = $candidate;
if (is_file($candidate)) {
$progress = $readJson($candidate);
}
}
}
}
}
}
if ($jobId !== '') { if ($jobId !== '') {
$jobPath = $jobsDir . '/' . $jobId; $jobPath = $jobsDir . '/' . $jobId;
$progress = $readJson($jobPath . '/progress.json'); $progressPath = $progressPath ?: ($jobPath . '/progress.json');
$manifest = $readJson($jobPath . '/manifest.json'); $manifestPath = $manifestPath ?: ($jobPath . '/manifest.json');
if (is_file($progressPath)) {
$progress = $readJson($progressPath);
}
if (is_file($manifestPath)) {
$manifest = $readJson($manifestPath);
}
if (!$progress && !$manifest && !is_dir($jobPath)) { if (!$progress && !$manifest && !is_dir($jobPath)) {
$progress = $readJson($fallbackProgress) ?: [ $progress = $readJson($fallbackProgress) ?: [
@@ -46,37 +108,45 @@ if ($jobId !== '') {
'percent' => null, 'percent' => null,
'timestamp' => time(), 'timestamp' => time(),
]; ];
echo json_encode([
'status' => 'success',
'message' => 'Safe upgrade job not found.',
'data' => [
'job' => null,
'progress' => $progress,
],
]);
exit;
} }
} }
if ($progress === null) { if ($progress === null) {
$progress = $readJson($fallbackProgress) ?: [ if ($progressPath && is_file($progressPath)) {
'stage' => 'idle', $progress = $readJson($progressPath);
'message' => '', }
'percent' => null,
'timestamp' => time(), if ($progress === null) {
]; $progress = $readJson($fallbackProgress) ?: [
'stage' => 'idle',
'message' => '',
'percent' => null,
'timestamp' => time(),
];
$progressPath = $fallbackProgress;
}
} }
if ($jobId !== '' && is_array($progress) && !isset($progress['job_id'])) { if ($jobId !== '' && is_array($progress) && !isset($progress['job_id'])) {
$progress['job_id'] = $jobId; $progress['job_id'] = $jobId;
} }
$contextPayload = [];
if ($manifestPath) {
$contextPayload['manifest'] = $manifestPath;
}
if ($progressPath) {
$contextPayload['progress'] = $progressPath;
}
$contextToken = $contextPayload ? base64_encode(json_encode($contextPayload)) : null;
echo json_encode([ echo json_encode([
'status' => 'success', 'status' => 'success',
'data' => [ 'data' => [
'job' => $manifest ?: null, 'job' => $manifest ?: null,
'progress' => $progress, 'progress' => $progress,
'context' => $contextToken,
], ],
]); ]);

View File

@@ -54,6 +54,7 @@ export default class SafeUpgrade {
this.active = false; this.active = false;
this.jobId = null; this.jobId = null;
this.statusFailures = 0; this.statusFailures = 0;
this.statusContext = null;
this.directStatusUrl = this.resolveDirectStatusUrl(); this.directStatusUrl = this.resolveDirectStatusUrl();
this.preferDirectStatus = !!this.directStatusUrl; this.preferDirectStatus = !!this.directStatusUrl;
@@ -120,6 +121,7 @@ export default class SafeUpgrade {
this.decisions = {}; this.decisions = {};
this.statusFailures = 0; this.statusFailures = 0;
this.preferDirectStatus = !!this.directStatusUrl; this.preferDirectStatus = !!this.directStatusUrl;
this.statusContext = null;
this.renderLoading(); this.renderLoading();
this.modal.open(); this.modal.open();
this.fetchPreflight(); this.fetchPreflight();
@@ -412,6 +414,7 @@ export default class SafeUpgrade {
} }
this.statusFailures = 0; this.statusFailures = 0;
this.preferDirectStatus = !!this.directStatusUrl; this.preferDirectStatus = !!this.directStatusUrl;
this.statusContext = data.context || null;
this.beginPolling(1200); this.beginPolling(1200);
} else { } else {
this.renderResult(data); this.renderResult(data);
@@ -453,8 +456,18 @@ export default class SafeUpgrade {
resolveStatusEndpoint() { resolveStatusEndpoint() {
const useDirect = this.directStatusUrl && this.preferDirectStatus; const useDirect = this.directStatusUrl && this.preferDirectStatus;
let url = useDirect ? this.directStatusUrl : this.urls.status; let url = useDirect ? this.directStatusUrl : this.urls.status;
const params = [];
if (this.jobId) { if (this.jobId) {
url += (url.includes('?') ? '&' : '?') + `job=${encodeURIComponent(this.jobId)}`; params.push(`job=${encodeURIComponent(this.jobId)}`);
}
if (this.statusContext) {
params.push(`context=${encodeURIComponent(this.statusContext)}`);
}
if (params.length) {
url += (url.includes('?') ? '&' : '?') + params.join('&');
} }
return { return {
@@ -532,6 +545,9 @@ export default class SafeUpgrade {
} }
const payload = response.data || {}; const payload = response.data || {};
if (Object.prototype.hasOwnProperty.call(payload, 'context')) {
this.statusContext = payload.context || null;
}
const job = payload.job || {}; const job = payload.job || {};
const data = payload.progress || payload; const data = payload.progress || payload;
nextStage = data.stage || null; nextStage = data.stage || null;

View File

@@ -4610,6 +4610,7 @@ var SafeUpgrade = /*#__PURE__*/function () {
this.active = false; this.active = false;
this.jobId = null; this.jobId = null;
this.statusFailures = 0; this.statusFailures = 0;
this.statusContext = null;
this.directStatusUrl = this.resolveDirectStatusUrl(); this.directStatusUrl = this.resolveDirectStatusUrl();
this.preferDirectStatus = !!this.directStatusUrl; this.preferDirectStatus = !!this.directStatusUrl;
this.registerEvents(); this.registerEvents();
@@ -4676,6 +4677,7 @@ var SafeUpgrade = /*#__PURE__*/function () {
this.decisions = {}; this.decisions = {};
this.statusFailures = 0; this.statusFailures = 0;
this.preferDirectStatus = !!this.directStatusUrl; this.preferDirectStatus = !!this.directStatusUrl;
this.statusContext = null;
this.renderLoading(); this.renderLoading();
this.modal.open(); this.modal.open();
this.fetchPreflight(); this.fetchPreflight();
@@ -4882,6 +4884,7 @@ var SafeUpgrade = /*#__PURE__*/function () {
} }
_this4.statusFailures = 0; _this4.statusFailures = 0;
_this4.preferDirectStatus = !!_this4.directStatusUrl; _this4.preferDirectStatus = !!_this4.directStatusUrl;
_this4.statusContext = data.context || null;
_this4.beginPolling(1200); _this4.beginPolling(1200);
} else { } else {
_this4.renderResult(data); _this4.renderResult(data);
@@ -4921,8 +4924,15 @@ var SafeUpgrade = /*#__PURE__*/function () {
value: function resolveStatusEndpoint() { value: function resolveStatusEndpoint() {
var useDirect = this.directStatusUrl && this.preferDirectStatus; var useDirect = this.directStatusUrl && this.preferDirectStatus;
var url = useDirect ? this.directStatusUrl : this.urls.status; var url = useDirect ? this.directStatusUrl : this.urls.status;
var params = [];
if (this.jobId) { if (this.jobId) {
url += (url.indexOf('?') !== -1 ? '&' : '?') + "job=".concat(encodeURIComponent(this.jobId)); params.push("job=".concat(encodeURIComponent(this.jobId)));
}
if (this.statusContext) {
params.push("context=".concat(encodeURIComponent(this.statusContext)));
}
if (params.length) {
url += (url.indexOf('?') !== -1 ? '&' : '?') + params.join('&');
} }
return { return {
url: url, url: url,
@@ -5002,6 +5012,9 @@ var SafeUpgrade = /*#__PURE__*/function () {
return; return;
} }
var payload = response.data || {}; var payload = response.data || {};
if (Object.prototype.hasOwnProperty.call(payload, 'context')) {
_this6.statusContext = payload.context || null;
}
var job = payload.job || {}; var job = payload.job || {};
var data = payload.progress || payload; var data = payload.progress || payload;
nextStage = data.stage || null; nextStage = data.stage || null;