67 lines
2.1 KiB
PHP
67 lines
2.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Deployment;
|
|
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\File;
|
|
use Throwable;
|
|
|
|
/**
|
|
* Was der Wächter zuletzt getan hat.
|
|
*
|
|
* Eigene Klasse und nicht ein weiteres Feld in `UpdateChannel`: der Wächter
|
|
* ist eine andere Sache als der Update-Kanal — er richtet einen liegen
|
|
* gebliebenen Stapel, während jener eine Auslieferung vermittelt. Und
|
|
* `UpdateChannel` ist bereits über neunhundert Zeilen lang.
|
|
*/
|
|
class WatchdogLog
|
|
{
|
|
private const FILE = 'deploy/watchdog-last-run.json';
|
|
|
|
/**
|
|
* Älter als das, und der Wächter läuft nicht mehr.
|
|
*
|
|
* Sein Zeitgeber steht auf einer Minute. Fünf lässt vier ausgefallenen
|
|
* Takten Luft — ein Wirt unter Last oder ein Lauf, der gerade heilt und
|
|
* dabei `sleep 15` macht, ist noch kein toter Wächter.
|
|
*/
|
|
private const STALE_AFTER_MINUTES = 5;
|
|
|
|
/**
|
|
* @return array{at: Carbon, outcome: string, actions: array<int, string>, stale: bool}|null
|
|
*/
|
|
public function lastRun(): ?array
|
|
{
|
|
try {
|
|
$path = storage_path('app/'.self::FILE);
|
|
|
|
if (! File::exists($path)) {
|
|
return null;
|
|
}
|
|
|
|
$data = json_decode((string) File::get($path), true);
|
|
|
|
if (! is_array($data) || ! isset($data['at'])) {
|
|
return null;
|
|
}
|
|
|
|
$at = Carbon::parse((string) $data['at']);
|
|
|
|
return [
|
|
'at' => $at,
|
|
'outcome' => (string) ($data['outcome'] ?? 'idle'),
|
|
'actions' => array_values(array_filter(
|
|
is_array($data['actions'] ?? null) ? $data['actions'] : [],
|
|
'is_string'
|
|
)),
|
|
'stale' => $at->lt(Carbon::now()->subMinutes(self::STALE_AFTER_MINUTES)),
|
|
];
|
|
} catch (Throwable) {
|
|
// Dieselbe Haltung wie `UpdateChannel::readJson()`: die Konsole
|
|
// liest das bei jedem Seitenaufbau, und „ich weiß es nicht" ist
|
|
// ein brauchbarer Zustand — eine geworfene Ausnahme nicht.
|
|
return null;
|
|
}
|
|
}
|
|
}
|