Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 50 additions & 7 deletions src/Command/InstallExtensionsForProjectCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Php\Pie\Command;

use Composer\Package\Link;
use Composer\Package\Version\VersionParser;
use OutOfRangeException;
use Php\Pie\ComposerIntegration\PieComposerFactory;
use Php\Pie\ComposerIntegration\PieComposerRequest;
Expand All @@ -16,6 +17,8 @@
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
use Php\Pie\Installing\InstallForPhpProject\InstallPiePackageFromPath;
use Php\Pie\Installing\InstallForPhpProject\InstallSelectedPackage;
use Php\Pie\Platform\InstalledPiePackages;
use Php\Pie\Util\Emoji;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
Expand Down Expand Up @@ -53,6 +56,7 @@ final class InstallExtensionsForProjectCommand extends Command
public function __construct(
private readonly ComposerFactoryForProject $composerFactoryForProject,
private readonly DetermineExtensionsRequired $determineExtensionsRequired,
private readonly InstalledPiePackages $installedPiePackages,
private readonly FindMatchingPackages $findMatchingPackages,
private readonly InstallSelectedPackage $installSelectedPackage,
private readonly InstallPiePackageFromPath $installPiePackageFromPath,
Expand Down Expand Up @@ -138,28 +142,62 @@ public function execute(InputInterface $input, OutputInterface $output): int
);

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

$anyErrorsHappened = false;

array_walk(
$extensionsRequired,
function (Link $link) use ($pieComposer, $phpEnabledExtensions, $input, $output, $helper, &$anyErrorsHappened): void {
$extension = ExtensionName::normaliseFromString($link->getTarget());
function (Link $link) use ($pieComposer, $phpEnabledExtensions, $installedPiePackages, $input, $output, $helper, &$anyErrorsHappened): void {
$extension = ExtensionName::normaliseFromString($link->getTarget());
$linkRequiresConstraint = $link->getPrettyConstraint();

$piePackageVersion = null;
if (in_array($extension->name(), array_keys($installedPiePackages))) {
$piePackageVersion = $installedPiePackages[$extension->name()]->version();
}

$piePackageVersionMatchesLinkConstraint = null;
if ($piePackageVersion !== null) {
$piePackageVersionMatchesLinkConstraint = $link
->getConstraint()
->matches(
(new VersionParser())->parseConstraints($piePackageVersion),
);
}

if (in_array($extension->name(), $phpEnabledExtensions)) {
if ($piePackageVersion !== null && $piePackageVersionMatchesLinkConstraint === false) {
$output->writeln(sprintf(
'%s: <comment>%s:%s</comment> %s Version %s is installed, but does not meet the version requirement %s',
$link->getDescription(),
$link->getTarget(),
$linkRequiresConstraint,
Emoji::WARNING,
$piePackageVersion,
$link->getConstraint()->getPrettyString(),
));

return;
}

$output->writeln(sprintf(
'%s: <info>%s</info> Already installed',
'%s: <info>%s:%s</info> %s Already installed',
$link->getDescription(),
$link,
$link->getTarget(),
$linkRequiresConstraint,
Emoji::GREEN_CHECKMARK,
));

return;
}

$output->writeln(sprintf(
'%s: <comment>%s</comment> ⚠️ Missing',
'%s: <comment>%s:%s</comment> %s Missing',
$link->getDescription(),
$link,
$link->getTarget(),
$linkRequiresConstraint,
Emoji::PROHIBITED,
));

try {
Expand Down Expand Up @@ -205,9 +243,14 @@ static function (array $match): string {
return;
}

$requestInstallConstraint = '';
if ($linkRequiresConstraint !== '*') {
$requestInstallConstraint = ':' . $linkRequiresConstraint;
}

try {
$this->installSelectedPackage->withPieCli(
substr($selectedPackageAnswer, 0, (int) strpos($selectedPackageAnswer, ':')),
substr($selectedPackageAnswer, 0, (int) strpos($selectedPackageAnswer, ':')) . $requestInstallConstraint,
$input,
$output,
);
Expand Down
10 changes: 8 additions & 2 deletions src/Command/SelfUpdateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Php\Pie\SelfManage\Update\ReleaseMetadata;
use Php\Pie\SelfManage\Verify\FailedToVerifyRelease;
use Php\Pie\SelfManage\Verify\VerifyPieReleaseUsingAttestation;
use Php\Pie\Util\Emoji;
use Php\Pie\Util\PieVersion;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
Expand All @@ -42,6 +43,7 @@ public function __construct(
private readonly string $githubApiBaseUrl,
private readonly QuieterConsoleIO $io,
private readonly ContainerInterface $container,
private readonly FullPathToSelf $fullPathToSelf,
) {
parent::__construct();
}
Expand Down Expand Up @@ -138,15 +140,19 @@ public function execute(InputInterface $input, OutputInterface $output): int
return Command::FAILURE;
}

$fullPathToSelf = (new FullPathToSelf())();
$fullPathToSelf = ($this->fullPathToSelf)();
$output->writeln(
sprintf('Writing new version to %s', $fullPathToSelf),
OutputInterface::VERBOSITY_VERBOSE,
);
SudoFilePut::contents($fullPathToSelf, file_get_contents($pharFilename->filePath));
unlink($pharFilename->filePath);

$output->writeln('<info>✅ PIE has been upgraded to ' . $latestRelease->tag . '</info>');
$output->writeln(sprintf(
'<info>%s PIE has been upgraded to %s</info>',
Emoji::GREEN_CHECKMARK,
$latestRelease->tag,
));

$this->exitSuccessfully();
}
Expand Down
7 changes: 5 additions & 2 deletions src/Command/SelfVerifyCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Php\Pie\SelfManage\Update\ReleaseMetadata;
use Php\Pie\SelfManage\Verify\FailedToVerifyRelease;
use Php\Pie\SelfManage\Verify\VerifyPieReleaseUsingAttestation;
use Php\Pie\Util\Emoji;
use Php\Pie\Util\PieVersion;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
Expand All @@ -34,6 +35,7 @@ public function __construct(
private readonly string $githubApiBaseUrl,
private readonly QuieterConsoleIO $io,
private readonly ContainerInterface $container,
private readonly FullPathToSelf $fullPathToSelf,
) {
parent::__construct();
}
Expand Down Expand Up @@ -64,7 +66,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
$httpDownloader = new HttpDownloader($this->io, $composer->getConfig());
$authHelper = new AuthHelper($this->io, $composer->getConfig());
$latestRelease = new ReleaseMetadata(PieVersion::get(), 'blah');
$pharFilename = BinaryFile::fromFileWithSha256Checksum((new FullPathToSelf())());
$pharFilename = BinaryFile::fromFileWithSha256Checksum(($this->fullPathToSelf)());
$verifyPiePhar = VerifyPieReleaseUsingAttestation::factory($this->githubApiBaseUrl, $httpDownloader, $authHelper);

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

$output->writeln(sprintf(
'<info>✅ You are running an authentic PIE version %s.</info>',
'<info>%s You are running an authentic PIE version %s.</info>',
Emoji::GREEN_CHECKMARK,
$latestRelease->tag,
));

Expand Down
16 changes: 13 additions & 3 deletions src/Command/ShowCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Php\Pie\Platform as PiePlatform;
use Php\Pie\Platform\InstalledPiePackages;
use Php\Pie\Platform\OperatingSystem;
use Php\Pie\Util\Emoji;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
Expand Down Expand Up @@ -133,7 +134,11 @@ static function (string $version, string $phpExtensionName) use ($showAll, $outp
$unmatchedPiePackages = array_diff(array_keys($piePackages), $piePackagesMatched);

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

foreach ($unmatchedPiePackages as $unmatchedPiePackage) {
Expand Down Expand Up @@ -179,9 +184,14 @@ private static function verifyChecksumInformation(
try {
$expectedBinaryFileFromMetadata->verifyAgainstOther($actualBinaryFile);
} catch (BinaryFileFailedVerification) {
return ' ⚠️ was ' . substr($actualBinaryFile->checksum, 0, 8) . '..., expected ' . substr($expectedBinaryFileFromMetadata->checksum, 0, 8) . '...';
return sprintf(
' %s was %s..., expected %s...',
Emoji::WARNING,
substr($actualBinaryFile->checksum, 0, 8),
substr($expectedBinaryFileFromMetadata->checksum, 0, 8),
);
}

return ' ✅';
return ' ' . Emoji::GREEN_CHECKMARK;
}
}
6 changes: 6 additions & 0 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use Php\Pie\DependencyResolver\ResolveDependencyWithComposer;
use Php\Pie\Downloading\GithubPackageReleaseAssets;
use Php\Pie\Downloading\PackageReleaseAssets;
use Php\Pie\File\FullPathToSelf;
use Php\Pie\Installing\Ini;
use Php\Pie\Installing\Install;
use Php\Pie\Installing\Uninstall;
Expand All @@ -43,6 +44,7 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;

use function getcwd;
use function str_starts_with;

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

$container->when(FullPathToSelf::class)
->needs('$originalCwd')
->give(getcwd());

$container->singleton(
Build::class,
static function (ContainerInterface $container): Build {
Expand Down
8 changes: 6 additions & 2 deletions src/File/FullPathToSelf.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use RuntimeException;

use function array_key_exists;
use function getcwd;
use function is_string;
use function preg_match;
use function realpath;
Expand All @@ -17,6 +16,11 @@
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
class FullPathToSelf
{
/** @psalm-suppress PossiblyUnusedMethod no direct reference; used in service locator */
public function __construct(private readonly string $originalCwd)
{
}

/** @return non-empty-string */
public function __invoke(): string
{
Expand All @@ -28,7 +32,7 @@ public function __invoke(): string

return $this->isAbsolutePath($phpSelf)
? $phpSelf
: (getcwd() . DIRECTORY_SEPARATOR . $phpSelf);
: ($this->originalCwd . DIRECTORY_SEPARATOR . $phpSelf);
}

private function isAbsolutePath(string $path): bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
class InstallSelectedPackage
{
public function __construct(private readonly FullPathToSelf $fullPathToSelf)
{
}

public function withPieCli(string $selectedPackage, InputInterface $input, OutputInterface $output): void
{
$process = [
(new FullPathToSelf())(),
($this->fullPathToSelf)(),
'install',
$selectedPackage,
];
Expand Down
6 changes: 4 additions & 2 deletions src/Installing/SetupIniFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Php\Pie\File\BinaryFile;
use Php\Pie\Installing\Ini\SetupIniApproach;
use Php\Pie\Platform\TargetPlatform;
use Php\Pie\Util\Emoji;
use Symfony\Component\Console\Output\OutputInterface;

use function sprintf;
Expand All @@ -34,15 +35,16 @@ public function __invoke(
&& $this->setupIniApproach->setup($targetPlatform, $downloadedPackage, $binaryFile, $output)
) {
$output->writeln(sprintf(
'<info>✅ Extension is enabled and loaded in</info> %s',
'<info>%s Extension is enabled and loaded in</info> %s',
Emoji::GREEN_CHECKMARK,
$targetPlatform->phpBinaryPath->phpBinaryPath,
));
} else {
if (! $attemptToSetupIniFile) {
$output->writeln('Automatic extension enabling was skipped.', OutputInterface::VERBOSITY_VERBOSE);
}

$output->writeln('<comment>⚠️ Extension has NOT been automatically enabled.</comment>');
$output->writeln(sprintf('<comment>%s Extension has NOT been automatically enabled.</comment>', Emoji::WARNING));
$output->writeln(sprintf(
'<comment>You must now add "%s=%s" to your php.ini</comment>',
$downloadedPackage->package->extensionType() === ExtensionType::PhpModule ? 'extension' : 'zend_extension',
Expand Down
6 changes: 5 additions & 1 deletion src/SelfManage/Verify/FallbackVerificationUsingOpenSsl.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use OpenSSLAsymmetricKey;
use Php\Pie\File\BinaryFile;
use Php\Pie\SelfManage\Update\ReleaseMetadata;
use Php\Pie\Util\Emoji;
use Symfony\Component\Console\Output\OutputInterface;
use Webmozart\Assert\Assert;

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

$output->writeln('<info>✅ Verified the new PIE version (using fallback verification)</info>');
$output->writeln(sprintf(
'<info>%s Verified the new PIE version (using fallback verification)</info>',
Emoji::GREEN_CHECKMARK,
));
}

private function assertCertificateSignedByTrustedRoot(Attestation $attestation): void
Expand Down
4 changes: 3 additions & 1 deletion src/SelfManage/Verify/GithubCliAttestationVerification.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@

use Php\Pie\File\BinaryFile;
use Php\Pie\SelfManage\Update\ReleaseMetadata;
use Php\Pie\Util\Emoji;
use Php\Pie\Util\Process;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\ExecutableFinder;

use function implode;
use function sprintf;

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

$output->writeln('<info> Verified the new PIE version</info>');
$output->writeln(sprintf('<info>%s Verified the new PIE version</info>', Emoji::GREEN_CHECKMARK));
}
}
13 changes: 13 additions & 0 deletions src/Util/Emoji.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Php\Pie\Util;

/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
final class Emoji
{
public const GREEN_CHECKMARK = '✅';
public const WARNING = '⚠️ ';
public const PROHIBITED = '🚫';
}
Loading