Skip to content

Commit 021b800

Browse files
authored
Merge pull request #270 from asgrim/use-version-constraints-for-php-project-install
Use version constraints for php project install
2 parents 357d0bf + aaf9373 commit 021b800

File tree

13 files changed

+149
-27
lines changed

13 files changed

+149
-27
lines changed

src/Command/InstallExtensionsForProjectCommand.php

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Php\Pie\Command;
66

77
use Composer\Package\Link;
8+
use Composer\Package\Version\VersionParser;
89
use OutOfRangeException;
910
use Php\Pie\ComposerIntegration\PieComposerFactory;
1011
use Php\Pie\ComposerIntegration\PieComposerRequest;
@@ -16,6 +17,8 @@
1617
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
1718
use Php\Pie\Installing\InstallForPhpProject\InstallPiePackageFromPath;
1819
use Php\Pie\Installing\InstallForPhpProject\InstallSelectedPackage;
20+
use Php\Pie\Platform\InstalledPiePackages;
21+
use Php\Pie\Util\Emoji;
1922
use Psr\Container\ContainerInterface;
2023
use Symfony\Component\Console\Attribute\AsCommand;
2124
use Symfony\Component\Console\Command\Command;
@@ -53,6 +56,7 @@ final class InstallExtensionsForProjectCommand extends Command
5356
public function __construct(
5457
private readonly ComposerFactoryForProject $composerFactoryForProject,
5558
private readonly DetermineExtensionsRequired $determineExtensionsRequired,
59+
private readonly InstalledPiePackages $installedPiePackages,
5660
private readonly FindMatchingPackages $findMatchingPackages,
5761
private readonly InstallSelectedPackage $installSelectedPackage,
5862
private readonly InstallPiePackageFromPath $installPiePackageFromPath,
@@ -138,28 +142,62 @@ public function execute(InputInterface $input, OutputInterface $output): int
138142
);
139143

140144
$phpEnabledExtensions = array_keys($targetPlatform->phpBinaryPath->extensions());
145+
$installedPiePackages = $this->installedPiePackages->allPiePackages($pieComposer);
141146

142147
$anyErrorsHappened = false;
143148

144149
array_walk(
145150
$extensionsRequired,
146-
function (Link $link) use ($pieComposer, $phpEnabledExtensions, $input, $output, $helper, &$anyErrorsHappened): void {
147-
$extension = ExtensionName::normaliseFromString($link->getTarget());
151+
function (Link $link) use ($pieComposer, $phpEnabledExtensions, $installedPiePackages, $input, $output, $helper, &$anyErrorsHappened): void {
152+
$extension = ExtensionName::normaliseFromString($link->getTarget());
153+
$linkRequiresConstraint = $link->getPrettyConstraint();
154+
155+
$piePackageVersion = null;
156+
if (in_array($extension->name(), array_keys($installedPiePackages))) {
157+
$piePackageVersion = $installedPiePackages[$extension->name()]->version();
158+
}
159+
160+
$piePackageVersionMatchesLinkConstraint = null;
161+
if ($piePackageVersion !== null) {
162+
$piePackageVersionMatchesLinkConstraint = $link
163+
->getConstraint()
164+
->matches(
165+
(new VersionParser())->parseConstraints($piePackageVersion),
166+
);
167+
}
148168

149169
if (in_array($extension->name(), $phpEnabledExtensions)) {
170+
if ($piePackageVersion !== null && $piePackageVersionMatchesLinkConstraint === false) {
171+
$output->writeln(sprintf(
172+
'%s: <comment>%s:%s</comment> %s Version %s is installed, but does not meet the version requirement %s',
173+
$link->getDescription(),
174+
$link->getTarget(),
175+
$linkRequiresConstraint,
176+
Emoji::WARNING,
177+
$piePackageVersion,
178+
$link->getConstraint()->getPrettyString(),
179+
));
180+
181+
return;
182+
}
183+
150184
$output->writeln(sprintf(
151-
'%s: <info>%s</info> Already installed',
185+
'%s: <info>%s:%s</info> %s Already installed',
152186
$link->getDescription(),
153-
$link,
187+
$link->getTarget(),
188+
$linkRequiresConstraint,
189+
Emoji::GREEN_CHECKMARK,
154190
));
155191

156192
return;
157193
}
158194

159195
$output->writeln(sprintf(
160-
'%s: <comment>%s</comment> ⚠️ Missing',
196+
'%s: <comment>%s:%s</comment> %s Missing',
161197
$link->getDescription(),
162-
$link,
198+
$link->getTarget(),
199+
$linkRequiresConstraint,
200+
Emoji::PROHIBITED,
163201
));
164202

165203
try {
@@ -205,9 +243,14 @@ static function (array $match): string {
205243
return;
206244
}
207245

246+
$requestInstallConstraint = '';
247+
if ($linkRequiresConstraint !== '*') {
248+
$requestInstallConstraint = ':' . $linkRequiresConstraint;
249+
}
250+
208251
try {
209252
$this->installSelectedPackage->withPieCli(
210-
substr($selectedPackageAnswer, 0, (int) strpos($selectedPackageAnswer, ':')),
253+
substr($selectedPackageAnswer, 0, (int) strpos($selectedPackageAnswer, ':')) . $requestInstallConstraint,
211254
$input,
212255
$output,
213256
);

src/Command/SelfUpdateCommand.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Php\Pie\SelfManage\Update\ReleaseMetadata;
1717
use Php\Pie\SelfManage\Verify\FailedToVerifyRelease;
1818
use Php\Pie\SelfManage\Verify\VerifyPieReleaseUsingAttestation;
19+
use Php\Pie\Util\Emoji;
1920
use Php\Pie\Util\PieVersion;
2021
use Psr\Container\ContainerInterface;
2122
use Symfony\Component\Console\Attribute\AsCommand;
@@ -42,6 +43,7 @@ public function __construct(
4243
private readonly string $githubApiBaseUrl,
4344
private readonly QuieterConsoleIO $io,
4445
private readonly ContainerInterface $container,
46+
private readonly FullPathToSelf $fullPathToSelf,
4547
) {
4648
parent::__construct();
4749
}
@@ -138,15 +140,19 @@ public function execute(InputInterface $input, OutputInterface $output): int
138140
return Command::FAILURE;
139141
}
140142

141-
$fullPathToSelf = (new FullPathToSelf())();
143+
$fullPathToSelf = ($this->fullPathToSelf)();
142144
$output->writeln(
143145
sprintf('Writing new version to %s', $fullPathToSelf),
144146
OutputInterface::VERBOSITY_VERBOSE,
145147
);
146148
SudoFilePut::contents($fullPathToSelf, file_get_contents($pharFilename->filePath));
147149
unlink($pharFilename->filePath);
148150

149-
$output->writeln('<info>✅ PIE has been upgraded to ' . $latestRelease->tag . '</info>');
151+
$output->writeln(sprintf(
152+
'<info>%s PIE has been upgraded to %s</info>',
153+
Emoji::GREEN_CHECKMARK,
154+
$latestRelease->tag,
155+
));
150156

151157
$this->exitSuccessfully();
152158
}

src/Command/SelfVerifyCommand.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Php\Pie\SelfManage\Update\ReleaseMetadata;
1515
use Php\Pie\SelfManage\Verify\FailedToVerifyRelease;
1616
use Php\Pie\SelfManage\Verify\VerifyPieReleaseUsingAttestation;
17+
use Php\Pie\Util\Emoji;
1718
use Php\Pie\Util\PieVersion;
1819
use Psr\Container\ContainerInterface;
1920
use Symfony\Component\Console\Attribute\AsCommand;
@@ -34,6 +35,7 @@ public function __construct(
3435
private readonly string $githubApiBaseUrl,
3536
private readonly QuieterConsoleIO $io,
3637
private readonly ContainerInterface $container,
38+
private readonly FullPathToSelf $fullPathToSelf,
3739
) {
3840
parent::__construct();
3941
}
@@ -64,7 +66,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
6466
$httpDownloader = new HttpDownloader($this->io, $composer->getConfig());
6567
$authHelper = new AuthHelper($this->io, $composer->getConfig());
6668
$latestRelease = new ReleaseMetadata(PieVersion::get(), 'blah');
67-
$pharFilename = BinaryFile::fromFileWithSha256Checksum((new FullPathToSelf())());
69+
$pharFilename = BinaryFile::fromFileWithSha256Checksum(($this->fullPathToSelf)());
6870
$verifyPiePhar = VerifyPieReleaseUsingAttestation::factory($this->githubApiBaseUrl, $httpDownloader, $authHelper);
6971

7072
try {
@@ -80,7 +82,8 @@ public function execute(InputInterface $input, OutputInterface $output): int
8082
}
8183

8284
$output->writeln(sprintf(
83-
'<info>✅ You are running an authentic PIE version %s.</info>',
85+
'<info>%s You are running an authentic PIE version %s.</info>',
86+
Emoji::GREEN_CHECKMARK,
8487
$latestRelease->tag,
8588
));
8689

src/Command/ShowCommand.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Php\Pie\Platform as PiePlatform;
1313
use Php\Pie\Platform\InstalledPiePackages;
1414
use Php\Pie\Platform\OperatingSystem;
15+
use Php\Pie\Util\Emoji;
1516
use Psr\Container\ContainerInterface;
1617
use Symfony\Component\Console\Attribute\AsCommand;
1718
use Symfony\Component\Console\Command\Command;
@@ -133,7 +134,11 @@ static function (string $version, string $phpExtensionName) use ($showAll, $outp
133134
$unmatchedPiePackages = array_diff(array_keys($piePackages), $piePackagesMatched);
134135

135136
if (count($unmatchedPiePackages)) {
136-
$output->writeln("\n" . ' ⚠️ <options=bold,underscore>PIE packages not loaded:</>');
137+
$output->writeln(sprintf(
138+
'%s %s <options=bold,underscore>PIE packages not loaded:</>',
139+
"\n",
140+
Emoji::WARNING,
141+
));
137142
$output->writeln('These extensions were installed with PIE but are not currently enabled.' . "\n");
138143

139144
foreach ($unmatchedPiePackages as $unmatchedPiePackage) {
@@ -179,9 +184,14 @@ private static function verifyChecksumInformation(
179184
try {
180185
$expectedBinaryFileFromMetadata->verifyAgainstOther($actualBinaryFile);
181186
} catch (BinaryFileFailedVerification) {
182-
return ' ⚠️ was ' . substr($actualBinaryFile->checksum, 0, 8) . '..., expected ' . substr($expectedBinaryFileFromMetadata->checksum, 0, 8) . '...';
187+
return sprintf(
188+
' %s was %s..., expected %s...',
189+
Emoji::WARNING,
190+
substr($actualBinaryFile->checksum, 0, 8),
191+
substr($expectedBinaryFileFromMetadata->checksum, 0, 8),
192+
);
183193
}
184194

185-
return ' ';
195+
return ' ' . Emoji::GREEN_CHECKMARK;
186196
}
187197
}

src/Container.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Php\Pie\DependencyResolver\ResolveDependencyWithComposer;
2828
use Php\Pie\Downloading\GithubPackageReleaseAssets;
2929
use Php\Pie\Downloading\PackageReleaseAssets;
30+
use Php\Pie\File\FullPathToSelf;
3031
use Php\Pie\Installing\Ini;
3132
use Php\Pie\Installing\Install;
3233
use Php\Pie\Installing\Uninstall;
@@ -43,6 +44,7 @@
4344
use Symfony\Component\Console\Output\OutputInterface;
4445
use Symfony\Component\EventDispatcher\EventDispatcher;
4546

47+
use function getcwd;
4648
use function str_starts_with;
4749

4850
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
@@ -115,6 +117,10 @@ static function (ConsoleCommandEvent $event) use (&$displayedBanner): void {
115117
->needs('$githubApiBaseUrl')
116118
->give('https://api.github.com');
117119

120+
$container->when(FullPathToSelf::class)
121+
->needs('$originalCwd')
122+
->give(getcwd());
123+
118124
$container->singleton(
119125
Build::class,
120126
static function (ContainerInterface $container): Build {

src/File/FullPathToSelf.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use RuntimeException;
88

99
use function array_key_exists;
10-
use function getcwd;
1110
use function is_string;
1211
use function preg_match;
1312
use function realpath;
@@ -17,6 +16,11 @@
1716
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
1817
class FullPathToSelf
1918
{
19+
/** @psalm-suppress PossiblyUnusedMethod no direct reference; used in service locator */
20+
public function __construct(private readonly string $originalCwd)
21+
{
22+
}
23+
2024
/** @return non-empty-string */
2125
public function __invoke(): string
2226
{
@@ -28,7 +32,7 @@ public function __invoke(): string
2832

2933
return $this->isAbsolutePath($phpSelf)
3034
? $phpSelf
31-
: (getcwd() . DIRECTORY_SEPARATOR . $phpSelf);
35+
: ($this->originalCwd . DIRECTORY_SEPARATOR . $phpSelf);
3236
}
3337

3438
private function isAbsolutePath(string $path): bool

src/Installing/InstallForPhpProject/InstallSelectedPackage.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@
2121
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
2222
class InstallSelectedPackage
2323
{
24+
public function __construct(private readonly FullPathToSelf $fullPathToSelf)
25+
{
26+
}
27+
2428
public function withPieCli(string $selectedPackage, InputInterface $input, OutputInterface $output): void
2529
{
2630
$process = [
27-
(new FullPathToSelf())(),
31+
($this->fullPathToSelf)(),
2832
'install',
2933
$selectedPackage,
3034
];

src/Installing/SetupIniFile.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Php\Pie\File\BinaryFile;
1010
use Php\Pie\Installing\Ini\SetupIniApproach;
1111
use Php\Pie\Platform\TargetPlatform;
12+
use Php\Pie\Util\Emoji;
1213
use Symfony\Component\Console\Output\OutputInterface;
1314

1415
use function sprintf;
@@ -34,15 +35,16 @@ public function __invoke(
3435
&& $this->setupIniApproach->setup($targetPlatform, $downloadedPackage, $binaryFile, $output)
3536
) {
3637
$output->writeln(sprintf(
37-
'<info>✅ Extension is enabled and loaded in</info> %s',
38+
'<info>%s Extension is enabled and loaded in</info> %s',
39+
Emoji::GREEN_CHECKMARK,
3840
$targetPlatform->phpBinaryPath->phpBinaryPath,
3941
));
4042
} else {
4143
if (! $attemptToSetupIniFile) {
4244
$output->writeln('Automatic extension enabling was skipped.', OutputInterface::VERBOSITY_VERBOSE);
4345
}
4446

45-
$output->writeln('<comment>⚠️ Extension has NOT been automatically enabled.</comment>');
47+
$output->writeln(sprintf('<comment>%s Extension has NOT been automatically enabled.</comment>', Emoji::WARNING));
4648
$output->writeln(sprintf(
4749
'<comment>You must now add "%s=%s" to your php.ini</comment>',
4850
$downloadedPackage->package->extensionType() === ExtensionType::PhpModule ? 'extension' : 'zend_extension',

src/SelfManage/Verify/FallbackVerificationUsingOpenSsl.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use OpenSSLAsymmetricKey;
1111
use Php\Pie\File\BinaryFile;
1212
use Php\Pie\SelfManage\Update\ReleaseMetadata;
13+
use Php\Pie\Util\Emoji;
1314
use Symfony\Component\Console\Output\OutputInterface;
1415
use Webmozart\Assert\Assert;
1516

@@ -89,7 +90,10 @@ public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilenam
8990
$output->writeln('#' . $attestationIndex . ': DSSE payload signature verified with certificate.', OutputInterface::VERBOSITY_VERBOSE);
9091
}
9192

92-
$output->writeln('<info>✅ Verified the new PIE version (using fallback verification)</info>');
93+
$output->writeln(sprintf(
94+
'<info>%s Verified the new PIE version (using fallback verification)</info>',
95+
Emoji::GREEN_CHECKMARK,
96+
));
9397
}
9498

9599
private function assertCertificateSignedByTrustedRoot(Attestation $attestation): void

src/SelfManage/Verify/GithubCliAttestationVerification.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66

77
use Php\Pie\File\BinaryFile;
88
use Php\Pie\SelfManage\Update\ReleaseMetadata;
9+
use Php\Pie\Util\Emoji;
910
use Php\Pie\Util\Process;
1011
use Symfony\Component\Console\Output\OutputInterface;
1112
use Symfony\Component\Process\Exception\ProcessFailedException;
1213
use Symfony\Component\Process\ExecutableFinder;
1314

1415
use function implode;
16+
use function sprintf;
1517

1618
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
1719
final class GithubCliAttestationVerification implements VerifyPiePhar
@@ -47,6 +49,6 @@ public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilenam
4749
throw FailedToVerifyRelease::fromGhCliFailure($releaseMetadata, $processFailedException);
4850
}
4951

50-
$output->writeln('<info> Verified the new PIE version</info>');
52+
$output->writeln(sprintf('<info>%s Verified the new PIE version</info>', Emoji::GREEN_CHECKMARK));
5153
}
5254
}

0 commit comments

Comments
 (0)