feat: show New Update Available badge when GitHub has newer commits
- Add checkForUpdates() with 10-minute cached GitHub API check - Add fetchLatestCommitInfo() to get latest commit SHA + message from GitHub API - Save deployed commit SHA to writable/deployed_commit.txt after each deploy - Invalidate update cache on successful deploy so next check is always fresh - Button turns yellow + red NEW badge when update available - Show commit message and short SHA diff (deployed → latest) in alert - Show green 'Up to date' with deployed SHA when already current - GitHub API rate limit safe: checks once per 10 mins via file cache
This commit is contained in:
parent
41f07357d2
commit
dcc366444b
|
|
@ -7,10 +7,19 @@ use RuntimeException;
|
|||
class SystemOperations
|
||||
{
|
||||
private string $historyPath;
|
||||
private string $deployedShaPath;
|
||||
private string $updateCachePath;
|
||||
|
||||
private const GITHUB_OWNER = 'infodevsofttech-admin';
|
||||
private const GITHUB_REPO = 'devsofttech_hms_ci4';
|
||||
private const GITHUB_BRANCH = 'main';
|
||||
private const UPDATE_CACHE_TTL = 600; // 10 minutes
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->historyPath = WRITEPATH . 'system_update_history.json';
|
||||
$this->historyPath = WRITEPATH . 'system_update_history.json';
|
||||
$this->deployedShaPath = WRITEPATH . 'deployed_commit.txt';
|
||||
$this->updateCachePath = WRITEPATH . 'github_update_cache.json';
|
||||
}
|
||||
|
||||
public function collectStatus(): array
|
||||
|
|
@ -35,8 +44,9 @@ class SystemOperations
|
|||
$status['network'] = $this->readNetworkStatus();
|
||||
$status['internet'] = $this->readInternetStatus();
|
||||
$status['services'] = $this->readServiceStatus();
|
||||
$status['raid'] = $this->readRaidStatus();
|
||||
$status['last_update'] = $this->getLatestHistoryEntry();
|
||||
$status['raid'] = $this->readRaidStatus();
|
||||
$status['last_update'] = $this->getLatestHistoryEntry();
|
||||
$status['update_check'] = $this->checkForUpdates();
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
|
@ -53,12 +63,12 @@ class SystemOperations
|
|||
$this->purgeStaleRunning();
|
||||
|
||||
try {
|
||||
$githubOwner = 'infodevsofttech-admin';
|
||||
$githubRepo = 'devsofttech_hms_ci4';
|
||||
$branch = 'main';
|
||||
$zipUrl = "https://github.com/{$githubOwner}/{$githubRepo}/archive/refs/heads/{$branch}.zip";
|
||||
$tempDir = sys_get_temp_dir();
|
||||
$zipFile = $tempDir . '/hms_update_' . time() . '.zip';
|
||||
$zipUrl = "https://github.com/" . self::GITHUB_OWNER . "/" . self::GITHUB_REPO . "/archive/refs/heads/" . self::GITHUB_BRANCH . ".zip";
|
||||
$tempDir = sys_get_temp_dir();
|
||||
$zipFile = $tempDir . '/hms_update_' . time() . '.zip';
|
||||
|
||||
// Fetch latest commit info for tracking (fail-safe, non-blocking)
|
||||
$latestInfo = $this->fetchLatestCommitInfo();
|
||||
|
||||
file_put_contents($logFile, '[' . date('Y-m-d H:i:s') . "] Downloading: {$zipUrl}\n", FILE_APPEND);
|
||||
|
||||
|
|
@ -95,15 +105,22 @@ class SystemOperations
|
|||
@unlink($zipFile);
|
||||
|
||||
$successMsg = 'HMS updated successfully from GitHub (direct deployment)';
|
||||
$shaLine = !empty($latestInfo['sha']) ? "\nCommit: " . substr($latestInfo['sha'], 0, 7) . ($latestInfo['message'] ? ' — ' . $latestInfo['message'] : '') : '';
|
||||
$this->appendHistory([
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
'type' => 'update',
|
||||
'status' => 'success',
|
||||
'message' => $successMsg,
|
||||
'detail' => "Downloaded: {$zipSize}\nSource: {$zipUrl}\nCompleted: " . date('Y-m-d H:i:s'),
|
||||
'detail' => "Downloaded: {$zipSize}\nSource: {$zipUrl}{$shaLine}\nCompleted: " . date('Y-m-d H:i:s'),
|
||||
]);
|
||||
file_put_contents($logFile, '[' . date('Y-m-d H:i:s') . "] SUCCESS: {$successMsg}\n", FILE_APPEND);
|
||||
|
||||
// Save deployed SHA so update checker knows what version is live
|
||||
if (!empty($latestInfo['sha'])) {
|
||||
file_put_contents($this->deployedShaPath, $latestInfo['sha']);
|
||||
}
|
||||
@unlink($this->updateCachePath); // invalidate cache so next check is fresh
|
||||
|
||||
return ['ok' => true, 'message' => $successMsg];
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
|
@ -386,6 +403,85 @@ class SystemOperations
|
|||
return $info;
|
||||
}
|
||||
|
||||
public function checkForUpdates(): array
|
||||
{
|
||||
$result = [
|
||||
'has_update' => false,
|
||||
'deployed_sha' => '',
|
||||
'latest_sha' => '',
|
||||
'latest_message' => '',
|
||||
'latest_date' => '',
|
||||
'error' => '',
|
||||
];
|
||||
|
||||
// Read cached result if fresh enough
|
||||
if (file_exists($this->updateCachePath)) {
|
||||
$cached = @json_decode((string) file_get_contents($this->updateCachePath), true);
|
||||
if (is_array($cached) && isset($cached['fetched_at']) && (time() - (int)$cached['fetched_at']) < self::UPDATE_CACHE_TTL) {
|
||||
$result['latest_sha'] = $cached['sha'] ?? '';
|
||||
$result['latest_message'] = $cached['message'] ?? '';
|
||||
$result['latest_date'] = $cached['date'] ?? '';
|
||||
$result['deployed_sha'] = $this->getDeployedSha();
|
||||
$result['has_update'] = $result['latest_sha'] !== '' && $result['latest_sha'] !== $result['deployed_sha'];
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
$info = $this->fetchLatestCommitInfo();
|
||||
if (!empty($info['error'])) {
|
||||
$result['error'] = $info['error'];
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Cache the result
|
||||
file_put_contents($this->updateCachePath, json_encode([
|
||||
'fetched_at' => time(),
|
||||
'sha' => $info['sha'],
|
||||
'message' => $info['message'],
|
||||
'date' => $info['date'],
|
||||
]));
|
||||
|
||||
$result['latest_sha'] = $info['sha'];
|
||||
$result['latest_message'] = $info['message'];
|
||||
$result['latest_date'] = $info['date'];
|
||||
$result['deployed_sha'] = $this->getDeployedSha();
|
||||
$result['has_update'] = $result['latest_sha'] !== '' && $result['latest_sha'] !== $result['deployed_sha'];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function fetchLatestCommitInfo(): array
|
||||
{
|
||||
$apiUrl = "https://api.github.com/repos/" . self::GITHUB_OWNER . "/" . self::GITHUB_REPO . "/commits/" . self::GITHUB_BRANCH;
|
||||
$ctx = stream_context_create(['http' => [
|
||||
'timeout' => 8,
|
||||
'header' => "User-Agent: HMS-CI4-Updater/1.0\r\nAccept: application/vnd.github.v3+json\r\n",
|
||||
'ignore_errors' => true,
|
||||
]]);
|
||||
$body = @file_get_contents($apiUrl, false, $ctx);
|
||||
if ($body === false) {
|
||||
return ['sha' => '', 'message' => '', 'date' => '', 'error' => 'Could not reach GitHub API'];
|
||||
}
|
||||
$data = json_decode($body, true);
|
||||
if (!is_array($data) || empty($data['sha'])) {
|
||||
return ['sha' => '', 'message' => '', 'date' => '', 'error' => 'Unexpected GitHub API response'];
|
||||
}
|
||||
$message = substr((string)($data['commit']['message'] ?? ''), 0, 72);
|
||||
$message = strtok($message, "\n"); // first line only
|
||||
return [
|
||||
'sha' => (string)$data['sha'],
|
||||
'message' => (string)$message,
|
||||
'date' => (string)($data['commit']['committer']['date'] ?? ''),
|
||||
'error' => '',
|
||||
];
|
||||
}
|
||||
|
||||
private function getDeployedSha(): string
|
||||
{
|
||||
if (!file_exists($this->deployedShaPath)) return '';
|
||||
return trim((string)@file_get_contents($this->deployedShaPath));
|
||||
}
|
||||
|
||||
public function getHistory(): array
|
||||
{
|
||||
if (! file_exists($this->historyPath)) {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,33 @@
|
|||
<h5 class="mb-0">Maintenance Actions</h5>
|
||||
</div>
|
||||
<div class="card-body d-grid gap-2">
|
||||
<button class="btn btn-success" id="btnUpdateDirectGithub" title="Download and deploy latest from GitHub">Deploy from GitHub</button>
|
||||
<?php
|
||||
$uc = $status['update_check'] ?? [];
|
||||
$hasUpdate = !empty($uc['has_update']);
|
||||
$latestMsg = esc($uc['latest_message'] ?? '');
|
||||
$latestSha = substr($uc['latest_sha'] ?? '', 0, 7);
|
||||
$deployedSha = substr($uc['deployed_sha'] ?? '', 0, 7);
|
||||
?>
|
||||
<?php if ($hasUpdate): ?>
|
||||
<div class="alert alert-warning py-2 px-3 mb-1 d-flex align-items-center gap-2" style="font-size:13px;">
|
||||
<i class="bi bi-arrow-up-circle-fill text-warning fs-5"></i>
|
||||
<div>
|
||||
<strong>New update available!</strong>
|
||||
<?php if ($latestMsg): ?><br><span class="text-muted"><?= $latestMsg ?></span><?php endif; ?>
|
||||
<?php if ($latestSha): ?><br><code><?= esc($deployedSha ?: 'none') ?> → <?= esc($latestSha) ?></code><?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif (empty($uc['error']) && $deployedSha): ?>
|
||||
<div class="text-success small mb-1 d-flex align-items-center gap-1">
|
||||
<i class="bi bi-check-circle-fill"></i> Up to date <code class="ms-1"><?= esc($deployedSha) ?></code>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<button class="btn <?= $hasUpdate ? 'btn-warning' : 'btn-success' ?> position-relative" id="btnUpdateDirectGithub" title="Download and deploy latest from GitHub">
|
||||
<i class="bi bi-cloud-download me-1"></i>Deploy from GitHub
|
||||
<?php if ($hasUpdate): ?>
|
||||
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger" style="font-size:10px;">NEW</span>
|
||||
<?php endif; ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user