Skip to content

Commit

Permalink
Merge pull request #165 from Bellangelo/add-force-option
Browse files Browse the repository at this point in the history
Add support for the --force option
  • Loading branch information
asgrim authored Dec 31, 2024
2 parents b39ab4c + 0c0a476 commit 883447d
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 22 deletions.
21 changes: 16 additions & 5 deletions src/Command/BuildCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ public function configure(): void

public function execute(InputInterface $input, OutputInterface $output): int
{
$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);

$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);
$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
$forceInstallPackageVersion = CommandHelper::determineForceInstallingPackageVersion($input);

$composer = PieComposerFactory::createPieComposer(
$this->container,
Expand All @@ -58,7 +58,12 @@ public function execute(InputInterface $input, OutputInterface $output): int
),
);

$package = ($this->dependencyResolver)($composer, $targetPlatform, $requestedNameAndVersion);
$package = ($this->dependencyResolver)(
$composer,
$targetPlatform,
$requestedNameAndVersion,
$forceInstallPackageVersion,
);
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName->nameWithExtPrefix()));

// Now we know what package we have, we can validate the configure options for the command and re-create the
Expand All @@ -80,7 +85,13 @@ public function execute(InputInterface $input, OutputInterface $output): int
);

try {
($this->composerIntegrationHandler)($package, $composer, $targetPlatform, $requestedNameAndVersion);
($this->composerIntegrationHandler)(
$package,
$composer,
$targetPlatform,
$requestedNameAndVersion,
$forceInstallPackageVersion,
);
} catch (ComposerRunFailed $composerRunFailed) {
$output->writeln('<error>' . $composerRunFailed->getMessage() . '</error>');

Expand Down
12 changes: 12 additions & 0 deletions src/Command/CommandHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ final class CommandHelper
private const OPTION_WITH_PHPIZE_PATH = 'with-phpize-path';
private const OPTION_MAKE_PARALLEL_JOBS = 'make-parallel-jobs';
private const OPTION_SKIP_ENABLE_EXTENSION = 'skip-enable-extension';
private const OPTION_FORCE = 'force';

/** @psalm-suppress UnusedConstructor */
private function __construct()
Expand Down Expand Up @@ -86,6 +87,12 @@ public static function configureDownloadBuildInstallOptions(Command $command): v
InputOption::VALUE_NONE,
'Specify this to skip attempting to enable the extension in php.ini',
);
$command->addOption(
self::OPTION_FORCE,
null,
InputOption::VALUE_NONE,
'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.',
);

self::configurePhpConfigOptions($command);

Expand Down Expand Up @@ -166,6 +173,11 @@ public static function determineAttemptToSetupIniFile(InputInterface $input): bo
return ! $input->hasOption(self::OPTION_SKIP_ENABLE_EXTENSION) || ! $input->getOption(self::OPTION_SKIP_ENABLE_EXTENSION);
}

public static function determineForceInstallingPackageVersion(InputInterface $input): bool
{
return $input->hasOption(self::OPTION_FORCE) && $input->getOption(self::OPTION_FORCE);
}

public static function determinePhpizePathFromInputs(InputInterface $input): PhpizePath|null
{
if ($input->hasOption(self::OPTION_WITH_PHPIZE_PATH)) {
Expand Down
21 changes: 16 additions & 5 deletions src/Command/DownloadCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ public function execute(InputInterface $input, OutputInterface $output): int
{
CommandHelper::validateInput($input, $this);

$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);

$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);
$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
$forceInstallPackageVersion = CommandHelper::determineForceInstallingPackageVersion($input);

$composer = PieComposerFactory::createPieComposer(
$this->container,
Expand All @@ -60,11 +60,22 @@ public function execute(InputInterface $input, OutputInterface $output): int
),
);

$package = ($this->dependencyResolver)($composer, $targetPlatform, $requestedNameAndVersion);
$package = ($this->dependencyResolver)(
$composer,
$targetPlatform,
$requestedNameAndVersion,
$forceInstallPackageVersion,
);
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName->nameWithExtPrefix()));

try {
($this->composerIntegrationHandler)($package, $composer, $targetPlatform, $requestedNameAndVersion);
($this->composerIntegrationHandler)(
$package,
$composer,
$targetPlatform,
$requestedNameAndVersion,
$forceInstallPackageVersion,
);
} catch (ComposerRunFailed $composerRunFailed) {
$output->writeln('<error>' . $composerRunFailed->getMessage() . '</error>');

Expand Down
7 changes: 6 additions & 1 deletion src/Command/InfoCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ public function execute(InputInterface $input, OutputInterface $output): int
),
);

$package = ($this->dependencyResolver)($composer, $targetPlatform, $requestedNameAndVersion);
$package = ($this->dependencyResolver)(
$composer,
$targetPlatform,
$requestedNameAndVersion,
CommandHelper::determineForceInstallingPackageVersion($input),
);
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName->nameWithExtPrefix()));

$output->writeln(sprintf('Extension name: %s', $package->extensionName->name()));
Expand Down
21 changes: 16 additions & 5 deletions src/Command/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ public function execute(InputInterface $input, OutputInterface $output): int
$output->writeln('This command may need elevated privileges, and may prompt you for your password.');
}

$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);

$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);
$requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input);
$forceInstallPackageVersion = CommandHelper::determineForceInstallingPackageVersion($input);

$composer = PieComposerFactory::createPieComposer(
$this->container,
Expand All @@ -63,7 +63,12 @@ public function execute(InputInterface $input, OutputInterface $output): int
),
);

$package = ($this->dependencyResolver)($composer, $targetPlatform, $requestedNameAndVersion);
$package = ($this->dependencyResolver)(
$composer,
$targetPlatform,
$requestedNameAndVersion,
$forceInstallPackageVersion,
);
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName->nameWithExtPrefix()));

// Now we know what package we have, we can validate the configure options for the command and re-create the
Expand All @@ -85,7 +90,13 @@ public function execute(InputInterface $input, OutputInterface $output): int
);

try {
($this->composerIntegrationHandler)($package, $composer, $targetPlatform, $requestedNameAndVersion);
($this->composerIntegrationHandler)(
$package,
$composer,
$targetPlatform,
$requestedNameAndVersion,
$forceInstallPackageVersion,
);
} catch (ComposerRunFailed $composerRunFailed) {
$output->writeln('<error>' . $composerRunFailed->getMessage() . '</error>');

Expand Down
11 changes: 9 additions & 2 deletions src/ComposerIntegration/ComposerIntegrationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Php\Pie\ComposerIntegration;

use Composer\Composer;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Installer;
use Composer\Json\JsonManipulator;
use Php\Pie\DependencyResolver\Package;
Expand All @@ -27,8 +28,13 @@ public function __construct(
) {
}

public function __invoke(Package $package, Composer $composer, TargetPlatform $targetPlatform, RequestedPackageAndVersion $requestedPackageAndVersion): void
{
public function __invoke(
Package $package,
Composer $composer,
TargetPlatform $targetPlatform,
RequestedPackageAndVersion $requestedPackageAndVersion,
bool $forceInstallPackageVersion,
): void {
$versionSelector = VersionSelectorFactory::make($composer, $requestedPackageAndVersion, $targetPlatform);

$recommendedRequireVersion = $requestedPackageAndVersion->version;
Expand Down Expand Up @@ -64,6 +70,7 @@ public function __invoke(Package $package, Composer $composer, TargetPlatform $t
->setInstall(true)
->setIgnoredTypes([])
->setDryRun(false)
->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($forceInstallPackageVersion))
->setDownloadOnly(false);

if (file_exists(PieComposerFactory::getLockFile($pieComposerJson))) {
Expand Down
7 changes: 6 additions & 1 deletion src/DependencyResolver/DependencyResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@
interface DependencyResolver
{
/** @throws UnableToResolveRequirement */
public function __invoke(Composer $composer, TargetPlatform $targetPlatform, RequestedPackageAndVersion $requestedPackageAndVersion): Package;
public function __invoke(
Composer $composer,
TargetPlatform $targetPlatform,
RequestedPackageAndVersion $requestedPackageAndVersion,
bool $forceInstallPackageVersion,
): Package;
}
10 changes: 8 additions & 2 deletions src/DependencyResolver/ResolveDependencyWithComposer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Php\Pie\DependencyResolver;

use Composer\Composer;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Package\CompletePackageInterface;
use Php\Pie\ComposerIntegration\QuieterConsoleIO;
use Php\Pie\ComposerIntegration\VersionSelectorFactory;
Expand All @@ -23,13 +24,18 @@ public function __construct(
) {
}

public function __invoke(Composer $composer, TargetPlatform $targetPlatform, RequestedPackageAndVersion $requestedPackageAndVersion): Package
{
public function __invoke(
Composer $composer,
TargetPlatform $targetPlatform,
RequestedPackageAndVersion $requestedPackageAndVersion,
bool $forceInstallPackageVersion,
): Package {
$versionSelector = VersionSelectorFactory::make($composer, $requestedPackageAndVersion, $targetPlatform);

$package = $versionSelector->findBestCandidate(
$requestedPackageAndVersion->package,
$requestedPackageAndVersion->version,
platformRequirementFilter: PlatformRequirementFilterFactory::fromBoolOrList($forceInstallPackageVersion),
io: $this->arrayCollectionIo,
);

Expand Down
22 changes: 22 additions & 0 deletions test/integration/Command/DownloadCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,26 @@ public function testDownloadCommandFailsWhenUsingIncompatiblePhpVersion(): void
// 1.0.0 is only compatible with PHP 8.3.0
$this->commandTester->execute(['requested-package-and-version' => self::TEST_PACKAGE . ':1.0.0']);
}

#[RequiresOperatingSystemFamily('Linux')]
#[RequiresPhp('<8.2')]
public function testDownloadCommandPassesWhenUsingIncompatiblePhpVersionWithForceOption(): void
{
// 1.0.1 is only compatible with PHP 8.3.0
$incompatiblePackage = self::TEST_PACKAGE . ':1.0.1';

$this->commandTester->execute(
[
'requested-package-and-version' => $incompatiblePackage,
'--force' => true,
],
);

$this->commandTester->assertCommandIsSuccessful();

$outputString = $this->commandTester->getDisplay();

self::assertStringContainsString('Found package: ' . $incompatiblePackage . ' which provides', $outputString);
self::assertStringContainsString('Extracted ' . $incompatiblePackage . ' source to', $outputString);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public function testDependenciesAreResolvedToExpectedVersions(
),
$targetPlatform,
$requestedPackageAndVersion,
false,
);

self::assertSame($expectedVersion, $package->version);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public function testPackageThatCanBeResolved(): void

$package = (new ResolveDependencyWithComposer(
$this->createMock(QuieterConsoleIO::class),
))($this->composer, $targetPlatform, new RequestedPackageAndVersion('asgrim/example-pie-extension', '^1.0'));
))($this->composer, $targetPlatform, new RequestedPackageAndVersion('asgrim/example-pie-extension', '^1.0'), false);

self::assertSame('asgrim/example-pie-extension', $package->name);
self::assertStringStartsWith('1.', $package->version);
Expand Down Expand Up @@ -118,9 +118,51 @@ public function testPackageThatCannotBeResolvedThrowsException(array $platformOv
$package,
$version,
),
false,
);
}

/**
* @param array<string, string> $platformOverrides
* @param non-empty-string $package
* @param non-empty-string $version
*/
#[DataProvider('unresolvableDependencies')]
public function testUnresolvedPackageCanBeInstalledWithForceOption(array $platformOverrides, string $package, string $version): void
{
$phpBinaryPath = $this->createMock(PhpBinaryPath::class);
$phpBinaryPath->expects(self::once())
->method('version')
->willReturn($platformOverrides['php']);

$targetPlatform = new TargetPlatform(
OperatingSystem::NonWindows,
OperatingSystemFamily::Linux,
$phpBinaryPath,
Architecture::x86_64,
ThreadSafetyMode::ThreadSafe,
1,
null,
);

$this->expectException(UnableToResolveRequirement::class);

$package = (new ResolveDependencyWithComposer(
$this->createMock(QuieterConsoleIO::class),
))(
$this->composer,
$targetPlatform,
new RequestedPackageAndVersion(
$package,
$version,
),
true,
);

self::assertSame('asgrim/example-pie-extension', $package->name);
self::assertStringStartsWith('1.', $package->version);
}

public function testZtsOnlyPackageCannotBeInstalledOnNtsSystem(): void
{
$pkg = new CompletePackage('test-vendor/test-package', '1.0.0.0', '1.0.0');
Expand Down Expand Up @@ -164,6 +206,7 @@ public function testZtsOnlyPackageCannotBeInstalledOnNtsSystem(): void
'test-vendor/test-package',
'1.0.0',
),
false,
);
}

Expand Down Expand Up @@ -210,6 +253,7 @@ public function testNtsOnlyPackageCannotBeInstalledOnZtsSystem(): void
'test-vendor/test-package',
'1.0.0',
),
false,
);
}

Expand Down Expand Up @@ -256,6 +300,7 @@ public function testExtensionCanOnlyBeInstalledIfOsFamilyIsCompatible(): void
'test-vendor/test-package',
'1.0.0',
),
false,
);
}

Expand Down Expand Up @@ -302,6 +347,7 @@ public function testExtensionCanOnlyBeInstalledIfOsFamilyIsNotInCompatible(): vo
'test-vendor/test-package',
'1.0.0',
),
false,
);
}
}

0 comments on commit 883447d

Please sign in to comment.