feat: add direct GitHub deployment as alternative to git pull
- New 'Deploy from GitHub' button downloads latest code directly - No git configuration required - more reliable for git-unfriendly servers - Automatically syncs changed files (skips .git, vendor, writable) - Both methods available: git pull (if configured) or direct GitHub download - Each has a clear confirmation dialog explaining the method - Handles temporary extraction and cleanup automatically This solves HMS deployment when git is not accessible to PHP process
This commit is contained in:
parent
cb4636e599
commit
f3b8e28312
|
|
@ -37,6 +37,14 @@ class SystemOps extends BaseController
|
|||
return $this->response->setJSON($result);
|
||||
}
|
||||
|
||||
public function updateDirect()
|
||||
{
|
||||
$ops = new SystemOperations();
|
||||
$result = $ops->runUpdateDirect();
|
||||
|
||||
return $this->response->setJSON($result);
|
||||
}
|
||||
|
||||
public function action()
|
||||
{
|
||||
$action = (string) $this->request->getPost('action');
|
||||
|
|
|
|||
|
|
@ -41,6 +41,160 @@ class SystemOperations
|
|||
return $status;
|
||||
}
|
||||
|
||||
public function runUpdateDirect(): array
|
||||
{
|
||||
$repoPath = ROOTPATH;
|
||||
$logFile = WRITEPATH . 'system_update.log';
|
||||
$startedAt = date('Y-m-d H:i:s');
|
||||
|
||||
$entry = [
|
||||
'timestamp' => $startedAt,
|
||||
'type' => 'update',
|
||||
'status' => 'running',
|
||||
'message' => 'HMS update started (direct deployment from GitHub)',
|
||||
];
|
||||
$this->appendHistory($entry);
|
||||
|
||||
try {
|
||||
// GitHub repo details - PUBLIC REPO, no auth needed
|
||||
$githubOwner = 'infodevsofttech-admin';
|
||||
$githubRepo = 'devsofttech_hms_ci4';
|
||||
$branch = 'main';
|
||||
|
||||
// Download latest ZIP from GitHub
|
||||
$zipUrl = "https://github.com/{$githubOwner}/{$githubRepo}/archive/refs/heads/{$branch}.zip";
|
||||
$tempDir = sys_get_temp_dir();
|
||||
$zipFile = $tempDir . '/hms_update_' . time() . '.zip';
|
||||
|
||||
file_put_contents($logFile, "Downloading from: {$zipUrl}\n", FILE_APPEND);
|
||||
|
||||
$zipContent = @file_get_contents($zipUrl);
|
||||
if ($zipContent === false) {
|
||||
throw new \Exception('Failed to download from GitHub. Check network connectivity and GitHub availability.');
|
||||
}
|
||||
|
||||
if (!file_put_contents($zipFile, $zipContent)) {
|
||||
throw new \Exception('Failed to write downloaded ZIP to temporary directory.');
|
||||
}
|
||||
|
||||
file_put_contents($logFile, "Downloaded ZIP: {$zipFile} (" . filesize($zipFile) . " bytes)\n", FILE_APPEND);
|
||||
|
||||
// Extract ZIP
|
||||
$zip = new \ZipArchive();
|
||||
if ($zip->open($zipFile) !== true) {
|
||||
throw new \Exception('Failed to open downloaded ZIP file.');
|
||||
}
|
||||
|
||||
$extractDir = $tempDir . '/hms_extract_' . time();
|
||||
if (!mkdir($extractDir)) {
|
||||
throw new \Exception('Failed to create extraction directory.');
|
||||
}
|
||||
|
||||
$zip->extractTo($extractDir);
|
||||
$zip->close();
|
||||
|
||||
// Find the extracted directory (GitHub creates a folder like "repo-main")
|
||||
$dirs = array_diff(scandir($extractDir), ['.', '..']);
|
||||
if (empty($dirs)) {
|
||||
throw new \Exception('Downloaded ZIP appears to be empty.');
|
||||
}
|
||||
|
||||
$extractedRepoDir = $extractDir . '/' . reset($dirs);
|
||||
file_put_contents($logFile, "Extracted to: {$extractedRepoDir}\n", FILE_APPEND);
|
||||
|
||||
// Sync files (skip git, vendor, and certain local files)
|
||||
$this->syncFiles($extractedRepoDir, $repoPath, $logFile);
|
||||
|
||||
// Cleanup
|
||||
$this->deleteDirectory($extractDir);
|
||||
@unlink($zipFile);
|
||||
|
||||
$successMsg = 'HMS updated successfully from GitHub (direct deployment)';
|
||||
$this->appendHistory([
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
'type' => 'update',
|
||||
'status' => 'success',
|
||||
'message' => $successMsg,
|
||||
'detail' => 'Files synced from ' . $zipUrl,
|
||||
]);
|
||||
file_put_contents($logFile, "SUCCESS: {$successMsg}\n", FILE_APPEND);
|
||||
|
||||
return ['ok' => true, 'message' => $successMsg];
|
||||
} catch (\Throwable $e) {
|
||||
$errorMsg = 'Update failed: ' . $e->getMessage();
|
||||
$this->appendHistory([
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
'type' => 'update',
|
||||
'status' => 'failed',
|
||||
'message' => 'HMS update failed',
|
||||
'detail' => $errorMsg,
|
||||
]);
|
||||
file_put_contents($logFile, "FAILED: {$errorMsg}\n", FILE_APPEND);
|
||||
return ['ok' => false, 'message' => $errorMsg];
|
||||
}
|
||||
}
|
||||
|
||||
private function syncFiles(string $sourceDir, string $destDir, string $logFile): void
|
||||
{
|
||||
$skip = ['.git', '.gitignore', 'vendor', 'node_modules', 'writable', '.env', '.env.example'];
|
||||
|
||||
$this->copyRecursive($sourceDir, $destDir, $skip, $logFile);
|
||||
file_put_contents($logFile, "File sync completed\n", FILE_APPEND);
|
||||
}
|
||||
|
||||
private function copyRecursive(string $src, string $dest, array $skip, string $logFile): void
|
||||
{
|
||||
$dir = opendir($src);
|
||||
if (!$dir) {
|
||||
throw new \Exception("Cannot open directory: {$src}");
|
||||
}
|
||||
|
||||
while (false !== ($file = readdir($dir))) {
|
||||
if ($file === '.' || $file === '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($file, $skip)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$srcPath = $src . '/' . $file;
|
||||
$destPath = $dest . '/' . $file;
|
||||
|
||||
if (is_dir($srcPath)) {
|
||||
if (!is_dir($destPath)) {
|
||||
@mkdir($destPath, 0755, true);
|
||||
}
|
||||
$this->copyRecursive($srcPath, $destPath, $skip, $logFile);
|
||||
} else {
|
||||
if (@copy($srcPath, $destPath)) {
|
||||
file_put_contents($logFile, " ✓ {$file}\n", FILE_APPEND);
|
||||
} else {
|
||||
file_put_contents($logFile, " ✗ {$file} (copy failed)\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
}
|
||||
|
||||
private function deleteDirectory(string $dir): void
|
||||
{
|
||||
if (!is_dir($dir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$files = array_diff(scandir($dir), ['.', '..']);
|
||||
foreach ($files as $file) {
|
||||
$path = $dir . '/' . $file;
|
||||
if (is_dir($path)) {
|
||||
$this->deleteDirectory($path);
|
||||
} else {
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
@rmdir($dir);
|
||||
}
|
||||
|
||||
public function runUpdate(): array
|
||||
{
|
||||
$repoPath = ROOTPATH;
|
||||
|
|
|
|||
|
|
@ -88,8 +88,9 @@
|
|||
setHealthState('healthy');
|
||||
setInterval(function(){ refreshPanel(); }, 30000);
|
||||
|
||||
$(document).off('click', '#btnUpdateSystem, #btnRestartWeb, #btnRestartPhp, #btnReboot, #btnShutdown')
|
||||
.on('click', '#btnUpdateSystem', function(){ if (!confirm('Pull latest HMS code from repository?')) return; postAction('<?= base_url('setting/admin/system-ops/update') ?>', {}, this); })
|
||||
$(document).off('click', '#btnUpdateSystem, #btnUpdateDirectGithub, #btnRestartWeb, #btnRestartPhp, #btnReboot, #btnShutdown')
|
||||
.on('click', '#btnUpdateSystem', function(){ if (!confirm('Pull latest HMS code from repository?\n\nThis method requires git to be configured on the server.')) return; postAction('<?= base_url('setting/admin/system-ops/update') ?>', {}, this); })
|
||||
.on('click', '#btnUpdateDirectGithub', function(){ if (!confirm('Deploy latest HMS code from GitHub?\n\nThis will download the latest code and update files.\nThis method is more reliable if git is not available.')) return; postAction('<?= base_url('setting/admin/system-ops/updateDirect') ?>', {}, this); })
|
||||
.on('click', '#btnRestartWeb', function(){ if (!confirm('Restart the web server now?')) return; postAction('<?= base_url('setting/admin/system-ops/action') ?>', {action: 'restart_web'}, this); })
|
||||
.on('click', '#btnRestartPhp', function(){ if (!confirm('Restart PHP-FPM now?')) return; postAction('<?= base_url('setting/admin/system-ops/action') ?>', {action: 'restart_php'}, this); })
|
||||
.on('click', '#btnReboot', function(){ if (!confirm('Reboot the server now?')) return; postAction('<?= base_url('setting/admin/system-ops/action') ?>', {action: 'reboot'}, this); })
|
||||
|
|
|
|||
|
|
@ -44,7 +44,11 @@
|
|||
<h5 class="mb-0">Maintenance Actions</h5>
|
||||
</div>
|
||||
<div class="card-body d-grid gap-2">
|
||||
<button class="btn btn-primary" id="btnUpdateSystem">Update HMS</button>
|
||||
<div class="d-grid gap-2">
|
||||
<button class="btn btn-primary" id="btnUpdateSystem" title="Update via git (requires git configuration)">Update HMS (Git)</button>
|
||||
<button class="btn btn-success" id="btnUpdateDirectGithub" title="Download and deploy latest from GitHub (more reliable)">Deploy from GitHub</button>
|
||||
</div>
|
||||
<hr class="my-2">
|
||||
<button class="btn btn-outline-warning" id="btnRestartWeb">Restart Web Server</button>
|
||||
<button class="btn btn-outline-info" id="btnRestartPhp">Restart PHP-FPM</button>
|
||||
<button class="btn btn-outline-danger" id="btnShutdown">Shutdown Server</button>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user