Skip to content

Commit

Permalink
Add --output-file option to directly write formatted result output to…
Browse files Browse the repository at this point in the history
… file
  • Loading branch information
Stephan Wentz authored and icanhazstring committed Mar 21, 2024
1 parent ab1d0b5 commit ad94b7d
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 1 deletion.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"dg/bypass-finals": "^1.6",
"ergebnis/composer-normalize": "^2.42",
"jangregor/phpstan-prophecy": "^1.0",
"mikey179/vfsstream": "^1.6",
"php-ds/php-ds": "^1.5",
"phpspec/prophecy-phpunit": "^2.2.0",
"phpstan/extension-installer": "^1.3",
Expand Down
39 changes: 38 additions & 1 deletion src/Console/Command/UnusedCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,22 @@
use ComposerUnused\ComposerUnused\Dependency\DependencyInterface;
use ComposerUnused\ComposerUnused\Dependency\RequiredDependency;
use ComposerUnused\ComposerUnused\Filter\FilterCollection;
use ComposerUnused\ComposerUnused\Output\FileOutput;
use ComposerUnused\ComposerUnused\Output\OutputStyleOutput;
use ComposerUnused\ComposerUnused\OutputFormatter\FormatterFactory;
use InvalidArgumentException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

use function dirname;
use function file_put_contents;
use function is_array;
use function is_writable;
use function sprintf;

use const DIRECTORY_SEPARATOR;
Expand Down Expand Up @@ -85,6 +91,13 @@ protected function configure(): void
'Change output style (default, github, json, junit, gitlab)'
);

$this->addOption(
'output-file',
null,
InputOption::VALUE_REQUIRED,
'Write output to given file'
);

$this->addOption(
'excludeDir',
null,
Expand Down Expand Up @@ -132,6 +145,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$ignoreExitCode = (bool)$input->getOption('ignore-exit-code');
/** @var string|list<string> $excludedDirs */
$excludedDirs = $input->getOption('excludeDir');
$outputFile = $input->getOption('output-file');

if (!is_array($excludedDirs)) {
$excludedDirs = [$excludedDirs];
}
Expand All @@ -147,6 +162,24 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return $ignoreExitCode ? 0 : 1;
}

if ($outputFile) {
if (!is_writable(dirname($outputFile))) {
$io->error(
sprintf(
'The directory of the output file %s is not writable.',
$outputFile
)
);

return $ignoreExitCode ? 0 : 1;
}

$bufferedOutput = new BufferedOutput();
$formatOutput = new SymfonyStyle($input, $bufferedOutput);
} else {
$formatOutput = $io;
}

try {
$composerConfig = $this->configFactory->fromPath($composerJsonPath);
} catch (InvalidArgumentException $e) {
Expand Down Expand Up @@ -289,9 +322,13 @@ static function (DependencyInterface $dependency) use ($filterCollection) {
$unusedDependencyCollection,
$ignoredDependencyCollection,
$filterCollection,
$io
$formatOutput
);

if ($outputFile) {
file_put_contents($outputFile, $bufferedOutput->fetch());
}

return !$ignoreExitCode ? $exitCode : 0;
}
}
114 changes: 114 additions & 0 deletions tests/Integration/UnusedCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace ComposerUnused\ComposerUnused\Test\Integration;

use ComposerUnused\ComposerUnused\Console\Command\UnusedCommand;
use org\bovigo\vfs\vfsStream;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Tester\CommandTester;
Expand Down Expand Up @@ -467,4 +468,117 @@ public function itDisplayErrorMessageWhenConfigValidationNotPassed(): void

self::assertSame(1, $exitCode);
}

/**
* @test
*/
public function itShouldWriteTextFormattedOutputToFile(): void
{
$commandTester = new CommandTester(self::$container->get(UnusedCommand::class));

vfsStream::setup();

$exitCode = $commandTester->execute([
'composer-json' => __DIR__ . '/../assets/TestProjects/IgnorePatternPackages/composer.json',
'--output-file' => vfsStream::url('root/test.log'),
]);

self::assertTrue(file_exists(vfsStream::url('root/test.log')));

self::assertStringContainsString(
"Found 0 used, 1 unused, 2 ignored and 0 zombie packages",
(string) file_get_contents(vfsStream::url('root/test.log'))
);

self::assertSame(1, $exitCode);
}

/**
* @test
*/
public function itShouldDisplayAnErrorMessageOnNotWritableDirectory(): void
{
$commandTester = new CommandTester(self::$container->get(UnusedCommand::class));

vfsStream::setup();

$exitCode = $commandTester->execute([
'composer-json' => __DIR__ . '/../assets/TestProjects/IgnorePatternPackages/composer.json',
'--output-file' => vfsStream::url('root/invalid/test.log'),
]);

self::assertFalse(file_exists(vfsStream::url('root/invalid/test.log')));

self::assertStringContainsString(
"The directory of the output file vfs://root/invalid/test.log is not writable.",
$commandTester->getDisplay()
);

self::assertSame(1, $exitCode);
}

/**
* @test
*/
public function itShouldWriteGitlabFormattedOutputToFile(): void
{
$expected = <<<'JSON'
[
{
"description": "dummy/test-package is unused",
"fingerprint": "0a07fbcd12300baf6461b9c0012a502d22faf7e6d0c51afc0700b2ba8b450eaf",
"location": {
"lines": {
"begin": 4
},
"path": "<path>/../assets/TestProjects/IgnorePatternPackages/composer.json"
},
"severity": "major"
},
{
"description": "psr/log-implementation was ignored",
"fingerprint": "c849d69b9a9b6cedba6c13d0635ebd1371cd66b82a5fcc55e04cfb4445254e12",
"location": {
"lines": {
"begin": 5
},
"path": "<path>/../assets/TestProjects/IgnorePatternPackages/composer.json"
},
"severity": "info"
},
{
"description": "dummy/ff-implementation was ignored",
"fingerprint": "df9c1b4be4940479665de3bbcc82ba7015b560cb168d753bc0b42751f48d317e",
"location": {
"lines": {
"begin": 6
},
"path": "<path>/../assets/TestProjects/IgnorePatternPackages/composer.json"
},
"severity": "info"
}
]
JSON;

$commandTester = new CommandTester(self::$container->get(UnusedCommand::class));

vfsStream::setup();

$exitCode = $commandTester->execute([
'composer-json' => __DIR__ . '/../assets/TestProjects/IgnorePatternPackages/composer.json',
'--output-file' => vfsStream::url('root/test.json'),
'--output-format' => 'gitlab',
]);

self::assertTrue(file_exists(vfsStream::url('root/test.json')));

$expected = str_replace('<path>', __DIR__, $expected);

self::assertJsonStringEqualsJsonString(
$expected,
(string) file_get_contents(vfsStream::url('root/test.json')),
);

self::assertSame(1, $exitCode);
}
}

0 comments on commit ad94b7d

Please sign in to comment.