Skip to content

Commit e20935b

Browse files
committed
Add dedicated distribution mirroring route
1 parent bf1e40f commit e20935b

3 files changed

Lines changed: 78 additions & 9 deletions

File tree

‎config/packages/security.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ security:
1919
pattern: ^/(_profiler|_wdt|assets|build)/
2020
security: false
2121
api:
22-
pattern: ^/(packages.json|downloads|p2/|dist/)
22+
pattern: ^/(packages.json|downloads|p2/|dist/|dist-mirror/)
2323
provider: database_users
2424
http_basic:
2525
realm: api

‎src/Controller/ApiController.php‎

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public function __construct(
4242
private readonly PackageDistributionResolver $distributionResolver,
4343
private readonly PackageProviderManager $providerManager,
4444
private readonly MessageBusInterface $messenger,
45+
#[Autowire(param: 'dirigent.distributions.async_api_requests')]
46+
private readonly bool $asyncApiRequests,
4547
#[Autowire(param: 'dirigent.packages.dynamic_updates')]
4648
private readonly bool $dynamicUpdatesEnabled,
4749
#[Autowire(param: 'dirigent.metadata.default_mirror_fetch_strategy')]
@@ -64,7 +66,7 @@ public function root(RouterInterface $router): JsonResponse
6466
];
6567

6668
if ($this->getParameter('dirigent.distributions.mirror')) {
67-
$distributionUrlPattern = u($router->getRouteCollection()->get('api_package_distribution')->getPath())
69+
$distributionUrlPattern = u($router->getRouteCollection()->get('api_package_distribution_mirror')->getPath())
6870
->replace('{package}', '%package%')
6971
->replace('{version}', '%version%')
7072
->replace('{reference}', '%reference%')
@@ -106,18 +108,19 @@ public function packageMetadata(Request $request): Response
106108
return $response;
107109
}
108110

109-
#[Route('/dist/{package}/{version}-{reference}.{type}',
111+
#[Route('/dist/{package}/{version}-r{revision}-{reference}.{type}',
110112
name: 'api_package_distribution',
111113
requirements: [
112114
'package' => MapPackage::PACKAGE_REGEX,
113115
'version' => Requirement::CATCH_ALL,
116+
'revision' => Requirement::POSITIVE_INT,
114117
'reference' => '[a-z0-9]+',
115118
'type' => '(zip)',
116119
],
117120
methods: ['GET'],
118121
)]
119122
#[IsGrantedAccess]
120-
public function packageDistribution(Request $request, string $reference, string $type): Response
123+
public function packageDistribution(Request $request, int $revision, string $reference, string $type): Response
121124
{
122125
if (!$this->getParameter('dirigent.distributions.enabled')) {
123126
throw $this->createNotFoundException();
@@ -130,11 +133,55 @@ public function packageDistribution(Request $request, string $reference, string
130133
throw $this->createNotFoundException();
131134
}
132135

133-
if (null === $metadata = $this->metadataRepository->findOneByNormalizedNameAndReference($package, $versionName, $reference)) {
136+
if (null === $metadata = $this->metadataRepository->findOneByNormalizedNameAndRevision($package, $versionName, $revision)) {
134137
throw $this->createNotFoundException();
135138
}
136139

137-
if (!$this->distributionResolver->resolve($metadata, $type, async: $this->getParameter('dirigent.distributions.async_api_requests'))) {
140+
if (!$this->distributionResolver->resolve($metadata, $type, $this->asyncApiRequests)) {
141+
throw $this->createNotFoundException();
142+
}
143+
144+
$path = $this->distributionResolver->path($metadata, $reference);
145+
$filename = u("$packageName-$versionName-r$revision-$reference.$type")->replace('/', '-')->toString();
146+
147+
return $this->file($path, $filename);
148+
}
149+
150+
/**
151+
* Fetch mirrored package distributions through the API.
152+
*
153+
* The endpoint to fetch mirrored distributions is separate because when Composer requests a mirror, it doesn't
154+
* know which revision to fetch, so we default to the latest revision with the specified reference.
155+
*/
156+
#[Route('/dist-mirror/{package}/{version}-{reference}.{type}',
157+
name: 'api_package_distribution_mirror',
158+
requirements: [
159+
'package' => MapPackage::PACKAGE_REGEX,
160+
'version' => Requirement::CATCH_ALL,
161+
'reference' => '[a-z0-9]+',
162+
'type' => '(zip)',
163+
],
164+
methods: ['GET'],
165+
)]
166+
#[IsGrantedAccess]
167+
public function packageDistributionMirror(Request $request, string $reference, string $type): Response
168+
{
169+
if (!$this->getParameter('dirigent.distributions.enabled')) {
170+
throw $this->createNotFoundException();
171+
}
172+
173+
$packageName = $request->attributes->get('package');
174+
$versionName = $request->attributes->get('version');
175+
176+
if (null === $package = $this->findPackage($packageName)) {
177+
throw $this->createNotFoundException();
178+
}
179+
180+
if (null === $metadata = $this->metadataRepository->findOneByNormalizedNameAndDistributionReference($package, $versionName, $reference)) {
181+
throw $this->createNotFoundException();
182+
}
183+
184+
if (!$this->distributionResolver->resolve($metadata, $type, $this->asyncApiRequests)) {
138185
throw $this->createNotFoundException();
139186
}
140187

‎src/Doctrine/Repository/MetadataRepository.php‎

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,38 @@ public function findAllMetadataForVersion(Version $version): array
7373
);
7474
}
7575

76-
public function findOneByNormalizedNameAndReference(Package $package, string $normalizedVersionName, string $reference): ?Metadata
76+
/**
77+
* Finds the latest metadata for a given package by the distribution reference.
78+
*
79+
* Note that this method may return a different metadata revision than the one that was used to generate the
80+
* initial reference if there are any shenanigans going on with revisions that have the same reference.
81+
*/
82+
public function findOneByNormalizedNameAndDistributionReference(Package $package, string $normalizedVersionName, string $distributionReference): ?Metadata
83+
{
84+
$builder = $this->createQueryBuilder('metadata')
85+
->leftJoin('metadata.version', 'version')
86+
->andWhere('metadata.package = :package')
87+
->andWhere('version.normalizedName = :versionName')
88+
->andWhere('metadata.distributionReference = :reference')
89+
->orderBy('metadata.revision', 'DESC')
90+
->setParameter('package', $package)
91+
->setParameter('versionName', $normalizedVersionName)
92+
->setParameter('reference', $distributionReference)
93+
->setMaxResults(1);
94+
95+
return $builder->getQuery()->getOneOrNullResult();
96+
}
97+
98+
public function findOneByNormalizedNameAndRevision(Package $package, string $normalizedVersionName, int $revision): ?Metadata
7799
{
78100
$builder = $this->createQueryBuilder('metadata')
79101
->leftJoin('metadata.version', 'version')
80102
->andWhere('metadata.package = :package')
81103
->andWhere('version.normalizedName = :versionName')
82-
->andWhere('metadata.sourceReference = :reference OR (metadata.sourceReference IS NULL AND metadata.distributionReference = :reference)')
104+
->andWhere('metadata.revision = :revision')
83105
->setParameter('package', $package)
84106
->setParameter('versionName', $normalizedVersionName)
85-
->setParameter('reference', $reference)
107+
->setParameter('revision', $revision)
86108
->setMaxResults(1);
87109

88110
return $builder->getQuery()->getOneOrNullResult();

0 commit comments

Comments
 (0)