-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #173 from asgrim/cleanup-vendor-after-install
Cleanup vendor after install
- Loading branch information
Showing
11 changed files
with
427 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
src/ComposerIntegration/RemoveUnrelatedInstallOperations.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Php\Pie\ComposerIntegration; | ||
|
||
use Closure; | ||
use Composer\Composer; | ||
use Composer\DependencyResolver\Operation\InstallOperation; | ||
use Composer\DependencyResolver\Operation\OperationInterface; | ||
use Composer\DependencyResolver\Transaction; | ||
use Composer\Installer\InstallerEvent; | ||
use Composer\Installer\InstallerEvents; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
|
||
use function array_filter; | ||
use function assert; | ||
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 */ | ||
class RemoveUnrelatedInstallOperations | ||
{ | ||
public function __construct( | ||
private readonly PieComposerRequest $composerRequest, | ||
) { | ||
} | ||
|
||
public static function selfRegister( | ||
Composer $composer, | ||
PieComposerRequest $composerRequest, | ||
): void { | ||
$composer | ||
->getEventDispatcher() | ||
->addListener( | ||
InstallerEvents::PRE_OPERATIONS_EXEC, | ||
new self($composerRequest), | ||
); | ||
} | ||
|
||
/** | ||
* @psalm-suppress InternalProperty | ||
* @psalm-suppress InternalMethod | ||
*/ | ||
public function __invoke(InstallerEvent $installerEvent): void | ||
{ | ||
$pieOutput = $this->composerRequest->pieOutput; | ||
|
||
$newOperations = array_filter( | ||
$installerEvent->getTransaction()?->getOperations() ?? [], | ||
function (OperationInterface $operation) use ($pieOutput): bool { | ||
if (! $operation instanceof InstallOperation) { | ||
$pieOutput->writeln( | ||
sprintf( | ||
'Unexpected operation during installer: %s', | ||
$operation::class, | ||
), | ||
OutputInterface::VERBOSITY_VERY_VERBOSE, | ||
); | ||
|
||
return false; | ||
} | ||
|
||
$isRequestedPiePackage = $this->composerRequest->requestedPackage->package === $operation->getPackage()->getName(); | ||
|
||
if (! $isRequestedPiePackage) { | ||
$pieOutput->writeln( | ||
sprintf( | ||
'Filtering package %s from install operations, as it was not the requested package', | ||
$operation->getPackage()->getName(), | ||
), | ||
OutputInterface::VERBOSITY_VERY_VERBOSE, | ||
); | ||
} | ||
|
||
return $isRequestedPiePackage; | ||
}, | ||
); | ||
|
||
$overrideOperations = Closure::Bind( | ||
static function (Transaction $transaction) use ($newOperations): void { | ||
/** @psalm-suppress InaccessibleProperty */ | ||
$transaction->operations = $newOperations; | ||
}, | ||
null, | ||
Transaction::class, | ||
); | ||
assert($overrideOperations !== null); | ||
$overrideOperations($installerEvent->getTransaction()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Php\Pie\ComposerIntegration; | ||
|
||
use Composer\Composer; | ||
use Composer\Util\Filesystem; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
|
||
use function array_filter; | ||
use function array_walk; | ||
use function in_array; | ||
use function is_array; | ||
use function scandir; | ||
use function sprintf; | ||
|
||
use const DIRECTORY_SEPARATOR; | ||
|
||
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ | ||
class VendorCleanup | ||
{ | ||
public function __construct( | ||
private readonly OutputInterface $output, | ||
private readonly Filesystem $filesystem, | ||
) { | ||
} | ||
|
||
public function __invoke(Composer $composer): void | ||
{ | ||
$vendorDir = (string) $composer->getConfig()->get('vendor-dir'); | ||
$vendorContents = scandir($vendorDir); | ||
|
||
if (! is_array($vendorContents)) { | ||
$this->output->writeln( | ||
sprintf( | ||
'<comment>Vendor directory (vendor-dir config) %s seemed invalid?/comment>', | ||
$vendorDir, | ||
), | ||
OutputInterface::VERBOSITY_VERY_VERBOSE, | ||
); | ||
|
||
return; | ||
} | ||
|
||
$toRemove = array_filter( | ||
$vendorContents, | ||
static function (string $path): bool { | ||
return ! in_array( | ||
$path, | ||
[ | ||
'.', | ||
'..', | ||
'autoload.php', | ||
'composer', | ||
], | ||
); | ||
}, | ||
); | ||
|
||
array_walk( | ||
$toRemove, | ||
function (string $pathToRemove) use ($vendorDir): void { | ||
$fullPathToRemove = $vendorDir . DIRECTORY_SEPARATOR . $pathToRemove; | ||
|
||
$this->output->writeln( | ||
sprintf( | ||
'<comment>Removing: %s</comment>', | ||
$fullPathToRemove, | ||
), | ||
OutputInterface::VERBOSITY_VERY_VERBOSE, | ||
); | ||
|
||
if ($this->filesystem->remove($fullPathToRemove)) { | ||
return; | ||
} | ||
|
||
$this->output->writeln( | ||
sprintf( | ||
'<comment>Warning: failed to remove %s</comment>', | ||
$fullPathToRemove, | ||
), | ||
OutputInterface::VERBOSITY_VERBOSE, | ||
); | ||
}, | ||
); | ||
} | ||
} |
Empty file.
Empty file.
Empty file.
Empty file.
136 changes: 136 additions & 0 deletions
136
test/unit/ComposerIntegration/RemoveUnrelatedInstallOperationsTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Php\PieUnitTest\ComposerIntegration; | ||
|
||
use Composer\Composer; | ||
use Composer\DependencyResolver\Operation\InstallOperation; | ||
use Composer\DependencyResolver\Operation\OperationInterface; | ||
use Composer\DependencyResolver\Transaction; | ||
use Composer\EventDispatcher\EventDispatcher; | ||
use Composer\Installer\InstallerEvent; | ||
use Composer\Installer\InstallerEvents; | ||
use Composer\IO\IOInterface; | ||
use Composer\Package\CompletePackage; | ||
use Php\Pie\ComposerIntegration\PieComposerRequest; | ||
use Php\Pie\ComposerIntegration\PieOperation; | ||
use Php\Pie\ComposerIntegration\RemoveUnrelatedInstallOperations; | ||
use Php\Pie\DependencyResolver\RequestedPackageAndVersion; | ||
use Php\Pie\Platform\Architecture; | ||
use Php\Pie\Platform\OperatingSystem; | ||
use Php\Pie\Platform\OperatingSystemFamily; | ||
use Php\Pie\Platform\TargetPhp\PhpBinaryPath; | ||
use Php\Pie\Platform\TargetPlatform; | ||
use Php\Pie\Platform\ThreadSafetyMode; | ||
use Php\Pie\Platform\WindowsCompiler; | ||
use PHPUnit\Framework\Attributes\CoversClass; | ||
use PHPUnit\Framework\MockObject\MockObject; | ||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
|
||
use function array_filter; | ||
use function array_map; | ||
|
||
#[CoversClass(RemoveUnrelatedInstallOperations::class)] | ||
final class RemoveUnrelatedInstallOperationsTest extends TestCase | ||
{ | ||
private Composer&MockObject $composer; | ||
|
||
public function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
$this->composer = $this->createMock(Composer::class); | ||
} | ||
|
||
public function testEventListenerRegistration(): void | ||
{ | ||
$eventDispatcher = $this->createMock(EventDispatcher::class); | ||
$eventDispatcher | ||
->expects(self::once()) | ||
->method('addListener') | ||
->with( | ||
InstallerEvents::PRE_OPERATIONS_EXEC, | ||
self::isInstanceOf(RemoveUnrelatedInstallOperations::class), | ||
); | ||
|
||
$this->composer | ||
->expects(self::once()) | ||
->method('getEventDispatcher') | ||
->willReturn($eventDispatcher); | ||
|
||
RemoveUnrelatedInstallOperations::selfRegister( | ||
$this->composer, | ||
new PieComposerRequest( | ||
$this->createMock(OutputInterface::class), | ||
new TargetPlatform( | ||
OperatingSystem::NonWindows, | ||
OperatingSystemFamily::Linux, | ||
PhpBinaryPath::fromCurrentProcess(), | ||
Architecture::x86_64, | ||
ThreadSafetyMode::NonThreadSafe, | ||
1, | ||
WindowsCompiler::VC15, | ||
), | ||
new RequestedPackageAndVersion('foo/bar', '^1.1'), | ||
PieOperation::Install, | ||
[], | ||
null, | ||
false, | ||
), | ||
); | ||
} | ||
|
||
/** @psalm-suppress InternalMethod */ | ||
public function testUnrelatedInstallOperationsAreRemoved(): void | ||
{ | ||
$composerPackage1 = new CompletePackage('foo/bar', '1.2.3.0', '1.2.3'); | ||
$composerPackage2 = new CompletePackage('bat/baz', '3.4.5.0', '3.4.5'); | ||
$composerPackage3 = new CompletePackage('qux/quux', '5.6.7.0', '5.6.7'); | ||
|
||
/** | ||
* @psalm-suppress InternalClass | ||
* @psalm-suppress InternalMethod | ||
*/ | ||
$installerEvent = new InstallerEvent( | ||
InstallerEvents::PRE_OPERATIONS_EXEC, | ||
$this->composer, | ||
$this->createMock(IOInterface::class), | ||
false, | ||
true, | ||
new Transaction([], [$composerPackage1, $composerPackage2, $composerPackage3]), | ||
); | ||
|
||
(new RemoveUnrelatedInstallOperations( | ||
new PieComposerRequest( | ||
$this->createMock(OutputInterface::class), | ||
new TargetPlatform( | ||
OperatingSystem::Windows, | ||
OperatingSystemFamily::Linux, | ||
PhpBinaryPath::fromCurrentProcess(), | ||
Architecture::x86_64, | ||
ThreadSafetyMode::NonThreadSafe, | ||
1, | ||
WindowsCompiler::VC15, | ||
), | ||
new RequestedPackageAndVersion('bat/baz', '^3.2'), | ||
PieOperation::Install, | ||
[], | ||
null, | ||
false, | ||
), | ||
))($installerEvent); | ||
|
||
self::assertSame( | ||
['bat/baz'], | ||
array_map( | ||
static fn (InstallOperation $operation): string => $operation->getPackage()->getName(), | ||
array_filter( | ||
$installerEvent->getTransaction()?->getOperations() ?? [], | ||
static fn (OperationInterface $operation): bool => $operation instanceof InstallOperation, | ||
), | ||
), | ||
); | ||
} | ||
} |
Oops, something went wrong.