Implemented Check for updates which removs the last shell exec from web ui, and reimplemented db_update switch to update_cli.php

This commit is contained in:
johnnyq
2026-08-26 23:38:23 -04:00
parent 14c38b9814
commit 069f587c1d
13 changed files with 573 additions and 132 deletions

View File

@@ -1156,7 +1156,7 @@ while ($row = mysqli_fetch_assoc($sql_invalid_recurring_expenses)) {
if ($config_telemetry > 0 || $config_telemetry == 2) {
$current_version = exec("git rev-parse HEAD");
$current_version = gitCurrentCommit();
// Client Count
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('client_id') AS num FROM clients"));
@@ -1403,13 +1403,23 @@ if ($config_telemetry > 0 || $config_telemetry == 2) {
// Fetch Updates
$updates = checkForUpdates();
/*
* The check itself is cron/update_check.php now - read what it stored rather than running a
* second git fetch here. Nothing to say until that job has run once, which is the same
* position this was in when a fetch failed.
*/
if (settingsColumnExists($mysqli, 'config_update_latest_commit')) {
$update_message = $updates->update_message;
$update_check_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT config_update_latest_commit FROM settings WHERE company_id = 1"));
$latest_version = (string) ($update_check_row['config_update_latest_commit'] ?? '');
$current_version = gitCurrentCommit();
if ($latest_version !== '' && $current_version !== '' && $latest_version !== $current_version) {
// Send Alert to inform Updates Available
appNotify("Update", "New Updates are Available [$latest_version]", "/admin/update.php");
}
if ($updates->current_version !== $updates->latest_version) {
// Send Alert to inform Updates Available
appNotify("Update", "$update_message", "/admin/update.php");
}

81
cron/update_check.php Normal file
View File

@@ -0,0 +1,81 @@
<?php
/*
* ITFlow - Update check job
*
* Asks the git remote whether a newer release exists and stores the answer. Nothing in the
* web tier shells out any more, so this is the only place the question gets asked: the
* Update page and the nightly notification both read what this job wrote.
*
* Check Now on Maintenance > Update sets cron_job_run_now on this job rather than checking
* inside the request - same mechanism as Queue Update, and the page shows "Checking" until
* the dispatcher consumes the flag.
*
* Safe to run twice: it reads and overwrites, and changes nothing outside the three settings
* columns it owns.
*/
// Set working directory to the directory this cron script lives at.
chdir(dirname(__FILE__));
// Ensure we're running from command line
if (php_sapi_name() !== 'cli') {
die("This script must be run from the command line.\n");
}
// Prevent overlapping runs of this script
$cron_lock_script = __FILE__;
require_once "includes/cron_lock.php";
require_once "../config.php";
// Set Timezone
require_once "../includes/inc_set_timezone.php";
require_once "../functions.php";
$update_check_settings = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT config_enable_cron FROM settings WHERE company_id = 1"));
$config_enable_cron = intval($update_check_settings['config_enable_cron'] ?? 0);
if ($config_enable_cron == 0) {
cronJobStop("Cron: is not enabled\n");
}
// An install whose files are newer than its schema reaches this job before the migration
// that adds the columns has run. Asking for them would throw
if (!settingsColumnExists($mysqli, 'config_update_latest_commit')) {
cronJobStop("The database update has not been applied yet\n");
}
// A zip-drop install has no remote to ask. Not a failure - there is simply nothing to check
if (!file_exists("../.git")) {
cronJobStop("Not a git checkout - there is no remote to check against\n");
}
if (!shellCommandsAvailable()) {
cronJobStop("PHP on the command line cannot run git (exec is disabled), so the check cannot run\n");
}
$updates = checkForUpdates();
/*
* Thrown rather than stopped: a fetch that fails is something to look at, and the dispatcher
* records a thrown message against the job so it shows on both Maintenance > Cron and the
* Update page. The last three lines, not the last one - git's final line is often the
* suggested fix rather than the problem.
*/
if ($updates->result !== 0) {
$update_check_error = trim(implode(' | ', array_slice($updates->output, -3)));
throw new Exception("git fetch failed: " . ($update_check_error === '' ? 'no output' : $update_check_error));
}
$update_check_latest = escapeSql($updates->latest_version);
$update_check_commits = escapeSql((string) json_encode($updates->pending_commits));
mysqli_query($mysqli, "UPDATE settings SET
config_update_latest_commit = '$update_check_latest',
config_update_pending_commits = '$update_check_commits',
config_update_checked_at = '" . date('Y-m-d H:i:s') . "'
WHERE company_id = 1");
echo count($updates->pending_commits) . " commit(s) behind the remote\n";