From 8e2a0756143aea11f45b95faed195be4975b0fc9 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 11 Sep 2026 22:53:18 +0200 Subject: [PATCH] Resolve dependencies through the repositories the packages filter dropped A partial build (`satis build satis.json web/ vendor/package`) narrows the repository list down to the ones providing the requested packages. The dependency pass then reused that narrowed list, so with `require-dependencies` nothing the filtered package required could be found -- packagist.org and every other repository had already been dropped. Feed the dependency repository set the repositories the filter removed, the same way the repository-URL filter already does. Fixes #706 --- src/PackageSelection/PackageSelection.php | 14 +++-- .../PackageSelection/PackageSelectionTest.php | 53 ++++++++++++++++--- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/PackageSelection/PackageSelection.php b/src/PackageSelection/PackageSelection.php index 6cf53dc..4242ef9 100644 --- a/src/PackageSelection/PackageSelection.php +++ b/src/PackageSelection/PackageSelection.php @@ -221,12 +221,17 @@ class PackageSelection } } - if ($this->hasFilterForPackages()) { - $repos = $this->filterPackages($repos); + $reposDroppedByPackagesFilter = []; - if (0 === count($repos)) { + if ($this->hasFilterForPackages()) { + $filteredRepos = $this->filterPackages($repos); + + if (0 === count($filteredRepos)) { throw new \InvalidArgumentException(sprintf('Could not find any repositories config with "name" matching your package(s) filter: %s', implode(', ', $this->packagesFilter))); } + + $reposDroppedByPackagesFilter = array_filter($repos, static fn ($repo) => !in_array($repo, $filteredRepos, true)); + $repos = $filteredRepos; } $repositorySet = new RepositorySet($this->minimumStability, $stabilityFlags); @@ -253,6 +258,9 @@ class PackageSelection ); } + // the packages filter picks what to request, not who may answer a dependency + $this->addRepositories($repositorySet, $reposDroppedByPackagesFilter); + // additional repositories for dependencies if (!$this->hasRepositoriesFilter() || true !== $this->repositoryFilterDep) { $this->addRepositories($repositorySet, $this->getDepRepos($composer)); diff --git a/tests/PackageSelection/PackageSelectionTest.php b/tests/PackageSelection/PackageSelectionTest.php index 7676f75..d648966 100644 --- a/tests/PackageSelection/PackageSelectionTest.php +++ b/tests/PackageSelection/PackageSelectionTest.php @@ -1401,6 +1401,32 @@ class PackageSelectionTest extends TestCase ); } + /** + * `require-dependencies` resolves through the repositories the filter dropped. + * + * @see https://github.com/composer/satis/issues/706 + */ + public function testSelectWithPackagesFilterStillResolvesDependencies(): void + { + $filteredPackage = 'vendor/filtered-package'; + $dependency = 'vendor/dependency-package'; + + $this->runPackagesFilterSelection( + fn (string $workDir): array => [ + $this->createLocalRepositoryConfig( + self::REPOSITORY_TYPE_PACKAGE, + $filteredPackage, + $workDir, + [$dependency => self::FIXTURE_VERSION] + ), + $this->createLocalRepositoryConfig(self::REPOSITORY_TYPE_PACKAGE, $dependency, $workDir), + ], + [$filteredPackage], + [$dependency, $filteredPackage], + ['require-dependencies' => true] + ); + } + /** * Runs `select()` with a packages filter over a throwaway working directory * and asserts which packages came out. @@ -1408,18 +1434,20 @@ class PackageSelectionTest extends TestCase * @param callable(string): list> $buildRepositories * @param list $packagesFilter * @param list $expectedPackageNames + * @param array $extraConfig */ private function runPackagesFilterSelection( callable $buildRepositories, array $packagesFilter, array $expectedPackageNames, + array $extraConfig = [], ): void { $filesystem = new Filesystem(); $workDir = sys_get_temp_dir() . '/satis-package-filter-' . uniqid('', true); $filesystem->ensureDirectoryExists($workDir); try { - $config = [ + $config = $extraConfig + [ 'name' => 'test/satis', 'homepage' => 'http://localhost', 'repositories' => $buildRepositories($workDir), @@ -1448,19 +1476,28 @@ class PackageSelectionTest extends TestCase /** * Builds the repository fixture on disk and returns its Satis config entry. * + * @param array $requires + * * @return array */ - private function createLocalRepositoryConfig(string $repositoryType, string $packageName, string $workDir): array - { - $composerJson = json_encode( - ['name' => $packageName, 'version' => self::FIXTURE_VERSION], - \JSON_THROW_ON_ERROR - ); + private function createLocalRepositoryConfig( + string $repositoryType, + string $packageName, + string $workDir, + array $requires = [], + ): array { + $manifest = ['name' => $packageName, 'version' => self::FIXTURE_VERSION]; + + if ([] !== $requires) { + $manifest['require'] = $requires; + } + + $composerJson = json_encode($manifest, \JSON_THROW_ON_ERROR); if (self::REPOSITORY_TYPE_PACKAGE === $repositoryType) { return [ 'type' => self::REPOSITORY_TYPE_PACKAGE, - 'package' => ['name' => $packageName, 'version' => self::FIXTURE_VERSION], + 'package' => $manifest, ]; }