mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-20 18:43:20 +00:00
Say what the update check found, not just that it ran
The Scheduler screen printed "Check for updates · Succeeded · —" and left it there. What it found — the whole reason that job exists — was in the settings, which that screen never read. Somebody opening it to ask "is there a new version?" got the answer to "did the job run?" The Message column now carries "Up to date" or the version that is waiting. A failure's own message still wins: what the last successful run found is not the answer to why this one broke. Joined at render time rather than recorded by the command, because Laravel's scheduler fires its finished event after the command returns and overwrites whatever the command wrote — which is exactly why that column was empty in the first place. Reading the settings instead also keeps the line true when the new Check now button did the work rather than the nightly run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,9 @@ namespace App\Modules\Platform\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Modules\Platform\Scheduling\ScheduledTaskRun;
|
||||
use App\Modules\Platform\Settings\Setting;
|
||||
use App\Modules\Platform\Settings\Settings;
|
||||
use App\Modules\Platform\Updates\LatestReleaseInfo;
|
||||
use App\Support\Pagination;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -48,13 +51,16 @@ class SchedulerMonitoringController extends Controller
|
||||
|
||||
public function __construct(
|
||||
private readonly FailedJobProviderInterface $failer,
|
||||
private readonly LatestReleaseInfo $latestRelease,
|
||||
private readonly Settings $settings,
|
||||
) {}
|
||||
|
||||
public function index(Request $request): Response|RedirectResponse
|
||||
{
|
||||
$runs = ScheduledTaskRun::query()->get()->keyBy('command');
|
||||
$details = $this->details();
|
||||
|
||||
$tasks = collect(self::KNOWN_COMMANDS)->map(function (string $label, string $command) use ($runs): array {
|
||||
$tasks = collect(self::KNOWN_COMMANDS)->map(function (string $label, string $command) use ($runs, $details): array {
|
||||
$run = $runs->get($command);
|
||||
|
||||
return [
|
||||
@@ -62,6 +68,7 @@ class SchedulerMonitoringController extends Controller
|
||||
'label' => $label,
|
||||
'status' => $run?->status->value,
|
||||
'message' => $run?->message,
|
||||
'detail' => $details[$command] ?? null,
|
||||
'duration_ms' => $run?->duration_ms,
|
||||
'ran_at' => $run?->ran_at?->toIso8601String(),
|
||||
];
|
||||
@@ -137,4 +144,32 @@ class SchedulerMonitoringController extends Controller
|
||||
|
||||
return back()->with('success', __('All failed jobs deleted.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* What a task actually found, for the tasks that find something.
|
||||
*
|
||||
* The run rows answer "did it work"; they cannot answer "and what did
|
||||
* it say", because Laravel's ScheduledTaskFinished event fires after
|
||||
* the command returns and carries no output — which is why a
|
||||
* successful check for updates has always shown an empty Message.
|
||||
* Joined here at render time instead, from what the command itself
|
||||
* wrote to the settings, so the line stays true whether the daily job
|
||||
* or somebody pressing the button did the work.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function details(): array
|
||||
{
|
||||
$release = $this->latestRelease->current();
|
||||
$checkedAt = $this->settings->get(Setting::LatestVersionCheckedAt);
|
||||
$everChecked = is_string($checkedAt) && $checkedAt !== '';
|
||||
|
||||
$updates = match (true) {
|
||||
$release !== null => (string) __(':version is available', ['version' => $release['version']]),
|
||||
$everChecked => (string) __('Up to date'),
|
||||
default => null,
|
||||
};
|
||||
|
||||
return array_filter(['projectsend:check-for-updates' => $updates]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ interface ScheduledTask {
|
||||
label: string;
|
||||
status: 'success' | 'failed' | null;
|
||||
message: string | null;
|
||||
/** What the task found, for the tasks that find something. Never set on a failure. */
|
||||
detail: string | null;
|
||||
duration_ms: number | null;
|
||||
ran_at: string | null;
|
||||
}
|
||||
@@ -130,7 +132,10 @@ export default function SchedulerSettings({
|
||||
<td className="text-muted-foreground px-4 py-2.5 whitespace-nowrap">
|
||||
{task.duration_ms !== null ? `${task.duration_ms} ms` : '—'}
|
||||
</td>
|
||||
<td className="text-muted-foreground max-w-xs px-4 py-2.5 break-words">{task.message ?? '—'}</td>
|
||||
{/* A failure's own message wins: what the
|
||||
last successful run found is not the
|
||||
answer to why this one broke. */}
|
||||
<td className="text-muted-foreground max-w-xs px-4 py-2.5 break-words">{task.message ?? task.detail ?? '—'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
@@ -7,6 +7,8 @@ use App\Modules\Identity\Models\Role;
|
||||
use App\Modules\Platform\Capabilities\Edition;
|
||||
use App\Modules\Platform\Scheduling\ScheduledTaskRun;
|
||||
use App\Modules\Platform\Scheduling\TaskRunStatus;
|
||||
use App\Modules\Platform\Settings\Setting;
|
||||
use App\Modules\Platform\Settings\Settings;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
@@ -167,3 +169,38 @@ test('staff without edit_settings cannot access the scheduler page', function ()
|
||||
|
||||
$this->actingAs($staffer)->get('/system/settings/scheduler')->assertForbidden();
|
||||
});
|
||||
|
||||
test('the update check reports what it found, not just that it ran', function () {
|
||||
config()->set('projectsend.version', '2.0.0');
|
||||
$settings = app(Settings::class);
|
||||
$settings->set(Setting::LatestVersionCheckedAt, now()->toIso8601String());
|
||||
$settings->set(Setting::LatestKnownVersion, '2.5.0');
|
||||
|
||||
$response = $this->actingAs($this->admin)->get('/system/settings/scheduler');
|
||||
$tasks = collect(schedulerPageProps($response)['tasks'])->keyBy('command');
|
||||
|
||||
expect($tasks->get('projectsend:check-for-updates')['detail'])->toContain('2.5.0');
|
||||
});
|
||||
|
||||
test('a check that found nothing newer says so', function () {
|
||||
config()->set('projectsend.version', '2.0.0');
|
||||
$settings = app(Settings::class);
|
||||
$settings->set(Setting::LatestVersionCheckedAt, now()->toIso8601String());
|
||||
$settings->set(Setting::LatestKnownVersion, '2.0.0');
|
||||
|
||||
$response = $this->actingAs($this->admin)->get('/system/settings/scheduler');
|
||||
$tasks = collect(schedulerPageProps($response)['tasks'])->keyBy('command');
|
||||
|
||||
expect($tasks->get('projectsend:check-for-updates')['detail'])->toBe('Up to date');
|
||||
});
|
||||
|
||||
test('a check that has never run has nothing to report', function () {
|
||||
$settings = app(Settings::class);
|
||||
$settings->set(Setting::LatestVersionCheckedAt, '');
|
||||
$settings->set(Setting::LatestKnownVersion, '');
|
||||
|
||||
$response = $this->actingAs($this->admin)->get('/system/settings/scheduler');
|
||||
$tasks = collect(schedulerPageProps($response)['tasks'])->keyBy('command');
|
||||
|
||||
expect($tasks->get('projectsend:check-for-updates')['detail'])->toBeNull();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user