Skip to content

Commit 5772246

Browse files
committed
Add distribution builder
1 parent 389f7d6 commit 5772246

5 files changed

Lines changed: 141 additions & 8 deletions

File tree

src/DependencyInjection/DirigentConfiguration.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ public function getConfigTreeBuilder(): TreeBuilder
7676
->scalarNode('periodic_update_interval')->defaultValue('P1W')->end()
7777
->end()
7878
->end()
79+
->arrayNode('dist_builder')
80+
->canBeEnabled()
81+
->children()
82+
->booleanNode('dev_packages')->defaultFalse()->end()
83+
->end()
84+
->end()
7985
->arrayNode('dist_mirroring')
8086
->canBeEnabled()
8187
->children()

src/DependencyInjection/DirigentExtension.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container
3535
$container->setParameter('dirigent.packages.periodic_updates', $mergedConfig['packages']['periodic_updates']);
3636
$container->setParameter('dirigent.packages.periodic_update_interval', $mergedConfig['packages']['periodic_update_interval']);
3737

38+
$container->setParameter('dirigent.dist_builder.enabled', $mergedConfig['dist_builder']['enabled']);
39+
$container->setParameter('dirigent.dist_builder.dev_packages', $mergedConfig['dist_builder']['dev_packages']);
40+
3841
$container->setParameter('dirigent.dist_mirroring.enabled', $mergedConfig['dist_mirroring']['enabled']);
3942
$container->setParameter('dirigent.dist_mirroring.preferred', $mergedConfig['dist_mirroring']['preferred']);
4043
$container->setParameter('dirigent.dist_mirroring.dev_packages', $mergedConfig['dist_mirroring']['dev_packages']);

src/Doctrine/Entity/Version.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace CodedMonkey\Dirigent\Doctrine\Entity;
44

55
use CodedMonkey\Dirigent\Doctrine\Repository\VersionRepository;
6+
use Composer\Package\Package as ComposerPackage;
7+
use Composer\Package\PackageInterface;
68
use Composer\Package\Version\VersionParser;
79
use Composer\Pcre\Preg;
810
use Doctrine\Common\Collections\ArrayCollection;
@@ -512,6 +514,21 @@ public function setReleasedAt(?\DateTimeInterface $releasedAt): void
512514
$this->releasedAt = $releasedAt;
513515
}
514516

517+
public function getSourceReference(): ?string
518+
{
519+
return $this->source['reference'] ?? null;
520+
}
521+
522+
public function getSourceType(): ?string
523+
{
524+
return $this->source['type'] ?? null;
525+
}
526+
527+
public function getSourceUrl(): ?string
528+
{
529+
return $this->source['url'] ?? null;
530+
}
531+
515532
public function getDistReference(): ?string
516533
{
517534
return $this->dist['reference'] ?? null;
@@ -702,6 +719,16 @@ public function toComposerArray(): array
702719
return $data;
703720
}
704721

722+
public function toComposerPackage(): PackageInterface
723+
{
724+
$composerPackage = new ComposerPackage($this->getName(), $this->getNormalizedVersion(), $this->getVersion());
725+
726+
$composerPackage->setSourceReference($this->getSourceReference());
727+
$composerPackage->setSourceUrl($this->getSourceUrl());
728+
729+
return $composerPackage;
730+
}
731+
705732
private function sortAuthorKeys(string $a, string $b): int
706733
{
707734
static $order = ['name' => 1, 'email' => 2, 'homepage' => 3, 'role' => 4];

src/Package/PackageDistributionResolver.php

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,33 @@
33
namespace CodedMonkey\Dirigent\Package;
44

55
use CodedMonkey\Dirigent\Composer\ComposerClient;
6+
use CodedMonkey\Dirigent\Composer\ConfigFactory;
67
use CodedMonkey\Dirigent\Doctrine\Entity\Version;
8+
use Composer\IO\NullIO;
9+
use Composer\Pcre\Preg;
10+
use Composer\Util\Filesystem as ComposerFilesystem;
11+
use Composer\Util\Git as GitUtility;
12+
use Composer\Util\ProcessExecutor;
13+
use Composer\Util\Url;
714
use Symfony\Component\DependencyInjection\Attribute\Autowire;
815
use Symfony\Component\Filesystem\Filesystem;
916

1017
readonly class PackageDistributionResolver
1118
{
1219
private Filesystem $filesystem;
13-
private string $storagePath;
20+
private string $distributionStoragePath;
1421

1522
public function __construct(
1623
private ComposerClient $composer,
24+
#[Autowire(param: 'dirigent.dist_builder.enabled')]
25+
private bool $buildDistributions,
26+
#[Autowire(param: 'dirigent.dist_builder.dev_packages')]
27+
private bool $buildDevDistributions,
1728
#[Autowire(param: 'dirigent.storage.path')]
1829
string $storagePath,
1930
) {
2031
$this->filesystem = new Filesystem();
21-
$this->storagePath = "$storagePath/distribution";
32+
$this->distributionStoragePath = "$storagePath/distribution";
2233
}
2334

2435
public function exists(string $packageName, string $packageVersion, string $reference, string $type): bool
@@ -28,7 +39,7 @@ public function exists(string $packageName, string $packageVersion, string $refe
2839

2940
public function path(string $packageName, string $packageVersion, string $reference, string $type): string
3041
{
31-
return "{$this->storagePath}/{$packageName}/{$packageVersion}-{$reference}.{$type}";
42+
return "$this->distributionStoragePath/$packageName/$packageVersion-$reference.$type";
3243
}
3344

3445
public function resolve(Version $version, string $reference, string $type): bool
@@ -41,17 +52,76 @@ public function resolve(Version $version, string $reference, string $type): bool
4152
return true;
4253
}
4354

44-
if ($reference !== $version->getDistReference() || $type !== $version->getDistType()) {
55+
if (
56+
null === $version->getDist()
57+
&& $this->buildDistributions
58+
&& (!$version->isDevelopment() || $this->buildDevDistributions)
59+
) {
60+
return $this->build($version, $reference, $type);
61+
} elseif (null !== $version->getDist()) {
62+
return $this->mirror($version, $reference, $type);
63+
}
64+
65+
return false;
66+
}
67+
68+
private function build(Version $version, string $reference, string $type): bool
69+
{
70+
// Skip building of outdated references for now
71+
if ($reference !== $version->getSourceReference()) {
72+
return false;
73+
}
74+
75+
// Only provide .zip support for now
76+
if ('zip' !== $type) {
77+
return false;
78+
}
79+
80+
$package = $version->getPackage();
81+
$repositoryUrl = $package->getRepositoryUrl();
82+
$distributionPath = $this->path($package->getName(), $version->getNormalizedVersion(), $reference, $type);
83+
84+
$composerConfig = ConfigFactory::createForVcsRepository($repositoryUrl, $package->getRepositoryCredentials());
85+
86+
$gitUtility = new GitUtility(
87+
$io = new NullIO(),
88+
$composerConfig,
89+
$process = new ProcessExecutor($io),
90+
new ComposerFilesystem($process),
91+
);
92+
93+
$cacheRepositoryName = Preg::replace('{[^a-z0-9.]}i', '-', Url::sanitize($repositoryUrl));
94+
$cachePath = $composerConfig->get('cache-vcs-dir') . '/' . $cacheRepositoryName . '/';
95+
96+
$this->filesystem->mkdir(dirname($distributionPath));
97+
98+
$gitUtility->runCommands([
99+
['git', 'archive', '--format=zip', "--output=$distributionPath", $reference],
100+
], $repositoryUrl, $cachePath);
101+
102+
return true;
103+
}
104+
105+
private function mirror(Version $version, string $reference, string $type): bool
106+
{
107+
// Skip mirroring of outdated references for now
108+
if ($reference !== $version->getDistReference()) {
109+
return false;
110+
}
111+
112+
// The distribution type must match the origin format
113+
if ($type !== $version->getDistType()) {
45114
return false;
46115
}
47116

48-
$distUrl = $version->getDistUrl();
49-
$path = $this->path($packageName, $packageVersion, $reference, $type);
117+
$package = $version->getPackage();
118+
$distributionUrl = $version->getDistUrl();
119+
$distributionPath = $this->path($package->getName(), $version->getNormalizedVersion(), $reference, $type);
50120

51-
$this->filesystem->mkdir(dirname($path));
121+
$this->filesystem->mkdir(dirname($distributionPath));
52122

53123
$httpDownloader = $this->composer->createHttpDownloader();
54-
$httpDownloader->copy($distUrl, $path);
124+
$httpDownloader->copy($distributionUrl, $distributionPath);
55125

56126
return true;
57127
}

src/Package/PackageProviderManager.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@
66
use Composer\MetadataMinifier\MetadataMinifier;
77
use Symfony\Component\DependencyInjection\Attribute\Autowire;
88
use Symfony\Component\Filesystem\Filesystem;
9+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
10+
use Symfony\Component\Routing\RouterInterface;
911

1012
readonly class PackageProviderManager
1113
{
1214
private Filesystem $filesystem;
1315
private string $storagePath;
1416

1517
public function __construct(
18+
private RouterInterface $router,
19+
#[Autowire(param: 'dirigent.dist_builder.enabled')]
20+
private bool $buildDistributions,
21+
#[Autowire(param: 'dirigent.dist_builder.dev_packages')]
22+
private bool $buildDevDistributions,
1623
#[Autowire(param: 'dirigent.storage.path')]
1724
string $storagePath,
1825
) {
@@ -31,6 +38,26 @@ public function dump(Package $package): void
3138
foreach ($versions as $version) {
3239
$versionData = $version->toComposerArray();
3340

41+
if (
42+
!$version->getDist()
43+
&& $this->buildDistributions
44+
&& (!$version->isDevelopment() || $this->buildDevDistributions)
45+
) {
46+
$distributionUrl = $this->router->generate('api_package_distribution', [
47+
'packageName' => $package->getName(),
48+
'packageVersion' => $version->getNormalizedVersion(),
49+
'reference' => $version->getSourceReference(),
50+
'type' => 'zip',
51+
], UrlGeneratorInterface::ABSOLUTE_URL);
52+
53+
$versionData['dist'] = [
54+
'type' => 'zip',
55+
'url' => $distributionUrl,
56+
'reference' => $version->getSourceReference(),
57+
'shasum' => '',
58+
];
59+
}
60+
3461
if (!$version->isDevelopment()) {
3562
$releasePackages[] = $versionData;
3663
} else {

0 commit comments

Comments
 (0)