mirror of
https://github.com/itflow-org/itflow
synced 2026-08-05 07:07:14 +00:00
Rework Update UI and fix banch
This commit is contained in:
@@ -69,6 +69,10 @@ Only this release needs the command line for the database update. Normal updates
|
||||
- Settings > Ticketing: the email-to-ticket hint no longer names a cron script that has not existed for several releases, and points at Maintenance > Cron instead.
|
||||
- Setup: the command line installer now prints the crontab entry and the reminder to turn cron on, the same two steps the web installer shows on its finish page.
|
||||
- Removed the unused `config_invoice_overdue_reminders` setting. It was seeded on install and read in two places but never actually used by anything; the overdue reminder schedule is fixed in the nightly job.
|
||||
- Update page: reworked. Release, tracked branch, database version and code commit are now shown whatever state the install is in, instead of only when it is already up to date, and a pending database update no longer hides a pending application update — both appear, in the order they need doing. Pending commits are listed in a readable table rather than a centred one, and the backup warning is stated once instead of twice.
|
||||
- Update page: **Force Update no longer resets to `origin/master` regardless of the branch you track.** On an install following any other branch it silently moved the files onto master and discarded the code that was actually running. It now resets to the branch in `$repo_branch`, and so does `update_cli.php --force_update`. Installs with no `$repo_branch` in `config.php` still get master, as before.
|
||||
- Update page: the database update is now offered using a proper version comparison. The old string comparison would have stopped offering updates once a version reached a two-digit part — `2.6.10` sorts below `2.6.9` as plain text.
|
||||
- Update page: Git output — commit subjects and error text — is escaped before it reaches the page, and the branch name is escaped before it reaches a shell command. The page also no longer runs a second `git fetch` just to read the error message from the first one.
|
||||
- Cron: the nightly run is safe to repeat. Late fees, overdue invoice reminders and autopay retries now apply at most once per invoice per day, so a Run Now after the scheduled pass no longer stacks fees or re-emails clients. Nightly Tasks only accepts the daily schedule.
|
||||
- Ticket SLAs, optional throughout. An SLA sets a response target and an optional resolution target, assigned per client and priority with a global default and an explicit "no SLA" override. Targets are measured against your business hours. Tickets show time remaining and turn yellow at a configurable warning threshold and red on breach, on both the ticket list and the kanban board, and can be filtered by SLA state. Nominated statuses pause the resolution clock for "waiting on customer", preserving the remaining budget. Two new reports, SLA Summary and SLA by Client. With no assignments defined nothing behaves any differently.
|
||||
- Ticket: added an Urgent priority.
|
||||
|
||||
@@ -8,11 +8,17 @@ if (isset($_GET['update'])) {
|
||||
|
||||
enforceAdminPermission();
|
||||
|
||||
//git fetch downloads the latest from remote without trying to merge or rebase anything. Then the git reset resets the master branch to what you just fetched. The --hard option changes all the files in your working tree to match the files in origin/master
|
||||
|
||||
// git fetch downloads the latest from the remote without merging or rebasing anything.
|
||||
// The hard reset then throws away every local change and makes the working tree match
|
||||
// the tracked branch exactly.
|
||||
//
|
||||
// That reset used to name origin/master outright, so a force update on an install
|
||||
// tracking any other branch silently moved it onto master and discarded the code it was
|
||||
// actually running.
|
||||
if (isset($_GET['force_update']) == 1) {
|
||||
$remote_ref = escapeshellarg("origin/" . getRepoBranch());
|
||||
exec("git fetch --all");
|
||||
exec("git reset --hard origin/master");
|
||||
exec("git reset --hard $remote_ref");
|
||||
} else {
|
||||
exec("git pull");
|
||||
}
|
||||
|
||||
247
admin/update.php
247
admin/update.php
@@ -3,106 +3,197 @@ require_once "includes/inc_all_admin.php";
|
||||
|
||||
require_once "../includes/database_version.php";
|
||||
|
||||
$repo_branch = getRepoBranch();
|
||||
$remote_ref = escapeshellarg("origin/$repo_branch");
|
||||
|
||||
$updates = checkForUpdates();
|
||||
|
||||
$latest_version = $updates->latest_version;
|
||||
$current_version = $updates->current_version;
|
||||
$result = $updates->result;
|
||||
$fetch_ok = $updates->result === 0;
|
||||
|
||||
$git_log = shell_exec("git log $repo_branch..origin/$repo_branch --pretty=format:'<tr><td>%h</td><td>%ar</td><td>%s</td></tr>'");
|
||||
// Commits sitting between this working tree and the remote branch. Fields are separated
|
||||
// by \x1f rather than having git build the table markup, because a commit subject comes
|
||||
// from outside this install and used to reach the page as unescaped HTML.
|
||||
$pending_commits = [];
|
||||
|
||||
$git_log = shell_exec("git log HEAD..$remote_ref --pretty=format:'%h%x1f%ar%x1f%s'");
|
||||
|
||||
foreach (explode("\n", trim((string) $git_log)) as $commit_line) {
|
||||
|
||||
if ($commit_line === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$commit_fields = explode("\x1f", $commit_line, 3);
|
||||
|
||||
if (count($commit_fields) === 3) {
|
||||
$pending_commits[] = $commit_fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// version_compare, not > - "2.6.10" is less than "2.6.9" as a plain string comparison, so
|
||||
// the plain comparison silently stops offering database updates once a minor reaches 10.
|
||||
$db_update_available = version_compare(LATEST_DATABASE_VERSION, CURRENT_DATABASE_VERSION, '>');
|
||||
$app_update_available = !empty($pending_commits);
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-3">
|
||||
<h3 class="card-title"><i class="fas fa-fw fa-download mr-2"></i>Update</h3>
|
||||
</div>
|
||||
<div class="card-body" style="text-align: center;">
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-3">
|
||||
<h3 class="card-title"><i class="fas fa-fw fa-download mr-2"></i>Update</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<!-- Check if git fetch result was successful (0), if not show a warning -->
|
||||
<?php if ($result !== 0) { ?>
|
||||
<div class="alert alert-danger">
|
||||
<strong>WARNING: Could not find execute 'git fetch'.</strong>
|
||||
<br><br>
|
||||
<i>Error details:- <?= shell_exec("git fetch 2>&1") ?></i>
|
||||
<br>
|
||||
<br>Things to check: Is Git installed? Is the Git origin/remote correct? Are web server file permissions too strict?
|
||||
<br>Seek support on the <a href="https://forum.itflow.org">Forum</a> if required - include relevant PHP error logs & ITFlow debug output
|
||||
<?php if (!$fetch_ok) { ?>
|
||||
<div class="alert alert-danger">
|
||||
<h5><i class="fas fa-fw fa-exclamation-triangle mr-2"></i>Cannot reach the Git remote</h5>
|
||||
ITFlow updates itself with Git, so nothing below is current until this is fixed.
|
||||
<?php if (!empty($updates->output)) { ?>
|
||||
<pre class="bg-dark text-white p-2 mt-2 mb-2"><?= escapeHtml(implode("\n", $updates->output)) ?></pre>
|
||||
<?php } ?>
|
||||
Check that Git is installed, that the remote is reachable from this server, and that the web server
|
||||
user can write to the ITFlow directory. The
|
||||
<a href="https://forum.itflow.org" class="alert-link" target="_blank">forum</a> can help - include
|
||||
your PHP error log and the output above.
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-6 mb-3">
|
||||
<small class="text-secondary text-uppercase">Release</small>
|
||||
<div class="h5 mb-0"><?= escapeHtml(APP_VERSION) ?></div>
|
||||
</div>
|
||||
<div class="col-md-3 col-6 mb-3">
|
||||
<small class="text-secondary text-uppercase">Branch</small>
|
||||
<div class="h5 mb-0"><?= escapeHtml($repo_branch) ?></div>
|
||||
<?php if ($repo_branch !== 'master') { ?>
|
||||
<small class="text-warning">Not the release branch</small>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<div class="col-md-3 col-6 mb-3">
|
||||
<small class="text-secondary text-uppercase">Database</small>
|
||||
<div class="h5 mb-0">
|
||||
<?= escapeHtml(CURRENT_DATABASE_VERSION) ?>
|
||||
<?php if ($db_update_available) { ?>
|
||||
<small class="text-danger">→ <?= escapeHtml(LATEST_DATABASE_VERSION) ?></small>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-6 mb-3">
|
||||
<small class="text-secondary text-uppercase">Commit</small>
|
||||
<div class="h5 mb-0"><code><?= escapeHtml(substr((string) $current_version, 0, 7)) ?></code></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<?php if (!$app_update_available && !$db_update_available) { ?>
|
||||
|
||||
<div class="text-center py-3">
|
||||
<i class="far fa-3x fa-smile-wink text-dark"></i>
|
||||
<p class="mt-3 mb-0"><strong>You are up to date</strong></p>
|
||||
<p class="text-muted">Everything is going to be alright.</p>
|
||||
</div>
|
||||
|
||||
<?php if (rand(1, 10) == 1) { ?>
|
||||
<div class="alert alert-info alert-dismissible fade show mb-0" role="alert">
|
||||
You are up to date, but when did you last check that your backup restores?
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { ?>
|
||||
<div class="alert alert-danger">
|
||||
<h1 class="font-weight-bold text-center">⚠️ DANGER ⚠️</h1>
|
||||
<h2 class="font-weight-bold text-center">Do NOT run updates without first taking a backup</h2>
|
||||
<p>VM Snapshots are highly recommended over other methods - see the <a href="https://docs.itflow.org/backups" class="alert-link" target="_blank">docs</a>. Review the <a href="https://github.com/itflow-org/itflow/blob/master/CHANGELOG.md" class="alert-link" target="_blank">changelog</a> for breaking changes that may require manual remediation.</p>
|
||||
<p class="text-center font-weight-bold">Ignore this warning at your own risk.</p>
|
||||
<?php } else { ?>
|
||||
|
||||
<div class="alert alert-danger">
|
||||
<h5><i class="fas fa-fw fa-exclamation-triangle mr-2"></i>Do not update without a backup</h5>
|
||||
A VM snapshot is the safest option - other methods are covered in the
|
||||
<a href="https://docs.itflow.org/backups" class="alert-link" target="_blank">docs</a>. Read the
|
||||
<a href="https://github.com/itflow-org/itflow/blob/master/CHANGELOG.md" class="alert-link" target="_blank">changelog</a>
|
||||
first: some releases need manual steps, and this page will not do them for you.
|
||||
</div>
|
||||
|
||||
<?php if ($app_update_available && $db_update_available) { ?>
|
||||
<p class="text-muted">
|
||||
<i class="fas fa-fw fa-info-circle mr-1"></i>Both are pending. Update the application files first -
|
||||
they bring the database migrations that the second step then applies.
|
||||
</p>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($app_update_available) { ?>
|
||||
<div class="mb-4">
|
||||
<h6 class="text-uppercase text-secondary">Application files</h6>
|
||||
<p class="mb-2">
|
||||
<?= count($pending_commits) ?> commit<?= count($pending_commits) === 1 ? '' : 's' ?>
|
||||
behind <code><?= escapeHtml("origin/$repo_branch") ?></code>.
|
||||
</p>
|
||||
<a class="btn btn-primary confirm-link" href="post.php?update&csrf_token=<?= $_SESSION['csrf_token'] ?>">
|
||||
<i class="fas fa-fw fa-download mr-2"></i>Update App
|
||||
</a>
|
||||
<a class="btn btn-outline-danger ml-2 confirm-link" href="post.php?update&force_update=1&csrf_token=<?= $_SESSION['csrf_token'] ?>">
|
||||
<i class="fas fa-fw fa-hammer mr-2"></i>Force Update
|
||||
</a>
|
||||
<p class="text-muted mt-2 mb-0">
|
||||
<small>
|
||||
Update App runs <code>git pull</code>. Force Update discards every local change and resets
|
||||
the files to <code><?= escapeHtml("origin/$repo_branch") ?></code> - use it only when a
|
||||
normal update will not apply.
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
<br>
|
||||
<a class="btn btn-dark btn-lg my-4" href="post.php?update_db&csrf_token=<?= $_SESSION['csrf_token'] ?>"><i class="fas fa-fw fa-4x fa-download mb-1"></i><h5>Update Database</h5></a>
|
||||
<br>
|
||||
<small class="text-secondary">Current DB Version: <?= CURRENT_DATABASE_VERSION ?></small>
|
||||
<br>
|
||||
<small class="text-secondary">Latest DB Version: <?= LATEST_DATABASE_VERSION ?></small>
|
||||
<br>
|
||||
<hr>
|
||||
<?php } ?>
|
||||
|
||||
<?php } else {
|
||||
if (!empty($git_log)) { ?>
|
||||
<div class="alert alert-danger">
|
||||
<h1 class="font-weight-bold text-center">⚠️ DANGER ⚠️</h1>
|
||||
<h2 class="font-weight-bold text-center">Do NOT run updates without first taking a backup</h2>
|
||||
<p>VM Snapshots are highly recommended over other methods - see the <a href="https://docs.itflow.org/backups" class="alert-link" target="_blank">docs</a>. Review the <a href="https://github.com/itflow-org/itflow/blob/master/CHANGELOG.md" class="alert-link" target="_blank">changelog</a> for breaking changes that may require manual remediation.</p>
|
||||
<p class="text-center font-weight-bold">Ignore this warning at your own risk.</p>
|
||||
</div>
|
||||
<?php if ($db_update_available) { ?>
|
||||
<div class="mb-4">
|
||||
<h6 class="text-uppercase text-secondary">Database</h6>
|
||||
<p class="mb-2">
|
||||
Schema is at <strong><?= escapeHtml(CURRENT_DATABASE_VERSION) ?></strong> and this code expects
|
||||
<strong><?= escapeHtml(LATEST_DATABASE_VERSION) ?></strong>. Parts of the app will error until
|
||||
this is applied.
|
||||
</p>
|
||||
<a class="btn btn-dark confirm-link" href="post.php?update_db&csrf_token=<?= $_SESSION['csrf_token'] ?>">
|
||||
<i class="fas fa-fw fa-database mr-2"></i>Update Database
|
||||
</a>
|
||||
<p class="text-muted mt-2 mb-0">
|
||||
<small>
|
||||
A large instance can take a minute or more. If it fails part way it stops without advancing
|
||||
the recorded version, so it is safe to run again after fixing the cause.
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<a class="btn btn-primary btn-lg my-4 confirm-link" href="post.php?update&csrf_token=<?= $_SESSION['csrf_token'] ?>"><i class="fas fa-fw fa-4x fa-download mb-1"></i><h5>Update App</h5></a>
|
||||
<a class="btn btn-danger btn-lg confirm-link" href="post.php?update&force_update=1&csrf_token=<?= $_SESSION['csrf_token'] ?>"><i class="fas fa-fw fa-4x fa-hammer mb-1"></i><h5>FORCE Update App</h5></a>
|
||||
<?php } ?>
|
||||
|
||||
<?php } else { ?>
|
||||
<p><strong>Application Release Version:<br><strong class="text-dark"><?= APP_VERSION ?></strong></p>
|
||||
<p class="text-secondary">Database Version:<br><strong class="text-dark"><?= CURRENT_DATABASE_VERSION ?></strong></p>
|
||||
<p class="text-secondary">Code Commit:<br><strong class="text-dark"><?= $current_version ?></strong></p>
|
||||
<p class="text-muted">You are up to date!<br>Everything is going to be alright</p>
|
||||
<i class="far fa-3x text-dark fa-smile-wink"></i><br>
|
||||
|
||||
<?php if (rand(1,10) == 1) { ?>
|
||||
<br>
|
||||
<div class="alert alert-info alert-dismissible fade show" role="alert">
|
||||
You're up to date, but when was the last time you checked your ITFlow backup works?
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php }
|
||||
}
|
||||
|
||||
if (!empty($git_log)) { ?>
|
||||
<table class="table ">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Commit</th>
|
||||
<th>When</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<?php if ($app_update_available) { ?>
|
||||
<h6 class="text-uppercase text-secondary mt-4">Pending commits</h6>
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-borderless table-hover">
|
||||
<thead class="text-secondary">
|
||||
<tr>
|
||||
<th>Commit</th>
|
||||
<th>When</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
echo $git_log;
|
||||
?>
|
||||
<?php foreach ($pending_commits as $commit) { ?>
|
||||
<tr>
|
||||
<td><code><?= escapeHtml($commit[0]) ?></code></td>
|
||||
<td class="text-nowrap"><?= escapeHtml($commit[1]) ?></td>
|
||||
<td><?= escapeHtml($commit[2]) ?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
require_once "../includes/footer.php";
|
||||
|
||||
|
||||
@@ -292,13 +292,29 @@ function displayFolderOptions($parent_folder_id, $client_id, $indent = 0) {
|
||||
}
|
||||
}
|
||||
|
||||
function checkForUpdates() {
|
||||
|
||||
/*
|
||||
* The branch this install tracks. Both setup paths write $repo_branch into config.php, but
|
||||
* an install older than that has no value at all, and every caller puts it into a shell
|
||||
* command - so it is defaulted and escaped here rather than trusted at each call site.
|
||||
*/
|
||||
function getRepoBranch(): string
|
||||
{
|
||||
global $repo_branch;
|
||||
|
||||
// Fetch the latest code changes but don't apply them
|
||||
exec("git fetch", $output, $result);
|
||||
$latest_version = exec("git rev-parse origin/$repo_branch");
|
||||
$branch = trim((string) ($repo_branch ?? ''));
|
||||
|
||||
return $branch === '' ? 'master' : $branch;
|
||||
}
|
||||
|
||||
function checkForUpdates() {
|
||||
|
||||
$remote_ref = escapeshellarg("origin/" . getRepoBranch());
|
||||
|
||||
// Fetch the latest code changes but don't apply them. stderr is merged in because git
|
||||
// reports failures there, and it is the only thing the update page can show when this
|
||||
// breaks - it used to run a second git fetch of its own just to get the message.
|
||||
exec("git fetch 2>&1", $output, $result);
|
||||
$latest_version = exec("git rev-parse $remote_ref");
|
||||
$current_version = exec("git rev-parse HEAD");
|
||||
|
||||
if ($current_version == $latest_version) {
|
||||
|
||||
@@ -29,7 +29,7 @@ function printHelp() {
|
||||
echo "Options:\n";
|
||||
echo " --help Show this help message.\n";
|
||||
echo " --update Perform a git pull to update the application.\n";
|
||||
echo " --force_update Perform a git fetch and hard reset to origin/master.\n";
|
||||
echo " --force_update Perform a git fetch and hard reset to the branch this install tracks.\n";
|
||||
echo " --update_db Update the database structure to the latest version.\n";
|
||||
echo "\nIf no options are provided, a standard update (git pull) is performed.\n";
|
||||
}
|
||||
@@ -82,9 +82,11 @@ if (count($options) === 0) {
|
||||
// If "update" or "force_update" is requested
|
||||
if (isset($options['update']) || isset($options['force_update'])) {
|
||||
if (isset($options['force_update'])) {
|
||||
// Perform a hard reset
|
||||
// Perform a hard reset onto the tracked branch. This named origin/master outright
|
||||
// until 26.08, which moved any install tracking another branch onto master.
|
||||
$remote_ref = escapeshellarg("origin/" . getRepoBranch());
|
||||
exec("git fetch --all 2>&1", $output, $return_var);
|
||||
exec("git reset --hard origin/master 2>&1", $output2, $return_var2);
|
||||
exec("git reset --hard $remote_ref 2>&1", $output2, $return_var2);
|
||||
echo implode("\n", $output) . "\n" . implode("\n", $output2) . "\n";
|
||||
} else {
|
||||
// Perform a standard update (git pull)
|
||||
|
||||
Reference in New Issue
Block a user