Skip to content

Commit f835cd0

Browse files
authored
Merge pull request #24 from php-fast-forward/codex/issue-10-dependencies-command
2 parents 31e94d1 + 8b3db07 commit f835cd0

File tree

9 files changed

+380
-15
lines changed

9 files changed

+380
-15
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ across Fast Forward libraries.
1616

1717
- Aggregates refactoring, PHPDoc, code style, tests, and reporting under a
1818
single Composer-facing command vocabulary
19+
- Adds dependency analysis for missing and unused Composer packages through a
20+
single report entrypoint
1921
- Ships shared workflow stubs, `.editorconfig`, Dependabot configuration, and
2022
other onboarding defaults for consumer repositories
2123
- Synchronizes packaged agent skills into consumer `.agents/skills`
@@ -47,6 +49,10 @@ You can also run individual commands for specific development tasks:
4749
# Run PHPUnit tests
4850
composer dev-tools tests
4951

52+
# Analyze missing and unused Composer dependencies
53+
composer dependencies
54+
vendor/bin/dev-tools dependencies
55+
5056
# Check and fix code style using ECS and Composer Normalize
5157
composer dev-tools code-style
5258

@@ -77,6 +83,10 @@ composer dev-tools gitignore
7783
composer dev-tools:sync
7884
```
7985

86+
The `dependencies` command ships with both dependency analyzers as direct
87+
dependencies of `fast-forward/dev-tools`, so it works without extra
88+
installation in the consumer project.
89+
8090
The `skills` command keeps `.agents/skills` aligned with the packaged Fast
8191
Forward skill set. It creates missing links, repairs broken links, and
8292
preserves existing non-symlink directories. The `dev-tools:sync` command calls
@@ -89,6 +99,7 @@ automation assets.
8999
|---------|---------|
90100
| `composer dev-tools` | Runs the full `standards` pipeline. |
91101
| `composer dev-tools tests` | Runs PHPUnit with local-or-packaged configuration. |
102+
| `composer dev-tools dependencies` | Reports missing and unused Composer dependencies. |
92103
| `composer dev-tools docs` | Builds the HTML documentation site from PSR-4 code and `docs/`. |
93104
| `composer dev-tools skills` | Creates or repairs packaged skill links in `.agents/skills`. |
94105
| `composer dev-tools:sync` | Updates scripts, workflow stubs, `.editorconfig`, `.gitignore`, wiki setup, and packaged skills. |

composer.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,23 @@
3434
"fakerphp/faker": "^1.24",
3535
"fast-forward/phpdoc-bootstrap-template": "^1.0",
3636
"friendsofphp/php-cs-fixer": "^3.94",
37+
"icanhazstring/composer-unused": "^0.9.6",
3738
"jolicode/jolinotif": "^3.3",
39+
"nikic/php-parser": "^5.7",
3840
"phpdocumentor/shim": "^3.9",
3941
"phpro/grumphp": "^2.19",
42+
"phpspec/prophecy": "^1.26",
4043
"phpspec/prophecy-phpunit": "^2.5",
4144
"phpunit/phpunit": "^12.5",
45+
"psr/log": "^3.0",
4246
"pyrech/composer-changelogs": "^2.2",
4347
"rector/rector": "^2.3",
4448
"saggre/phpdocumentor-markdown": "^1.0",
49+
"shipmonk/composer-dependency-analyser": "^1.8.4",
50+
"symfony/console": "^7.3",
51+
"symfony/filesystem": "^7.4",
52+
"symfony/finder": "^7.4",
53+
"symfony/process": "^7.4",
4554
"symfony/var-dumper": "^7.4",
4655
"symfony/var-exporter": "^7.4",
4756
"symplify/easy-coding-standard": "^13.0",

docs/api/commands.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ resolution, configuration fallback, PSR-4 lookup, and child-command dispatch.
3030
* - ``FastForward\DevTools\Command\TestsCommand``
3131
- ``tests``
3232
- Runs PHPUnit with optional coverage output.
33+
* - ``FastForward\DevTools\Command\DependenciesCommand``
34+
- ``dependencies``
35+
- Reports missing and unused Composer dependencies.
3336
* - ``FastForward\DevTools\Command\DocsCommand``
3437
- ``docs``
3538
- Builds the HTML documentation site.

docs/running/specialized-commands.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,26 @@ Important details:
2121
- ``--no-cache`` disables ``tmp/cache/phpunit``;
2222
- the packaged configuration registers the DevTools PHPUnit extension.
2323

24+
``dependencies``
25+
----------------
26+
27+
Analyzes missing and unused Composer dependencies.
28+
29+
.. code-block:: bash
30+
31+
composer dependencies
32+
vendor/bin/dev-tools dependencies
33+
34+
Important details:
35+
36+
- it ships ``shipmonk/composer-dependency-analyser`` and
37+
``icanhazstring/composer-unused`` as direct dependencies of
38+
``fast-forward/dev-tools``;
39+
- it uses ``composer-dependency-analyser`` only for missing dependency checks
40+
and leaves unused-package reporting to ``composer-unused``;
41+
- it returns a non-zero exit code when missing or unused dependencies are
42+
found.
43+
2444
``code-style``
2545
--------------
2646

src/Command/AbstractCommand.php

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,42 +64,36 @@ public function __construct(?Filesystem $filesystem = null)
6464
*
6565
* @param Process $command the configured process instance to run
6666
* @param OutputInterface $output the output interface to log warnings or results
67+
* @param bool $tty
6768
*
6869
* @return int the status code of the command execution
6970
*/
70-
protected function runProcess(Process $command, OutputInterface $output): int
71+
protected function runProcess(Process $command, OutputInterface $output, bool $tty = true): int
7172
{
7273
/** @var ProcessHelper $processHelper */
7374
$processHelper = $this->getHelper('process');
7475

7576
$command = $command->setWorkingDirectory($this->getCurrentWorkingDirectory());
7677
$callback = null;
7778

78-
if (Process::isTtySupported()) {
79-
$command->setTty(true);
80-
} else {
79+
try {
80+
$command->setTty($tty);
81+
} catch (RuntimeException) {
8182
$output->writeln(
8283
'<comment>Warning: TTY is not supported. The command may not display output as expected.</comment>'
8384
);
85+
$tty = false;
86+
}
8487

88+
if (! $tty) {
8589
$callback = function (string $type, string $buffer) use ($output): void {
8690
$output->write($buffer);
8791
};
8892
}
8993

9094
$process = $processHelper->run(output: $output, cmd: $command, callback: $callback);
9195

92-
if (! $process->isSuccessful()) {
93-
$output->writeln(\sprintf(
94-
'<error>Command "%s" failed with exit code %d. Please check the output above for details.</error>',
95-
$command->getCommandLine(),
96-
$command->getExitCode()
97-
));
98-
99-
return self::FAILURE;
100-
}
101-
102-
return self::SUCCESS;
96+
return $process->isSuccessful() ? self::SUCCESS : self::FAILURE;
10397
}
10498

10599
/**
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of fast-forward/dev-tools.
7+
*
8+
* This source file is subject to the license bundled
9+
* with this source code in the file LICENSE.
10+
*
11+
* @copyright Copyright (c) 2026 Felipe Sayão Lobato Abreu <github@mentordosnerds.com>
12+
* @license https://opensource.org/licenses/MIT MIT License
13+
*
14+
* @see https://github.com/php-fast-forward/dev-tools
15+
* @see https://github.com/php-fast-forward
16+
* @see https://datatracker.ietf.org/doc/html/rfc2119
17+
*/
18+
19+
namespace FastForward\DevTools\Command;
20+
21+
use Symfony\Component\Console\Input\InputInterface;
22+
use Symfony\Component\Console\Output\OutputInterface;
23+
use Symfony\Component\Process\Process;
24+
25+
/**
26+
* Orchestrates dependency analysis across the supported Composer analyzers.
27+
* This command MUST report missing and unused dependencies using a single,
28+
* deterministic report that is friendly for local development and CI runs.
29+
*/
30+
final class DependenciesCommand extends AbstractCommand
31+
{
32+
/**
33+
* Configures the dependency analysis command metadata.
34+
*
35+
* The command MUST expose the `dependencies` name so it can run via both
36+
* Composer and the local `dev-tools` binary.
37+
*
38+
* @return void
39+
*/
40+
protected function configure(): void
41+
{
42+
$this
43+
->setName('dependencies')
44+
->setAliases(['deps'])
45+
->setDescription('Analyzes missing and unused Composer dependencies.')
46+
->setHelp(
47+
'This command runs composer-dependency-analyser and composer-unused to report '
48+
. 'missing and unused Composer dependencies.'
49+
);
50+
}
51+
52+
/**
53+
* Executes the dependency analysis workflow.
54+
*
55+
* The command MUST verify the required binaries before executing the tools,
56+
* SHOULD normalize their machine-readable output into a unified report, and
57+
* SHALL return a non-zero exit code when findings or execution failures exist.
58+
*
59+
* @param InputInterface $input the runtime command input
60+
* @param OutputInterface $output the console output stream
61+
*
62+
* @return int the command execution status code
63+
*/
64+
protected function execute(InputInterface $input, OutputInterface $output): int
65+
{
66+
$output->writeln('<info>Running dependency analysis...</info>');
67+
68+
$composerJson = $this->getConfigFile('composer.json');
69+
70+
$results[] = $this->runProcess(
71+
new Process(['vendor/bin/composer-unused', $composerJson, '--no-progress']),
72+
$output
73+
);
74+
$results[] = $this->runProcess(new Process([
75+
'vendor/bin/composer-dependency-analyser',
76+
'--composer-json=' . $composerJson,
77+
'--ignore-unused-deps',
78+
'--ignore-prod-only-in-dev-deps',
79+
]), $output);
80+
81+
return \in_array(self::FAILURE, $results, true) ? self::FAILURE : self::SUCCESS;
82+
}
83+
}

src/Composer/Capability/DevToolsCommandProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use FastForward\DevTools\Command\AbstractCommand;
2222
use Composer\Plugin\Capability\CommandProvider as CommandProviderCapability;
2323
use FastForward\DevTools\Command\CodeStyleCommand;
24+
use FastForward\DevTools\Command\DependenciesCommand;
2425
use FastForward\DevTools\Command\DocsCommand;
2526
use FastForward\DevTools\Command\GitIgnoreCommand;
2627
use FastForward\DevTools\Command\PhpDocCommand;
@@ -52,6 +53,7 @@ public function getCommands()
5253
new CodeStyleCommand(),
5354
new RefactorCommand(),
5455
new TestsCommand(),
56+
new DependenciesCommand(),
5557
new PhpDocCommand(),
5658
new DocsCommand(),
5759
new StandardsCommand(),

0 commit comments

Comments
 (0)