Skip to content

Commit

Permalink
Drupal 10 compatibility check must respect --locked command parameter
Browse files Browse the repository at this point in the history
Also introduce our own domain concept for an installed package repository.
  • Loading branch information
mxr576 committed Jan 14, 2024
1 parent fa8530d commit 51d1211
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 14 deletions.
15 changes: 15 additions & 0 deletions phparkitect-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@
"fqcn": "mxr576\\ddqgComposerAudit\\Infrastructure\\Composer\\UnsupportedPackageFinderConfigurationProvider",
"line": 78,
"error": "depends on mxr576\\ddqgComposerAudit\\Application\\PackageFinder\\Type\\PackageIgnoreRule, but should not depend on these namespaces: mxr576\\ddqgComposerAudit\\Application, mxr576\\ddqgComposerAudit\\Presentation, mxr576\\ddqgComposerAudit\\Supportive because The Infrastructure layer should only depend on the Domain layer and on external namespaces. It is better if it does not depend on anything from Application. (See exceptions in baseline.)"
},
{
"fqcn": "mxr576\\ddqgComposerAudit\\Presentation\\Composer\\Plugin",
"line": 86,
"error": "depends on mxr576\\ddqgComposerAudit\\Infrastructure\\Composer\\InstalledPackagesReadOnlyRepository, but should not depend on these namespaces: mxr576\\ddqgComposerAudit\\Domain, mxr576\\ddqgComposerAudit\\Infrastructure because The Presentation layer should only depend on the Application layer, stuff in Supportive and external namespaces."
},
{
"fqcn": "mxr576\\ddqgComposerAudit\\Presentation\\Composer\\Plugin",
"line": 89,
"error": "depends on mxr576\\ddqgComposerAudit\\Infrastructure\\Composer\\InstalledPackagesReadOnlyRepository, but should not depend on these namespaces: mxr576\\ddqgComposerAudit\\Domain, mxr576\\ddqgComposerAudit\\Infrastructure because The Presentation layer should only depend on the Application layer, stuff in Supportive and external namespaces."
},
{
"fqcn": "mxr576\\ddqgComposerAudit\\Presentation\\Composer\\Plugin",
"line": 89,
"error": "depends on mxr576\\ddqgComposerAudit\\Infrastructure\\Composer\\InstalledPackagesReadOnlyRepository, but should not depend on these namespaces: mxr576\\ddqgComposerAudit\\Domain, mxr576\\ddqgComposerAudit\\Infrastructure because The Presentation layer should only depend on the Application layer, stuff in Supportive and external namespaces."
}
],
"stopOnFailure": false
Expand Down
7 changes: 1 addition & 6 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,5 @@ parameters:

-
message: "#^Cannot cast Symfony\\\\Component\\\\Console\\\\Input\\\\InputInterface to string\\.$#"
count: 1
path: src/Presentation/Composer/Plugin.php

-
message: "#^Method mxr576\\\\ddqgComposerAudit\\\\Presentation\\\\Composer\\\\Plugin\\:\\:activate\\(\\) throws checked exception RuntimeException but it's missing from the PHPDoc @throws tag\\.$#"
count: 1
count: 2
path: src/Presentation/Composer/Plugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

/**
* Copyright (c) 2023-2024 Dezső Biczó
*
* For the full copyright and license information, please view
* the LICENSE.md file that was distributed with this source code.
*
* @see https://github.com/mxr576/ddqg-composer-audit/LICENSE.md
*
*/

namespace mxr576\ddqgComposerAudit\Domain\InstalledPackages;

use Composer\Package\PackageInterface;
use Composer\Semver\Constraint\ConstraintInterface;

interface InstalledPackagesReadOnlyRepository
{
public function findByName(string $package_name, string|ConstraintInterface $version): ?PackageInterface;

/**
* @return \Composer\Package\PackageInterface[]
*/
public function getPackages(): array;
}
118 changes: 118 additions & 0 deletions src/Infrastructure/Composer/InstalledPackagesReadOnlyRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

declare(strict_types=1);

/**
* Copyright (c) 2023-2024 Dezső Biczó
*
* For the full copyright and license information, please view
* the LICENSE.md file that was distributed with this source code.
*
* @see https://github.com/mxr576/ddqg-composer-audit/LICENSE.md
*
*/

namespace mxr576\ddqgComposerAudit\Infrastructure\Composer;

use Composer\Package\Locker;
use Composer\Package\PackageInterface;
use Composer\Package\RootPackageInterface;
use Composer\Repository\InstalledRepository;
use Composer\Repository\LockArrayRepository;
use Composer\Repository\RepositoryUtils;
use Composer\Semver\Constraint\ConstraintInterface;
use mxr576\ddqgComposerAudit\Domain\InstalledPackages\InstalledPackagesReadOnlyRepository as InstalledPackagesReadOnlyRepositoryConstract;

/**
* @internal
*/
final class InstalledPackagesReadOnlyRepository implements InstalledPackagesReadOnlyRepositoryConstract
{
/**
* @var callable(string, string|ConstraintInterface): ?PackageInterface
*/
private $packageFinder;

/**
* @var callable(): PackageInterface[]
*/
private $packageGetter;

/**
* Constructs a new object.
*
* @param callable(string $package_name, string|ConstraintInterface $version): ?PackageInterface $packageFinder
* @param callable(): PackageInterface[] $packageGetter
*/
private function __construct(
$packageFinder,
$packageGetter
) {
$this->packageGetter = $packageGetter;
$this->packageFinder = $packageFinder;
}

public static function fromLocker(Locker $locker, bool $withDevDependencies): self
{
if (!$locker->isLocked()) {
throw new \InvalidArgumentException('Valid composer.json and composer.lock files are required to run this command with --locked');
}

try {
return self::fromLockedPackages($locker->getLockedRepository($withDevDependencies));
} catch (\RuntimeException $e) {
throw new \InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
}

public static function fromLockedPackages(LockArrayRepository $lockArrayRepository): self
{
return new self(
static fn (string $package_name, string|ConstraintInterface $version): ?PackageInterface => $lockArrayRepository->findPackage($package_name, $version),
/** @return PackageInterface[] */
static fn (): array => $lockArrayRepository->getPackages(),
);
}

public static function fromInstalledPackages(InstalledRepository $installedRepository): self
{
return new self(
static fn (string $package_name, string|ConstraintInterface $version): ?PackageInterface => $installedRepository->findPackage($package_name, $version),
/** @return PackageInterface[] */
static fn (): array => $installedRepository->getPackages(),
);
}

public static function fromInstalledRequiredPackages(InstalledRepository $installedRepository, RootPackageInterface $rootPackage): self
{
return new self(
static function (string $package_name, string|ConstraintInterface $version) use (
$installedRepository, $rootPackage
): ?PackageInterface {
$package = $installedRepository->findPackage($package_name, $version);
if (null === $package) {
return null;
}

$tmp = RepositoryUtils::filterRequiredPackages([$package], $rootPackage);
if ([] === $tmp) {
return null;
}

return $package;
},
/** @return PackageInterface[] */
static fn (): array => $installedRepository->getPackages(),
);
}

public function findByName(string $package_name, string|ConstraintInterface $version): ?PackageInterface
{
return ($this->packageFinder)($package_name, $version);
}

public function getPackages(): array
{
return ($this->packageGetter)();
}
}
14 changes: 13 additions & 1 deletion src/Presentation/Composer/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
use Composer\IO\NullIO;
use Composer\Package\Version\VersionParser;
use Composer\Plugin\PluginInterface;
use Composer\Repository\InstalledRepository;
use mxr576\ddqgComposerAudit\Infrastructure\Composer\InstalledPackagesReadOnlyRepository;
use mxr576\ddqgComposerAudit\Presentation\Composer\Repository\ComposerAuditRepository;
use mxr576\ddqgComposerAudit\Supportive\Adapter\Composer\DeprecatedPackageWasIgnoredAdapter;
use mxr576\ddqgComposerAudit\Supportive\Adapter\Composer\UnsupportedPackageWasIgnoredAdapter;
Expand Down Expand Up @@ -65,6 +67,7 @@ public function activate(Composer $composer, IOInterface $io): void
}

$with_dev_dependencies = false;
$locked_dependencies = false;
// This is not great, not terrible... the only benefit of this is
// probably reducing the amount of objects build by the
// LockerRepository.
Expand All @@ -75,9 +78,18 @@ public function activate(Composer $composer, IOInterface $io): void
$ro_io->getProperty('input')->setAccessible(true);
assert($ro_io->getProperty('input')->getValue($io) instanceof InputInterface);
$with_dev_dependencies = str_contains((string) $ro_io->getProperty('input')->getValue($io), '--no-dev');
$locked_dependencies = str_contains((string) $ro_io->getProperty('input')->getValue($io), '--locked');
}
}

// @todo Fix layering rules.
if ($locked_dependencies) {
$installed_packages_repository = InstalledPackagesReadOnlyRepository::fromLocker($composer->getLocker(), $with_dev_dependencies);
} else {
$composer_installed_repository = new InstalledRepository([$composer->getRepositoryManager()->getLocalRepository()]);
$installed_packages_repository = $with_dev_dependencies ? InstalledPackagesReadOnlyRepository::fromInstalledPackages($composer_installed_repository) : InstalledPackagesReadOnlyRepository::fromInstalledRequiredPackages($composer_installed_repository, $composer->getPackage());
}

$version_parser = new VersionParser();
// Composer currently only displays advisories from one repository for
// a package. If multiple ones provides advisories only the first one
Expand Down Expand Up @@ -111,7 +123,7 @@ public function activate(Composer $composer, IOInterface $io): void
);
$composer->getRepositoryManager()->prependRepository(
new ComposerAuditRepository(
(new FindNonDrupal10CompatiblePackagesFactoryFromComposerRuntimeDependencies($composer->getPackage(), $composer->getLocker()->getLockedRepository($with_dev_dependencies), $version_parser))->create(),
(new FindNonDrupal10CompatiblePackagesFactoryFromComposerRuntimeDependencies($composer->getPackage(), $installed_packages_repository, $version_parser))->create(),
$io
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*/
final class Psr14EventDispatcherAdapterForComposer implements EventDispatcherInterface
{
public function __construct(private ComposerEventDispatcher $dispatcher)
public function __construct(private readonly ComposerEventDispatcher $dispatcher)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
namespace mxr576\ddqgComposerAudit\Supportive\Factory;

use Composer\Package\RootPackageInterface;
use Composer\Repository\LockArrayRepository;
use Composer\Semver\VersionParser;
use mxr576\ddqgComposerAudit\Application\PackageFinder\FindNonDrupal10CompatiblePackages;
use mxr576\ddqgComposerAudit\Infrastructure\Composer\InstalledPackagesReadOnlyRepository;
use mxr576\ddqgComposerAudit\Infrastructure\Composer\NonDrupal10CompatiblePackageFinderConfigurationProvider;
use mxr576\ddqgComposerAudit\Supportive\Infrastructure\Composer\NonDrupal10CompatiblePackageVersionsProviderFromComposerLock;

Expand All @@ -28,15 +28,15 @@ final class FindNonDrupal10CompatiblePackagesFactoryFromComposerRuntimeDependenc
{
public function __construct(
private readonly RootPackageInterface $rootPackage,
private readonly LockArrayRepository $lockRepository,
private readonly InstalledPackagesReadOnlyRepository $installedPackageFinder,
private readonly VersionParser $versionParser,
) {
}

public function create(): FindNonDrupal10CompatiblePackages
{
return new FindNonDrupal10CompatiblePackages(
new NonDrupal10CompatiblePackageVersionsProviderFromComposerLock($this->lockRepository, $this->versionParser),
new NonDrupal10CompatiblePackageVersionsProviderFromComposerLock($this->installedPackageFinder, $this->versionParser),
$this->versionParser,
new NonDrupal10CompatiblePackageFinderConfigurationProvider($this->rootPackage)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

namespace mxr576\ddqgComposerAudit\Supportive\Infrastructure\Composer;

use Composer\Repository\LockArrayRepository;
use Composer\Semver\Constraint\Constraint;
use Composer\Semver\VersionParser;
use mxr576\ddqgComposerAudit\Domain\PackageVersionsProvider\NonDrupal10CompatiblePackageVersionsProvider;
use mxr576\ddqgComposerAudit\Infrastructure\Composer\InstalledPackagesReadOnlyRepository;

/**
* @internal
Expand All @@ -42,15 +42,16 @@ final class NonDrupal10CompatiblePackageVersionsProviderFromComposerLock impleme
'drupal/core-dev',
];

public function __construct(private readonly LockArrayRepository $lockRepository, private readonly VersionParser $versionParser)
public function __construct(private readonly InstalledPackagesReadOnlyRepository $installedPackagesRepository, private readonly VersionParser $versionParser)
{
}

public function findByPackages(string ...$package_names): array
{
$result = [];
$d10_compatible_constraint = new Constraint('>=', $this->versionParser->normalize('10.0.0'));
foreach ($this->lockRepository->getPackages() as $package) {

foreach ($this->installedPackagesRepository->getPackages() as $package) {
if (!in_array($package->getName(), $package_names, true)) {
continue;
}
Expand Down

0 comments on commit 51d1211

Please sign in to comment.