Skip to content

Commit b2864c9

Browse files
authored
Merge pull request php#705 from asgrim/701-fallback-improvements
701: add `--suppress-download-url-method` for end users
2 parents 7dbfa3f + 1fe3ed2 commit b2864c9

11 files changed

Lines changed: 279 additions & 1 deletion

src/Command/BuildCommand.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
147147
PieOperation::Build,
148148
$configureOptionsValues,
149149
false, // setting up INI not needed for build
150+
suppressedDownloadUrlMethods: CommandHelper::determineSuppressedDownloadUrlMethods($input),
150151
),
151152
);
152153

src/Command/CommandHelper.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Php\Pie\DependencyResolver\RequestedPackageAndVersion;
2424
use Php\Pie\DependencyResolver\ResolvedPackageRequest;
2525
use Php\Pie\DependencyResolver\UnableToResolveRequirement;
26+
use Php\Pie\Downloading\DownloadUrlMethod;
2627
use Php\Pie\ExtensionName;
2728
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
2829
use Php\Pie\Platform as PiePlatform;
@@ -36,6 +37,7 @@
3637
use Symfony\Component\Console\Input\InputInterface;
3738
use Symfony\Component\Console\Input\InputOption;
3839
use Throwable;
40+
use ValueError;
3941
use Webmozart\Assert\Assert;
4042

4143
use function array_key_exists;
@@ -44,6 +46,7 @@
4446
use function assert;
4547
use function count;
4648
use function explode;
49+
use function implode;
4750
use function is_array;
4851
use function is_dir;
4952
use function is_string;
@@ -75,6 +78,7 @@ final class CommandHelper
7578
private const OPTION_MAKE_PARALLEL_JOBS = 'make-parallel-jobs';
7679
private const OPTION_SKIP_ENABLE_EXTENSION = 'skip-enable-extension';
7780
private const OPTION_FORCE = 'force';
81+
private const OPTION_SUPPRESS_DOWNLOAD_URL_METHOD = 'suppress-download-url-method';
7882
private const OPTION_NO_CACHE = 'no-cache';
7983
private const OPTION_AUTO_INSTALL_BUILD_TOOLS = 'auto-install-build-tools';
8084
private const OPTION_SUPPRESS_BUILD_TOOLS_CHECK = 'no-build-tools-check';
@@ -152,6 +156,14 @@ public static function configureDownloadBuildInstallOptions(Command $command, bo
152156
'To attempt to install a version that doesn\'t match the version constraints from the meta-data, for instance to install an older version than recommended, or when the signature is not available.',
153157
);
154158

159+
$command->addOption(
160+
self::OPTION_SUPPRESS_DOWNLOAD_URL_METHOD,
161+
null,
162+
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
163+
'Do not use the specified download URL methods if they are supported by the extension. May be specified multiple times. Valid values: '
164+
. implode(', ', array_map(static fn (DownloadUrlMethod $downloadUrlMethod): string => $downloadUrlMethod->value, DownloadUrlMethod::cases())),
165+
);
166+
155167
$command->addOption(
156168
self::OPTION_WORKING_DIRECTORY,
157169
'd',
@@ -293,6 +305,35 @@ public static function determineForceInstallingPackageVersion(InputInterface $in
293305
return $input->hasOption(self::OPTION_FORCE) && $input->getOption(self::OPTION_FORCE);
294306
}
295307

308+
/** @return list<DownloadUrlMethod> */
309+
public static function determineSuppressedDownloadUrlMethods(InputInterface $input): array
310+
{
311+
if (! $input->hasOption(self::OPTION_SUPPRESS_DOWNLOAD_URL_METHOD)) {
312+
return [];
313+
}
314+
315+
$suppressedDownloadUrlMethods = $input->getOption(self::OPTION_SUPPRESS_DOWNLOAD_URL_METHOD);
316+
assert(is_array($suppressedDownloadUrlMethods));
317+
318+
return array_values(array_map(
319+
static function (mixed $suppressedDownloadUrlMethod): DownloadUrlMethod {
320+
assert(is_string($suppressedDownloadUrlMethod) && $suppressedDownloadUrlMethod !== '');
321+
322+
try {
323+
return DownloadUrlMethod::from($suppressedDownloadUrlMethod);
324+
} catch (ValueError) {
325+
throw new InvalidArgumentException(sprintf(
326+
'Invalid value "%s" for --%s; valid values are: %s',
327+
$suppressedDownloadUrlMethod,
328+
self::OPTION_SUPPRESS_DOWNLOAD_URL_METHOD,
329+
implode(', ', array_map(static fn (DownloadUrlMethod $downloadUrlMethod): string => $downloadUrlMethod->value, DownloadUrlMethod::cases())),
330+
));
331+
}
332+
},
333+
$suppressedDownloadUrlMethods,
334+
));
335+
}
336+
296337
public static function autoInstallBuildTools(InputInterface $input): bool
297338
{
298339
return $input->hasOption(self::OPTION_AUTO_INSTALL_BUILD_TOOLS)

src/Command/DownloadCommand.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
7373
PieOperation::Download,
7474
[], // Configure options are not needed for download only
7575
false, // setting up INI not needed for download
76+
suppressedDownloadUrlMethods: CommandHelper::determineSuppressedDownloadUrlMethods($input),
7677
),
7778
);
7879

src/Command/InstallCommand.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
195195
$configureOptionsValues,
196196
CommandHelper::determineAttemptToSetupIniFile($input),
197197
installAllPackages: $installFromLock,
198+
suppressedDownloadUrlMethods: CommandHelper::determineSuppressedDownloadUrlMethods($input),
198199
),
199200
);
200201

src/Command/UpgradeCommand.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
102102
$configureOptions,
103103
CommandHelper::determineAttemptToSetupIniFile($input),
104104
installAllPackages: true,
105+
suppressedDownloadUrlMethods: CommandHelper::determineSuppressedDownloadUrlMethods($input),
105106
),
106107
);
107108

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Php\Pie\ComposerIntegration\Listeners;
6+
7+
use Php\Pie\DependencyResolver\Package;
8+
use RuntimeException;
9+
10+
use function sprintf;
11+
12+
class AllDownloadUrlMethodsSuppressed extends RuntimeException
13+
{
14+
public static function forPackage(Package $piePackage): self
15+
{
16+
return new self(sprintf(
17+
'Could not find a way to download %s as all possible download URL methods were suppressed',
18+
$piePackage->name(),
19+
));
20+
}
21+
}

src/ComposerIntegration/Listeners/OverrideDownloadUrlInstallListener.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Throwable;
2222

2323
use function array_walk;
24+
use function in_array;
2425
use function pathinfo;
2526

2627
use const PATHINFO_EXTENSION;
@@ -75,6 +76,25 @@ function (OperationInterface $operation): void {
7576
$targetPlatform = $this->composerRequest->targetPlatform;
7677
$downloadUrlMethods = DownloadUrlMethod::possibleDownloadUrlMethodsForPackage($piePackage, $targetPlatform);
7778

79+
if ($this->composerRequest->suppressedDownloadUrlMethods !== []) {
80+
$remainingDownloadUrlMethods = [];
81+
82+
foreach ($downloadUrlMethods as $downloadUrlMethod) {
83+
if (in_array($downloadUrlMethod, $this->composerRequest->suppressedDownloadUrlMethods, true)) {
84+
$this->io->write('Suppressing download method: ' . $downloadUrlMethod->value, verbosity: IOInterface::VERBOSE);
85+
continue;
86+
}
87+
88+
$remainingDownloadUrlMethods[] = $downloadUrlMethod;
89+
}
90+
91+
if ($remainingDownloadUrlMethods === []) {
92+
throw AllDownloadUrlMethodsSuppressed::forPackage($piePackage);
93+
}
94+
95+
$downloadUrlMethods = $remainingDownloadUrlMethods;
96+
}
97+
7898
$selectedDownloadUrlMethod = null;
7999
$downloadMethodFailures = [];
80100

src/ComposerIntegration/PieComposerRequest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Composer\IO\IOInterface;
88
use Php\Pie\DependencyResolver\RequestedPackageAndVersion;
9+
use Php\Pie\Downloading\DownloadUrlMethod;
910
use Php\Pie\Platform\TargetPlatform;
1011

1112
use function array_map;
@@ -23,7 +24,8 @@ final class PieComposerRequest
2324

2425
/**
2526
* @param list<RequestedPackageAndVersion> $requestedPackages
26-
* @param array<string, list<non-empty-string>> $configureOptions Keyed by package name
27+
* @param array<string, list<non-empty-string>> $configureOptions Keyed by package name
28+
* @param list<DownloadUrlMethod> $suppressedDownloadUrlMethods
2729
*/
2830
public function __construct(
2931
public readonly IOInterface $pieOutput,
@@ -33,6 +35,7 @@ public function __construct(
3335
public readonly array $configureOptions,
3436
public readonly bool $attemptToSetupIniFile,
3537
public readonly bool $installAllPackages = false,
38+
public readonly array $suppressedDownloadUrlMethods = [],
3639
) {
3740
$this->requestedPackageNames = array_map(static fn (RequestedPackageAndVersion $request) => $request->package, $this->requestedPackages);
3841
}

test/unit/Command/CommandHelperTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Php\Pie\DependencyResolver\RequestedPackageAndVersion;
2424
use Php\Pie\DependencyResolver\ResolvedPackageRequest;
2525
use Php\Pie\DependencyResolver\UnableToResolveRequirement;
26+
use Php\Pie\Downloading\DownloadUrlMethod;
2627
use Php\Pie\Platform\TargetPlatform;
2728
use PHPUnit\Framework\Attributes\CoversClass;
2829
use PHPUnit\Framework\Attributes\DataProvider;
@@ -340,6 +341,41 @@ public function testWindowsMachinesCannotUseWithPhpizePathOption(): void
340341
CommandHelper::determineTargetPlatformFromInputs($input, $io);
341342
}
342343

344+
public function testDetermineSuppressedDownloadUrlMethodsDefaultsToEmpty(): void
345+
{
346+
$command = new Command();
347+
$input = new ArrayInput([]);
348+
CommandHelper::configureDownloadBuildInstallOptions($command);
349+
CommandHelper::validateInput($input, $command);
350+
351+
self::assertSame([], CommandHelper::determineSuppressedDownloadUrlMethods($input));
352+
}
353+
354+
public function testDetermineSuppressedDownloadUrlMethodsParsesGivenValues(): void
355+
{
356+
$command = new Command();
357+
$input = new ArrayInput(['--suppress-download-url-method' => ['composer-default', 'pre-packaged-source']]);
358+
CommandHelper::configureDownloadBuildInstallOptions($command);
359+
CommandHelper::validateInput($input, $command);
360+
361+
self::assertSame(
362+
[DownloadUrlMethod::ComposerDefaultDownload, DownloadUrlMethod::PrePackagedSourceDownload],
363+
CommandHelper::determineSuppressedDownloadUrlMethods($input),
364+
);
365+
}
366+
367+
public function testDetermineSuppressedDownloadUrlMethodsThrowsForInvalidValue(): void
368+
{
369+
$command = new Command();
370+
$input = new ArrayInput(['--suppress-download-url-method' => ['not-a-real-method']]);
371+
CommandHelper::configureDownloadBuildInstallOptions($command);
372+
CommandHelper::validateInput($input, $command);
373+
374+
$this->expectException(InvalidArgumentException::class);
375+
$this->expectExceptionMessage('Invalid value "not-a-real-method" for --suppress-download-url-method; valid values are: composer-default, windows-binary, pre-packaged-source, pre-packaged-binary');
376+
CommandHelper::determineSuppressedDownloadUrlMethods($input);
377+
}
378+
343379
public function testListRepositories(): void
344380
{
345381
$io = new BufferIO();
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Php\PieUnitTest\ComposerIntegration\Listeners;
6+
7+
use Composer\Package\CompletePackageInterface;
8+
use Php\Pie\ComposerIntegration\Listeners\AllDownloadUrlMethodsSuppressed;
9+
use Php\Pie\DependencyResolver\Package;
10+
use Php\Pie\ExtensionName;
11+
use Php\Pie\ExtensionType;
12+
use PHPUnit\Framework\Attributes\CoversClass;
13+
use PHPUnit\Framework\TestCase;
14+
15+
#[CoversClass(AllDownloadUrlMethodsSuppressed::class)]
16+
final class AllDownloadUrlMethodsSuppressedTest extends TestCase
17+
{
18+
public function testForPackage(): void
19+
{
20+
self::assertSame(
21+
'Could not find a way to download foo/bar as all possible download URL methods were suppressed',
22+
AllDownloadUrlMethodsSuppressed::forPackage(new Package(
23+
$this->createMock(CompletePackageInterface::class),
24+
ExtensionType::PhpModule,
25+
ExtensionName::normaliseFromString('bar'),
26+
'foo/bar',
27+
'1.2.3',
28+
null,
29+
))->getMessage(),
30+
);
31+
}
32+
}

0 commit comments

Comments
 (0)