feat: divi-check gibt die Matrix je Fähigkeit aus (Exit 2 bei nicht nutzbarer Fähigkeit); Texte nennen Fähigkeiten statt pauschal gesperrt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
2aef6e1154
commit
a4faeda326
@@ -9,6 +9,7 @@ use LIT\MCP\Integrations\Divi\Release\CheckReport;
|
||||
use LIT\MCP\Integrations\Divi\Release\CheckReportStore;
|
||||
use LIT\MCP\Integrations\Divi\Release\CheckStep;
|
||||
use LIT\MCP\Integrations\Divi\Release\CompatibilityCheck;
|
||||
use LIT\MCP\Integrations\Divi\Release\CompatibilityMatrix;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviAdapters;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviEnvironment;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviReference;
|
||||
@@ -22,7 +23,14 @@ defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* wp lit-mcp divi-check (spec §7): the admin check from the command line, for a developer checking a new Divi
|
||||
* version. Never releases anything – a green version goes into DiviReleases::TESTED by hand.
|
||||
* version. Never releases anything – a green version goes into DiviReleases::TESTED by hand. After the report it
|
||||
* prints the compatibility matrix (E3).
|
||||
*
|
||||
* Exit codes: 0 = report green and every feature usable; 1 = report red (or the check could not run, or
|
||||
* --update-reference refused); 2 = report green but a feature is not usable. The 2 is for a version that is
|
||||
* checked but not yet released (not in DiviReleases::TESTED, automatic release off or a reference missing): the
|
||||
* checks passed, yet LIT MCP would still refuse the affected tools. A script that only cares about "usable" tests
|
||||
* for ≠ 0. --update-reference keeps its own code: it runs before the new reference is part of any release.
|
||||
*/
|
||||
final class DiviCheckCommand
|
||||
{
|
||||
@@ -37,6 +45,7 @@ final class DiviCheckCommand
|
||||
* @param (Closure(): ?string)|null $sourceDir null = DiviReference::sourceDir().
|
||||
* @param (Closure(): array<string, string>)|null $signatures "sig:<symbol>" => sha256 of every Divi call in the
|
||||
* running process; null = DiviReference::currentSignatures().
|
||||
* @param (Closure(): CompatibilityMatrix)|null $matrix null = the release gate's matrix, computed again.
|
||||
*/
|
||||
public function __construct(
|
||||
private Closure $check,
|
||||
@@ -45,7 +54,8 @@ final class DiviCheckCommand
|
||||
private ?Closure $themeDir = null,
|
||||
private ?string $expectedFile = null,
|
||||
private ?Closure $sourceDir = null,
|
||||
private ?Closure $signatures = null
|
||||
private ?Closure $signatures = null,
|
||||
private ?Closure $matrix = null
|
||||
) {
|
||||
$this->out = $out ?? static function (string $line): void {
|
||||
\WP_CLI::line($line);
|
||||
@@ -62,7 +72,9 @@ final class DiviCheckCommand
|
||||
|
||||
/**
|
||||
* Prüft die installierte Divi-Version wie der Reiter „Divi“: Manifeste, Fingerabdrücke, Modulkatalog, Format
|
||||
* und Schreibtest auf einer Wegwerfseite. Gibt nichts frei.
|
||||
* und Schreibtest auf einer Wegwerfseite. Gibt danach den Zustand je Fähigkeit aus und gibt nichts frei.
|
||||
* Exit-Code 0: Prüfung grün und alle Fähigkeiten nutzbar; 1: Prüfung rot; 2: Prüfung grün, aber mindestens
|
||||
* eine Fähigkeit nicht nutzbar (z. B. Version noch nicht in DiviReleases::TESTED).
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
@@ -111,10 +123,69 @@ final class DiviCheckCommand
|
||||
$this->diff();
|
||||
}
|
||||
if (isset($assoc['update-reference'])) {
|
||||
return $this->updateReference($report);
|
||||
$code = $this->updateReference($report);
|
||||
$this->printMatrix();
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
return $report->green() ? 0 : 1;
|
||||
$unusable = $this->printMatrix();
|
||||
if (! $report->green()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return $unusable > 0 ? 2 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints one line per feature (✓ released, ✗ incompatible, – untested) with label and state, the problems
|
||||
* indented below.
|
||||
*
|
||||
* @return int The number of features that are not usable.
|
||||
*/
|
||||
private function printMatrix(): int
|
||||
{
|
||||
$matrix = $this->currentMatrix();
|
||||
($this->out)('Fähigkeiten:');
|
||||
foreach ($matrix->toArray() as $row) {
|
||||
($this->out)(sprintf(
|
||||
'%s %s – %s',
|
||||
match ($row['state']) {
|
||||
'released' => '✓',
|
||||
'incompatible' => '✗',
|
||||
default => '–',
|
||||
},
|
||||
$row['label'],
|
||||
self::stateLabel($row['state'], $row['source'])
|
||||
));
|
||||
foreach ($row['problems'] as $problem) {
|
||||
($this->out)(' ' . $problem);
|
||||
}
|
||||
}
|
||||
|
||||
return count($matrix->unusable());
|
||||
}
|
||||
|
||||
/** The check just ran, so the state is computed again instead of read from a cache older than the report. */
|
||||
private function currentMatrix(): CompatibilityMatrix
|
||||
{
|
||||
if ($this->matrix !== null) {
|
||||
return ($this->matrix)();
|
||||
}
|
||||
|
||||
delete_transient(ReleaseResolver::CACHE);
|
||||
ReleaseGate::forget();
|
||||
|
||||
return ReleaseGate::status()->matrix;
|
||||
}
|
||||
|
||||
private static function stateLabel(string $state, ?string $source): string
|
||||
{
|
||||
return match ($state) {
|
||||
'released' => 'freigegeben (' . ($source ?? '?') . ')',
|
||||
'incompatible' => 'inkompatibel',
|
||||
default => 'ungetestet',
|
||||
};
|
||||
}
|
||||
|
||||
private function diff(): void
|
||||
|
||||
@@ -20,16 +20,19 @@ final class ReleaseStatus
|
||||
public const SOURCE_DATABASE = 'database';
|
||||
public const SOURCE_AUTO = 'auto';
|
||||
|
||||
private const UNTESTED = 'Achtung: %s ist auf dieser Website noch nicht für LIT MCP freigegeben. Schreibende '
|
||||
. 'Divi-Werkzeuge sind gesperrt, bis ein Administrator unter Einstellungen → LIT MCP → Divi die Prüfung '
|
||||
. 'ausführt und die Version freigibt. Es wurde nichts geändert.';
|
||||
private const UNTESTED = 'Achtung: %s ist auf dieser Website noch nicht für LIT MCP freigegeben. Dieses '
|
||||
. 'Werkzeug braucht Divi-Fähigkeiten, die noch nicht geprüft sind; andere Divi-Werkzeuge laufen weiter. '
|
||||
. 'Ein Administrator kann unter Einstellungen → LIT MCP → Divi die Prüfung ausführen und die Version '
|
||||
. 'freigeben. Es wurde nichts geändert.';
|
||||
private const UNTESTED_HINT = 'Den Nutzer bitten, die Divi-Prüfung auszuführen und die Version freizugeben; '
|
||||
. 'lesende Divi-Werkzeuge funktionieren weiter.';
|
||||
private const INCOMPATIBLE = '%s hat Stellen geändert, die LIT MCP nutzt. Schreibende Divi-Werkzeuge sind '
|
||||
. 'gesperrt, bis LIT MCP aktualisiert wurde. Es wurde nichts geändert.';
|
||||
private const INCOMPATIBLE_HINT = 'Den Nutzer informieren, dass LIT MCP aktualisiert werden muss; lesende '
|
||||
. 'Divi-Werkzeuge funktionieren weiter.';
|
||||
private const NOTICE = 'Achtung: %s ist für LIT MCP %s; Lesen funktioniert, Schreiben ist gesperrt.';
|
||||
. 'bis dahin ein Werkzeug ohne die betroffene Fähigkeit nutzen. lit-site-status zeigt unter components → '
|
||||
. 'divi → features, welche Fähigkeiten und Werkzeuge betroffen sind.';
|
||||
private const INCOMPATIBLE = '%s hat Stellen geändert, die LIT MCP für dieses Werkzeug nutzt; andere '
|
||||
. 'Divi-Werkzeuge laufen weiter. Die betroffenen Werkzeuge bleiben gesperrt, bis LIT MCP aktualisiert '
|
||||
. 'wurde. Es wurde nichts geändert.';
|
||||
private const INCOMPATIBLE_HINT = 'Den Nutzer informieren, dass LIT MCP aktualisiert werden muss; bis dahin '
|
||||
. 'ein Werkzeug ohne die betroffene Fähigkeit nutzen. lit-site-status zeigt unter components → divi → '
|
||||
. 'features, welche Fähigkeiten und Werkzeuge betroffen sind.';
|
||||
private const FEATURE_UNTESTED = 'Achtung: %1$s ist auf dieser Website für %2$s noch nicht für LIT MCP '
|
||||
. 'freigegeben. Das ist gesperrt, bis ein Administrator unter Einstellungen → LIT MCP → Divi die Prüfung '
|
||||
. 'ausführt und die Version freigibt. Es wurde nichts geändert.';
|
||||
@@ -158,16 +161,6 @@ final class ReleaseStatus
|
||||
);
|
||||
}
|
||||
|
||||
/** The warning a reading Divi tool carries while writes are locked. */
|
||||
public function notice(): string
|
||||
{
|
||||
return sprintf(
|
||||
self::NOTICE,
|
||||
$this->versionLabel(),
|
||||
$this->state === ReleaseState::Incompatible ? 'inkompatibel' : 'ungetestet'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* "„Seiten schreiben“", "„A“ und „B“", "„A“, „B“ und „C“".
|
||||
*
|
||||
|
||||
@@ -10,7 +10,14 @@ use LIT\MCP\Integrations\Divi\Release\CheckReport;
|
||||
use LIT\MCP\Integrations\Divi\Release\CheckReportStore;
|
||||
use LIT\MCP\Integrations\Divi\Release\CheckStep;
|
||||
use LIT\MCP\Integrations\Divi\Release\CompatibilityCheck;
|
||||
use LIT\MCP\Integrations\Divi\Release\CompatibilityMatrix;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviReference;
|
||||
use LIT\MCP\Integrations\Divi\Release\ReleaseGate;
|
||||
use LIT\MCP\Integrations\Divi\Release\ReleaseResolver;
|
||||
use LIT\MCP\Integrations\Divi\Release\ReleaseState;
|
||||
use LIT\MCP\Integrations\Divi\Release\ReleaseStatus;
|
||||
use LIT\MCP\Tests\Support\MatrixFixtures;
|
||||
use LIT\MCP\Tests\Unit\TestCase;
|
||||
use Mockery;
|
||||
use RuntimeException;
|
||||
@@ -61,7 +68,8 @@ final class DiviCheckCommandTest extends TestCase
|
||||
private function command(
|
||||
array $problems = [],
|
||||
bool $expectSave = false,
|
||||
bool $noSourceDir = false
|
||||
bool $noSourceDir = false,
|
||||
?CompatibilityMatrix $matrix = null
|
||||
): DiviCheckCommand {
|
||||
$steps = array_map(
|
||||
static fn (string $key): CheckStep => new CheckStep($key, $problems[$key] ?? []),
|
||||
@@ -91,7 +99,8 @@ final class DiviCheckCommandTest extends TestCase
|
||||
}
|
||||
|
||||
return $this->signatures;
|
||||
}
|
||||
},
|
||||
$matrix === null ? null : static fn (): CompatibilityMatrix => $matrix
|
||||
);
|
||||
}
|
||||
|
||||
@@ -256,4 +265,100 @@ final class DiviCheckCommandTest extends TestCase
|
||||
file_put_contents($file, "<?php\n" . $code . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
public function testPrintsTheMatrixAfterTheReport(): void
|
||||
{
|
||||
self::assertSame(0, $this->command()->run([]));
|
||||
|
||||
$schreibtest = array_search('✓ Schreibtest auf einer Wegwerfseite', $this->lines, true);
|
||||
$header = array_search('Fähigkeiten:', $this->lines, true);
|
||||
self::assertIsInt($schreibtest);
|
||||
self::assertIsInt($header);
|
||||
self::assertGreaterThan($schreibtest, $header);
|
||||
self::assertContains('✓ Seiten schreiben – freigegeben (code)', $this->lines);
|
||||
self::assertContains('✓ Theme Builder lesen – freigegeben (code)', $this->lines);
|
||||
}
|
||||
|
||||
public function testEveryFeatureGetsOneLine(): void
|
||||
{
|
||||
$this->command()->run([]);
|
||||
|
||||
$matrixLines = array_filter(
|
||||
$this->lines,
|
||||
static fn (string $line): bool => str_contains($line, ' – freigegeben')
|
||||
);
|
||||
self::assertCount(count(DiviFeature::cases()), $matrixLines);
|
||||
}
|
||||
|
||||
public function testAnIncompatibleFeatureFailsWithItsProblemsIndented(): void
|
||||
{
|
||||
$matrix = MatrixFixtures::with(
|
||||
DiviFeature::PageWrite,
|
||||
ReleaseState::Incompatible,
|
||||
'auto',
|
||||
'Seiten: et_pb_x() fehlt'
|
||||
);
|
||||
|
||||
$code = $this->command(matrix: $matrix)->run([]);
|
||||
|
||||
self::assertSame(2, $code);
|
||||
self::assertContains('✗ Seiten schreiben – inkompatibel', $this->lines);
|
||||
self::assertContains(' Seiten: et_pb_x() fehlt', $this->lines);
|
||||
self::assertContains('✓ Seiten lesen – freigegeben (auto)', $this->lines);
|
||||
}
|
||||
|
||||
public function testAnUntestedFeatureFailsWithExitTwoAlthoughTheReportIsGreen(): void
|
||||
{
|
||||
$matrix = MatrixFixtures::with(DiviFeature::Catalog, ReleaseState::Untested, 'auto');
|
||||
|
||||
$code = $this->command(expectSave: true, matrix: $matrix)->run([]);
|
||||
|
||||
self::assertSame(2, $code);
|
||||
self::assertContains('– Modulkatalog – ungetestet', $this->lines);
|
||||
}
|
||||
|
||||
public function testARedReportKeepsExitOneWhateverTheMatrixSays(): void
|
||||
{
|
||||
$red = [CheckStep::CATALOG => ['divi/text: unbekanntes Modul']];
|
||||
$incompatible = MatrixFixtures::with(DiviFeature::Catalog, ReleaseState::Incompatible, 'auto', 'x');
|
||||
|
||||
self::assertSame(1, $this->command($red)->run([]));
|
||||
self::assertSame(1, $this->command($red, matrix: $incompatible)->run([]));
|
||||
}
|
||||
|
||||
public function testUpdateReferenceKeepsItsOwnExitCodeAndStillPrintsTheMatrix(): void
|
||||
{
|
||||
$this->writeDivi();
|
||||
$matrix = MatrixFixtures::with(
|
||||
DiviFeature::PageWrite,
|
||||
ReleaseState::Incompatible,
|
||||
'auto',
|
||||
'Seiten: Nachbau geändert'
|
||||
);
|
||||
|
||||
$code = $this->command([CheckStep::MIRRORS => ['geändert']], matrix: $matrix)
|
||||
->run(['update-reference' => true]);
|
||||
|
||||
self::assertSame(0, $code);
|
||||
self::assertContains('✗ Seiten schreiben – inkompatibel', $this->lines);
|
||||
}
|
||||
|
||||
public function testWithoutAnInjectedMatrixTheStateIsComputedAfterTheCheck(): void
|
||||
{
|
||||
$order = [];
|
||||
Functions\when('delete_transient')->alias(static function (string $key) use (&$order): bool {
|
||||
$order[] = 'delete:' . $key;
|
||||
|
||||
return true;
|
||||
});
|
||||
ReleaseGate::useResolver(static function () use (&$order): ReleaseStatus {
|
||||
$order[] = 'resolve';
|
||||
|
||||
return ReleaseStatus::released('5.3.0');
|
||||
});
|
||||
|
||||
$this->command()->run([]);
|
||||
|
||||
self::assertSame(['delete:' . ReleaseResolver::CACHE, 'resolve'], $order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +30,14 @@ final class ReleaseStatusTest extends TestCase
|
||||
);
|
||||
|
||||
self::assertSame(
|
||||
'Achtung: Divi 5.3.0 ist auf dieser Website noch nicht für LIT MCP freigegeben. Schreibende '
|
||||
. 'Divi-Werkzeuge sind gesperrt, bis ein Administrator unter Einstellungen → LIT MCP → Divi die '
|
||||
. 'Prüfung ausführt und die Version freigibt. Es wurde nichts geändert.',
|
||||
'Achtung: Divi 5.3.0 ist auf dieser Website noch nicht für LIT MCP freigegeben. Dieses Werkzeug braucht '
|
||||
. 'Divi-Fähigkeiten, die noch nicht geprüft sind; andere Divi-Werkzeuge laufen weiter. Ein '
|
||||
. 'Administrator kann unter Einstellungen → LIT MCP → Divi die Prüfung ausführen und die Version '
|
||||
. 'freigeben. Es wurde nichts geändert.',
|
||||
$payload['message']
|
||||
);
|
||||
self::assertStringContainsString('lesende Divi-Werkzeuge funktionieren weiter', $payload['hint']);
|
||||
self::assertStringContainsString('ein Werkzeug ohne die betroffene Fähigkeit nutzen', $payload['hint']);
|
||||
self::assertStringContainsString('components → divi → features', $payload['hint']);
|
||||
}
|
||||
|
||||
public function testIncompatibleRefusalListsTheProblems(): void
|
||||
@@ -44,26 +46,14 @@ final class ReleaseStatusTest extends TestCase
|
||||
$payload = self::assertToolError($status->refusal(), 'divi_incompatible');
|
||||
|
||||
self::assertSame(
|
||||
'Divi 5.3.0 hat Stellen geändert, die LIT MCP nutzt. Schreibende Divi-Werkzeuge sind gesperrt, bis '
|
||||
. 'LIT MCP aktualisiert wurde. Es wurde nichts geändert.',
|
||||
'Divi 5.3.0 hat Stellen geändert, die LIT MCP für dieses Werkzeug nutzt; andere Divi-Werkzeuge laufen '
|
||||
. 'weiter. Die betroffenen Werkzeuge bleiben gesperrt, bis LIT MCP aktualisiert wurde. Es wurde '
|
||||
. 'nichts geändert.',
|
||||
$payload['message']
|
||||
);
|
||||
self::assertSame(['Theme Builder: Nachbau von x()'], $payload['problems']);
|
||||
}
|
||||
|
||||
public function testNoticesForReadingTools(): void
|
||||
{
|
||||
self::assertSame(
|
||||
'Achtung: Divi 5.3.0 ist für LIT MCP ungetestet; Lesen funktioniert, Schreiben ist gesperrt.',
|
||||
(new ReleaseStatus(ReleaseState::Untested, '5.3.0'))->notice()
|
||||
);
|
||||
self::assertSame(
|
||||
'Achtung: Divi (Version unbekannt) ist für LIT MCP inkompatibel; Lesen funktioniert, Schreiben ist '
|
||||
. 'gesperrt.',
|
||||
(new ReleaseStatus(ReleaseState::Incompatible, ''))->notice()
|
||||
);
|
||||
}
|
||||
|
||||
public function testAReleasedStatusHasNoRefusal(): void
|
||||
{
|
||||
$this->expectException(LogicException::class);
|
||||
|
||||
Reference in New Issue
Block a user