Skip to content

Commit

Permalink
feat: detect circular dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka committed Sep 5, 2024
1 parent e057be4 commit 2606a03
Showing 1 changed file with 27 additions and 18 deletions.
45 changes: 27 additions & 18 deletions src/Command/LinkCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,12 @@ protected function configure(): void
->setDefinition([
new InputArgument('path', InputArgument::OPTIONAL, 'Path to link.'),
new InputOption('working-directory', 'wd', InputOption::VALUE_REQUIRED, "Defaults to SERVER['PWD']"),
new InputOption('permanent', 'p', InputOption::VALUE_NONE, "Permanent composer change, does not revert backups."),
]);
}

private function getComposerFileAtPath(mixed $path): string
{
return is_string($path) ? join(DIRECTORY_SEPARATOR, [$path, 'composer.json']) : join(DIRECTORY_SEPARATOR, [getcwd(), Factory::getComposerFile()]);
return is_string($path) ? join('/', [$path, 'composer.json']) : join('/', [getcwd(), Factory::getComposerFile()]);
}

protected function execute(InputInterface $input, OutputInterface $output): int
Expand All @@ -63,7 +62,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
throw new RuntimeException(sprintf('Can not find working directory, specify its value using "composer link %s --working-directory=$(pwd)"', $path));
}

$path = join(DIRECTORY_SEPARATOR, [$wd, $path]);
$path = join('/', [$wd, $path]);
}

$monoRepositoryComposerFile = $this->getComposerFileAtPath($path);
Expand All @@ -72,14 +71,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$config = Config::createFromJson($monoRepositoryComposer, $baseDir);
$composerFile = $this->getComposerFileAtPath($wd);
$repositories = $this->buildRepositories($config->composerFiles);
$composer = self::$fileContents[$composerFile] = $this->readJsonFile($composerFile);
$composer = static::$fileContents[$composerFile] = $this->readJsonFile($composerFile);

$filesToWrite = [];
$revert = [];

$dependencies = [
'require' => $this->mapRequireDependencies($composer, $config->composerFiles),
'require-dev' => $this->mapRequireDevDependencies($composer, $config->composerFiles)
'require' => $this->mapRequireDependencies($composer, $config->composerFiles, $output),
'require-dev' => $this->mapRequireDevDependencies($composer, $config->composerFiles, $output)
];

foreach ($dependencies['require'] as $dependency) {
Expand Down Expand Up @@ -128,10 +127,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
} catch (\Exception $e) {
}

if ($input->getOption('permanent')) {
return 0;
}

foreach ($revert as $file => $backup) {
rename($backup, $file);
}
Expand All @@ -145,8 +140,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
*
* @return array<string>
*/
private function mapRequireDependencies(array $composer, array $composerFiles): array {
return array_unique(iterator_to_array($this->mapDependencies($composer, $composerFiles, 'require')));
private function mapRequireDependencies(array $composer, array $composerFiles, OutputInterface $output): array {
return array_unique(iterator_to_array($this->mapDependencies($composer, $composerFiles, 'require', $output)));
}

/**
Expand All @@ -155,8 +150,8 @@ private function mapRequireDependencies(array $composer, array $composerFiles):
*
* @return array<string>
*/
private function mapRequireDevDependencies(array $composer, array $composerFiles): array {
return array_unique(iterator_to_array($this->mapDependencies($composer, $composerFiles, 'require-dev')));
private function mapRequireDevDependencies(array $composer, array $composerFiles, OutputInterface $output): array {
return array_unique(iterator_to_array($this->mapDependencies($composer, $composerFiles, 'require-dev', $output)));
}

/**
Expand All @@ -165,16 +160,30 @@ private function mapRequireDevDependencies(array $composer, array $composerFiles
*
* @return iterable<string>
*/
private function mapDependencies(array $composer, array $composerFiles, string $key): iterable {
private function mapDependencies(array $composer, array $composerFiles, string $key, OutputInterface $output, array $log = []): iterable {

Check failure on line 163 in src/Command/LinkCommand.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Pmu\Command\LinkCommand::mapDependencies() has parameter $log with no value type specified in iterable type array.
foreach (array_keys($composer[$key] ?? []) as $package) {
if (!isset($composerFiles[$package]) || !is_string($package)) {
continue;
}

yield $package;

self::$fileContents[$composerFiles[$package]] = $this->readJsonFile($composerFiles[$package]);
foreach ($this->mapDependencies(static::$fileContents[$composerFiles[$package]], $composerFiles, $key) as $package) {
static::$fileContents[$composerFiles[$package]] = $this->readJsonFile($composerFiles[$package]);
if (!isset($log[$composer['name']])){

Check failure on line 172 in src/Command/LinkCommand.php

View workflow job for this annotation

GitHub Actions / PHPStan

Offset 'name' does not exist on array{repositories?: array<int, array{type: string, url: string}>, require?: array<string, string>, require-dev?: array<string, string>, extra?: array{pmu?: array{projects?: array<string>, exclude?: array<string>}}}.
$log[$composer['name']] = [];

Check failure on line 173 in src/Command/LinkCommand.php

View workflow job for this annotation

GitHub Actions / PHPStan

Offset 'name' does not exist on array{repositories?: array<int, array{type: string, url: string}>, require?: array<string, string>, require-dev?: array<string, string>, extra?: array{pmu?: array{projects?: array<string>, exclude?: array<string>}}}.
}

if (isset($log[$composer['name']][$package])) {

Check failure on line 176 in src/Command/LinkCommand.php

View workflow job for this annotation

GitHub Actions / PHPStan

Offset 'name' does not exist on array{repositories?: array<int, array{type: string, url: string}>, require?: array<string, string>, require-dev?: array<string, string>, extra?: array{pmu?: array{projects?: array<string>, exclude?: array<string>}}}.
$output->writeln('Circular dependency detected while computing the dependency tree.');
foreach ($log as $package => $dependencies) {
$output->writeln(sprintf('%s installs %s', $package, implode(', ', array_keys($dependencies))));
}
continue;
}

$log[$composer['name']][$package] = 1;

Check failure on line 184 in src/Command/LinkCommand.php

View workflow job for this annotation

GitHub Actions / PHPStan

Offset 'name' does not exist on array{repositories?: array<int, array{type: string, url: string}>, require?: array<string, string>, require-dev?: array<string, string>, extra?: array{pmu?: array{projects?: array<string>, exclude?: array<string>}}}.

foreach ($this->mapDependencies(static::$fileContents[$composerFiles[$package]], $composerFiles, $key, $output, $log) as $package) {
yield $package;
}
}
Expand Down

0 comments on commit 2606a03

Please sign in to comment.