86 lines
3.3 KiB
PHP
86 lines
3.3 KiB
PHP
<?php
|
|
|
|
use App\Services\Deployment\UpdateChannel;
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Facades\Process;
|
|
|
|
/**
|
|
* Der Wirt-Helfer ist root-eigen und wird von `install-agent.sh` gesetzt.
|
|
* Hebt ein Release seine Vertragsversion an, braucht JEDER Wirt einmal
|
|
* Handarbeit als root — bei zehn Servern zehnmal.
|
|
*
|
|
* Automatisieren lässt sich das nicht: `sudoers` gewährt dem Dienstbenutzer
|
|
* genau drei benannte Befehle, und etwas Root-Eigenes, das ungeprüft aus dem
|
|
* (vom Dienstbenutzer beschreibbaren) Checkout ausführt, gäbe jedem, der je
|
|
* an diesen Benutzer kommt, Root auf dem Wirt.
|
|
*
|
|
* Also wird gemeldet statt automatisiert — aber VOLLSTÄNDIG und BEVOR jemand
|
|
* einen Knopf drückt, der daran scheitert.
|
|
*/
|
|
|
|
afterEach(function () {
|
|
File::deleteDirectory(storage_path('app/deploy'));
|
|
});
|
|
|
|
it('keeps the needed contract version in exactly one place', function () {
|
|
// Sie stand zweimal im Repo: `HOST_STEP_NEEDS=3` in update.sh und eine
|
|
// hartkodierte 3 im Agenten. Zwei Zahlen, die zusammenpassen müssen,
|
|
// laufen irgendwann auseinander.
|
|
$lib = File::get(base_path('deploy/lib/release.sh'));
|
|
$update = File::get(base_path('deploy/update.sh'));
|
|
$agent = File::get(base_path('deploy/update-agent.sh'));
|
|
|
|
expect($lib)->toContain('release_host_step_needs')
|
|
->and($update)->toContain('release_host_step_needs')
|
|
->and($agent)->toContain('release_host_step_needs');
|
|
|
|
// Und keine nackte Zahl mehr an den beiden alten Stellen.
|
|
expect($update)->not->toContain('HOST_STEP_NEEDS=3')
|
|
->and($agent)->not->toContain('(( have < 3 ))');
|
|
});
|
|
|
|
it('answers the needed contract version from the shell', function () {
|
|
$result = Process::path(base_path())->timeout(30)->run(
|
|
'bash -c '.escapeshellarg('set -Eeuo pipefail; . deploy/lib/release.sh; release_host_step_needs')
|
|
);
|
|
|
|
expect($result->exitCode())->toBe(0)
|
|
->and(trim($result->output()))->toMatch('/^[0-9]+$/');
|
|
});
|
|
|
|
it('reports the helper as not ok when the host has an older one', function () {
|
|
File::ensureDirectoryExists(storage_path('app/deploy'));
|
|
File::put(storage_path('app/deploy/update-status.json'), json_encode([
|
|
'state' => 'idle',
|
|
'host_step_contract' => 2,
|
|
'host_step_needs' => 3,
|
|
]));
|
|
|
|
$state = app(UpdateChannel::class)->state();
|
|
|
|
expect($state['host_step_ok'])->toBeFalse()
|
|
->and($state['host_step_have'])->toBe(2)
|
|
->and($state['host_step_needs'])->toBe(3);
|
|
});
|
|
|
|
it('reports the helper as ok when it is current', function () {
|
|
File::ensureDirectoryExists(storage_path('app/deploy'));
|
|
File::put(storage_path('app/deploy/update-status.json'), json_encode([
|
|
'state' => 'idle',
|
|
'host_step_contract' => 3,
|
|
'host_step_needs' => 3,
|
|
]));
|
|
|
|
expect(app(UpdateChannel::class)->state()['host_step_ok'])->toBeTrue();
|
|
});
|
|
|
|
it('does not cry wolf when the agent has not reported yet', function () {
|
|
// Ein Wirt, dessen Agent die Zahlen noch nie gemeldet hat (alte Fassung,
|
|
// erster Lauf), darf nicht als kaputt dastehen. „Ich weiß es nicht" ist
|
|
// nicht dasselbe wie „zu alt".
|
|
File::ensureDirectoryExists(storage_path('app/deploy'));
|
|
File::put(storage_path('app/deploy/update-status.json'), json_encode(['state' => 'idle']));
|
|
|
|
expect(app(UpdateChannel::class)->state()['host_step_ok'])->toBeTrue();
|
|
});
|