feat(release): ReleasePlanner — proposed beta targets from the current version
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>feat/v1-foundation
parent
39ccf0d539
commit
05c98a7b9e
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
/**
|
||||
* Pure semver arithmetic for the Release page: given the current version, what beta-target versions
|
||||
* may the operator cut? The dashboard computes the target X.Y.Z; the host bridge computes -betaN.
|
||||
*/
|
||||
class ReleasePlanner
|
||||
{
|
||||
/**
|
||||
* Proposed target versions (bare X.Y.Z) for the current version. A beta also offers continuing
|
||||
* its own base (another beta of the same target).
|
||||
*
|
||||
* @return array{patch:string, minor:string, major:string, continueBeta?:string}
|
||||
*/
|
||||
public function proposedTargets(string $current): array
|
||||
{
|
||||
$current = ltrim($current, 'vV');
|
||||
$base = preg_replace('/-.*$/', '', $current);
|
||||
[$maj, $min, $pat] = array_map('intval', array_pad(explode('.', (string) $base), 3, '0'));
|
||||
|
||||
$targets = [
|
||||
'patch' => "{$maj}.{$min}.".($pat + 1),
|
||||
'minor' => "{$maj}.".($min + 1).'.0',
|
||||
'major' => ($maj + 1).'.0.0',
|
||||
];
|
||||
|
||||
if (str_contains($current, '-beta')) {
|
||||
$targets['continueBeta'] = "{$maj}.{$min}.{$pat}";
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
/**
|
||||
* The flat list of allowed target versions — the server-side allow-list for a posted target.
|
||||
*
|
||||
* @return array<int,string>
|
||||
*/
|
||||
public function allowedTargets(string $current): array
|
||||
{
|
||||
return array_values($this->proposedTargets($current));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Services\ReleasePlanner;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ReleasePlannerTest extends TestCase
|
||||
{
|
||||
private ReleasePlanner $planner;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->planner = new ReleasePlanner;
|
||||
}
|
||||
|
||||
public function test_stable_version_offers_patch_minor_major_without_continue_beta(): void
|
||||
{
|
||||
$t = $this->planner->proposedTargets('0.9.58');
|
||||
|
||||
$this->assertSame('0.9.59', $t['patch']);
|
||||
$this->assertSame('0.10.0', $t['minor']);
|
||||
$this->assertSame('1.0.0', $t['major']);
|
||||
$this->assertArrayNotHasKey('continueBeta', $t);
|
||||
}
|
||||
|
||||
public function test_beta_version_also_offers_continue_beta_of_its_base(): void
|
||||
{
|
||||
$t = $this->planner->proposedTargets('0.10.0-beta3');
|
||||
|
||||
$this->assertSame('0.10.0', $t['continueBeta']);
|
||||
$this->assertSame('0.10.1', $t['patch']);
|
||||
$this->assertSame('0.11.0', $t['minor']);
|
||||
$this->assertSame('1.0.0', $t['major']);
|
||||
}
|
||||
|
||||
public function test_strips_a_leading_v(): void
|
||||
{
|
||||
$this->assertSame('0.9.59', $this->planner->proposedTargets('v0.9.58')['patch']);
|
||||
}
|
||||
|
||||
public function test_allowed_targets_are_the_proposed_values(): void
|
||||
{
|
||||
$allowed = $this->planner->allowedTargets('0.10.0-beta1');
|
||||
|
||||
$this->assertContains('0.10.0', $allowed); // continueBeta
|
||||
$this->assertContains('0.11.0', $allowed); // minor
|
||||
$this->assertNotContains('0.9.0', $allowed);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue