Skip to content

Commit 757e0bd

Browse files
committed
Add package distribution strategy field
Signed-off-by: Tim Goudriaan <tim@codedmonkey.com>
1 parent cee694c commit 757e0bd

11 files changed

Lines changed: 238 additions & 20 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DoctrineMigrations;
6+
7+
use Doctrine\DBAL\Schema\Schema;
8+
use Doctrine\Migrations\AbstractMigration;
9+
10+
final class Version20251109181654 extends AbstractMigration
11+
{
12+
public function getDescription(): string
13+
{
14+
return 'Add package distribution strategy';
15+
}
16+
17+
public function up(Schema $schema): void
18+
{
19+
$this->addSql(<<<'SQL'
20+
ALTER TABLE package ADD distribution_strategy VARCHAR(255) DEFAULT NULL
21+
SQL);
22+
$this->addSql(<<<'SQL'
23+
UPDATE package SET distribution_strategy = 'dynamic'
24+
SQL);
25+
$this->addSql(<<<'SQL'
26+
ALTER TABLE package ALTER distribution_strategy TYPE VARCHAR(255)
27+
SQL);
28+
$this->addSql(<<<'SQL'
29+
ALTER TABLE package ALTER distribution_strategy SET NOT NULL
30+
SQL);
31+
}
32+
33+
public function down(Schema $schema): void
34+
{
35+
$this->addSql(<<<'SQL'
36+
ALTER TABLE package DROP distribution_strategy
37+
SQL);
38+
}
39+
}

src/Doctrine/Entity/Package.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ class Package extends TrackedEntity
6262
private ?string $remoteId = null;
6363

6464
#[ORM\Column(nullable: true, enumType: PackageFetchStrategy::class)]
65-
private PackageFetchStrategy|string|null $fetchStrategy = null;
65+
private ?PackageFetchStrategy $fetchStrategy = null;
66+
67+
#[ORM\Column(enumType: PackageDistributionStrategy::class)]
68+
private PackageDistributionStrategy $distributionStrategy = PackageDistributionStrategy::Dynamic;
6669

6770
#[ORM\ManyToOne]
6871
private ?Registry $mirrorRegistry = null;
@@ -234,20 +237,30 @@ public function setRemoteId(?string $remoteId): void
234237
$this->remoteId = $remoteId;
235238
}
236239

237-
public function getFetchStrategy(): PackageFetchStrategy|string
240+
public function getFetchStrategy(): PackageFetchStrategy
238241
{
239-
if (!$this->fetchStrategy) {
242+
if (null === $this->fetchStrategy) {
240243
return $this->mirrorRegistry ? PackageFetchStrategy::Mirror : PackageFetchStrategy::Vcs;
241244
}
242245

243246
return $this->fetchStrategy;
244247
}
245248

246-
public function setFetchStrategy(PackageFetchStrategy|string $fetchStrategy): void
249+
public function setFetchStrategy(?PackageFetchStrategy $fetchStrategy): void
247250
{
248251
$this->fetchStrategy = $fetchStrategy;
249252
}
250253

254+
public function getDistributionStrategy(): PackageDistributionStrategy
255+
{
256+
return $this->distributionStrategy;
257+
}
258+
259+
public function setDistributionStrategy(PackageDistributionStrategy $distributionStrategy): void
260+
{
261+
$this->distributionStrategy = $distributionStrategy;
262+
}
263+
251264
public function getMirrorRegistry(): ?Registry
252265
{
253266
return $this->mirrorRegistry;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace CodedMonkey\Dirigent\Doctrine\Entity;
4+
5+
use Symfony\Contracts\Translation\TranslatableInterface;
6+
use Symfony\Contracts\Translation\TranslatorInterface;
7+
8+
enum PackageDistributionStrategy: string implements TranslatableInterface
9+
{
10+
case Disabled = 'none';
11+
case Dynamic = 'dynamic';
12+
case Automatic = 'auto';
13+
14+
public function trans(TranslatorInterface $translator, ?string $locale = null): string
15+
{
16+
return $translator->trans("package.distribution-strategy.{$this->value}");
17+
}
18+
19+
public function isDisabled(): bool
20+
{
21+
return self::Disabled === $this;
22+
}
23+
24+
public function isDynamic(): bool
25+
{
26+
return self::Dynamic === $this;
27+
}
28+
29+
public function isAutomatic(): bool
30+
{
31+
return self::Automatic === $this;
32+
}
33+
34+
/**
35+
* Whether the strategy allows dynamic updates.
36+
*
37+
* Only the disabled strategy doesn't allow this.
38+
*/
39+
public function allowDynamic(): bool
40+
{
41+
return !$this->isDisabled();
42+
}
43+
}

src/Doctrine/Entity/PackageFetchStrategy.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,14 @@ enum PackageFetchStrategy: string
66
{
77
case Mirror = 'mirror';
88
case Vcs = 'vcs';
9+
10+
public function isMirror(): bool
11+
{
12+
return self::Mirror === $this;
13+
}
14+
15+
public function isVcs(): bool
16+
{
17+
return self::Vcs === $this;
18+
}
919
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace CodedMonkey\Dirigent\Doctrine\EventListener;
4+
5+
use CodedMonkey\Dirigent\Doctrine\Entity\Version;
6+
use CodedMonkey\Dirigent\Message\ResolveDistribution;
7+
use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener;
8+
use Doctrine\ORM\Event\PostUpdateEventArgs;
9+
use Doctrine\ORM\Events;
10+
use Symfony\Component\DependencyInjection\Attribute\Autowire;
11+
use Symfony\Component\Messenger\Envelope;
12+
use Symfony\Component\Messenger\MessageBusInterface;
13+
use Symfony\Component\Messenger\Stamp\TransportNamesStamp;
14+
15+
#[AsEntityListener(Events::postPersist, entity: Version::class)]
16+
#[AsEntityListener(Events::postUpdate, entity: Version::class)]
17+
readonly class VersionListener
18+
{
19+
public function __construct(
20+
private MessageBusInterface $messenger,
21+
#[Autowire(param: 'dirigent.distributions.enabled')]
22+
private bool $distributionsEnabled,
23+
#[Autowire(param: 'dirigent.distributions.dev_versions')]
24+
private bool $resolveDevDistributions,
25+
) {
26+
}
27+
28+
public function postPersist(Version $version): void
29+
{
30+
$this->resolveDistribution($version);
31+
}
32+
33+
public function postUpdate(Version $version, PostUpdateEventArgs $event): void
34+
{
35+
// Only resolve the distribution if the source has changed
36+
$changedFields = $event->getObjectManager()->getUnitOfWork()->getEntityChangeSet($version);
37+
if (isset($changedFields['source'])) {
38+
$this->resolveDistribution($version);
39+
}
40+
}
41+
42+
private function resolveDistribution(Version $version): void
43+
{
44+
if (
45+
!$this->distributionsEnabled
46+
|| ($version->isDevelopment() && !$this->resolveDevDistributions)
47+
) {
48+
return;
49+
}
50+
51+
if ($version->getPackage()->getDistributionStrategy()->isAutomatic()) {
52+
$message = Envelope::wrap(new ResolveDistribution($version->getId()))
53+
->with(new TransportNamesStamp('async'));
54+
$this->messenger->dispatch($message);
55+
}
56+
}
57+
}

src/Form/PackageFormType.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
use CodedMonkey\Dirigent\Doctrine\Entity\Credentials;
66
use CodedMonkey\Dirigent\Doctrine\Entity\Package;
7+
use CodedMonkey\Dirigent\Doctrine\Entity\PackageDistributionStrategy;
78
use CodedMonkey\Dirigent\Doctrine\Entity\PackageFetchStrategy;
89
use CodedMonkey\Dirigent\Doctrine\Entity\Registry;
910
use CodedMonkey\Dirigent\Doctrine\Repository\RegistryRepository;
1011
use CodedMonkey\Dirigent\Package\PackageVcsRepositoryValidator;
1112
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
13+
use Symfony\Component\DependencyInjection\Attribute\Autowire;
1214
use Symfony\Component\Form\AbstractType;
1315
use Symfony\Component\Form\Event\PostSetDataEvent;
1416
use Symfony\Component\Form\Event\SubmitEvent;
@@ -23,6 +25,8 @@ class PackageFormType extends AbstractType
2325
{
2426
public function __construct(
2527
private readonly PackageVcsRepositoryValidator $vcsRepositoryValidator,
28+
#[Autowire(param: 'dirigent.distributions.enabled')]
29+
private readonly bool $distributionsEnabled,
2630
) {
2731
}
2832

@@ -46,6 +50,14 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
4650
])
4751
->addEventListener(FormEvents::POST_SET_DATA, [$this, 'onPostSetData'])
4852
->addEventListener(FormEvents::SUBMIT, [$this, 'onSubmit']);
53+
54+
if ($this->distributionsEnabled) {
55+
$builder->add('distributionStrategy', EnumType::class, [
56+
'label' => 'Resolve distributions',
57+
'class' => PackageDistributionStrategy::class,
58+
'expanded' => true,
59+
]);
60+
}
4961
}
5062

5163
public function onPostSetData(PostSetDataEvent $event): void

src/Message/ResolveDistribution.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
{
1010
public function __construct(
1111
public int $versionId,
12-
public string $reference,
13-
public string $type,
12+
public ?string $reference = null,
13+
public ?string $type = null,
1414
) {
1515
}
1616
}

src/Package/PackageDistributionResolver.php

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,17 @@ public function __construct(
3939
$this->distributionStoragePath = "$storagePath/distribution";
4040
}
4141

42-
public function exists(string $packageName, string $versionName, string $reference, string $type): bool
42+
public function exists(string $packageName, string $versionName, ?string $reference, ?string $type): bool
4343
{
44-
return $this->filesystem->exists($this->path($packageName, $versionName, $reference, $type));
44+
return null !== $reference && null !== $type && $this->filesystem->exists($this->path($packageName, $versionName, $reference, $type));
4545
}
4646

4747
public function path(string $packageName, string $versionName, string $reference, string $type): string
4848
{
4949
return "$this->distributionStoragePath/$packageName/$versionName-$reference.$type";
5050
}
5151

52-
public function resolve(Version $version, string $reference, string $type, bool $async): bool
52+
public function resolve(Version $version, ?string $reference, ?string $type, bool $async): bool
5353
{
5454
$package = $version->getPackage();
5555
$packageName = $package->getName();
@@ -73,16 +73,30 @@ public function resolve(Version $version, string $reference, string $type, bool
7373
return false;
7474
}
7575

76-
$hasDistribution = null !== $version->getDist();
76+
$result = false;
7777

78-
return match (true) {
79-
$this->buildDistributions && !$hasDistribution => $this->build($version, $reference, $type),
80-
$this->mirrorDistributions && $hasDistribution => $this->mirror($version, $reference, $type),
81-
default => false,
82-
};
78+
// Build the distribution from source
79+
if (
80+
$this->buildDistributions
81+
&& $version->getPackage()->getFetchStrategy()->isVcs()
82+
) {
83+
$result = $this->build($version, $reference ?? $version->getSourceReference(), $type ?? $version->getSourceType());
84+
}
85+
86+
// Mirror the distribution from a remote source if it can't be built from source
87+
$distributionAvailable = null !== $version->getDist();
88+
if (
89+
!$result
90+
&& $this->mirrorDistributions
91+
&& $distributionAvailable
92+
) {
93+
$result = $this->mirror($version, $reference ?? $version->getDistReference(), $type ?? $version->getDistType());
94+
}
95+
96+
return $result;
8397
}
8498

85-
private function build(Version $version, string $reference, string $type): bool
99+
private function build(Version $version, ?string $reference, ?string $type): bool
86100
{
87101
// Skip building of outdated references for now
88102
if ($reference !== $version->getSourceReference()) {
@@ -119,7 +133,7 @@ private function build(Version $version, string $reference, string $type): bool
119133
return true;
120134
}
121135

122-
private function mirror(Version $version, string $reference, string $type): bool
136+
private function mirror(Version $version, ?string $reference, ?string $type): bool
123137
{
124138
// Skip mirroring of outdated references for now
125139
if ($reference !== $version->getDistReference()) {

src/Package/PackageMetadataResolver.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ public function resolve(Package $package): void
7979
match ($package->getFetchStrategy()) {
8080
PackageFetchStrategy::Mirror => $this->resolveRegistryPackage($package),
8181
PackageFetchStrategy::Vcs => $this->resolveVcsPackage($package),
82-
default => throw new \LogicException(),
8382
};
8483

8584
$this->messenger->dispatch(new DumpPackageProvider($package->getId()));

templates/dashboard/packages/edit.html.twig

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<div class="form-fieldset-header">
2222
<div class="form-fieldset-title">
2323
<span class="form-fieldset-title-content">
24-
Repository
24+
{{ 'Repository'|trans }}
2525
</span>
2626
</div>
2727
</div>
@@ -42,7 +42,7 @@
4242
<div class="form-fieldset-header">
4343
<div class="form-fieldset-title">
4444
<span class="form-fieldset-title-content">
45-
Mirroring
45+
{{ 'Mirroring'|trans }}
4646
</span>
4747
</div>
4848
</div>
@@ -59,5 +59,27 @@
5959
</div>
6060
</fieldset>
6161
</div>
62+
63+
{% if form.distributionStrategy is defined %}
64+
<div class="form-fieldset">
65+
<fieldset>
66+
<div class="form-fieldset-header">
67+
<div class="form-fieldset-title">
68+
<span class="form-fieldset-title-content">
69+
{{ 'Distributions'|trans }}
70+
</span>
71+
</div>
72+
</div>
73+
74+
<div class="form-fieldset-body">
75+
<div class="row">
76+
<div>
77+
{{ form_row(form.distributionStrategy) }}
78+
</div>
79+
</div>
80+
</div>
81+
</fieldset>
82+
</div>
83+
{% endif %}
6284
{{ form_end(form) }}
6385
{% endblock %}

0 commit comments

Comments
 (0)