Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ across Fast Forward libraries.

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

# Analyze missing and unused Composer dependencies
composer dependencies
vendor/bin/dev-tools dependencies

# Check and fix code style using ECS and Composer Normalize
composer dev-tools code-style

Expand Down Expand Up @@ -77,6 +83,10 @@ composer dev-tools gitignore
composer dev-tools:sync
```

The `dependencies` command ships with both dependency analyzers as direct
dependencies of `fast-forward/dev-tools`, so it works without extra
installation in the consumer project.

The `skills` command keeps `.agents/skills` aligned with the packaged Fast
Forward skill set. It creates missing links, repairs broken links, and
preserves existing non-symlink directories. The `dev-tools:sync` command calls
Expand All @@ -89,6 +99,7 @@ automation assets.
|---------|---------|
| `composer dev-tools` | Runs the full `standards` pipeline. |
| `composer dev-tools tests` | Runs PHPUnit with local-or-packaged configuration. |
| `composer dev-tools dependencies` | Reports missing and unused Composer dependencies. |
| `composer dev-tools docs` | Builds the HTML documentation site from PSR-4 code and `docs/`. |
| `composer dev-tools skills` | Creates or repairs packaged skill links in `.agents/skills`. |
| `composer dev-tools:sync` | Updates scripts, workflow stubs, `.editorconfig`, `.gitignore`, wiki setup, and packaged skills. |
Expand Down
9 changes: 9 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,23 @@
"fakerphp/faker": "^1.24",
"fast-forward/phpdoc-bootstrap-template": "^1.0",
"friendsofphp/php-cs-fixer": "^3.94",
"icanhazstring/composer-unused": "^0.9.6",
"jolicode/jolinotif": "^3.3",
"nikic/php-parser": "^5.7",
"phpdocumentor/shim": "^3.9",
"phpro/grumphp": "^2.19",
"phpspec/prophecy": "^1.26",
"phpspec/prophecy-phpunit": "^2.5",
"phpunit/phpunit": "^12.5",
"psr/log": "^3.0",
"pyrech/composer-changelogs": "^2.2",
"rector/rector": "^2.3",
"saggre/phpdocumentor-markdown": "^1.0",
"shipmonk/composer-dependency-analyser": "^1.8.4",
"symfony/console": "^7.3",
"symfony/filesystem": "^7.4",
"symfony/finder": "^7.4",
"symfony/process": "^7.4",
"symfony/var-dumper": "^7.4",
"symfony/var-exporter": "^7.4",
"symplify/easy-coding-standard": "^13.0",
Expand Down
3 changes: 3 additions & 0 deletions docs/api/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ resolution, configuration fallback, PSR-4 lookup, and child-command dispatch.
* - ``FastForward\DevTools\Command\TestsCommand``
- ``tests``
- Runs PHPUnit with optional coverage output.
* - ``FastForward\DevTools\Command\DependenciesCommand``
- ``dependencies``
- Reports missing and unused Composer dependencies.
* - ``FastForward\DevTools\Command\DocsCommand``
- ``docs``
- Builds the HTML documentation site.
Expand Down
20 changes: 20 additions & 0 deletions docs/running/specialized-commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,26 @@ Important details:
- ``--no-cache`` disables ``tmp/cache/phpunit``;
- the packaged configuration registers the DevTools PHPUnit extension.

``dependencies``
----------------

Analyzes missing and unused Composer dependencies.

.. code-block:: bash

composer dependencies
vendor/bin/dev-tools dependencies

Important details:

- it ships ``shipmonk/composer-dependency-analyser`` and
``icanhazstring/composer-unused`` as direct dependencies of
``fast-forward/dev-tools``;
- it uses ``composer-dependency-analyser`` only for missing dependency checks
and leaves unused-package reporting to ``composer-unused``;
- it returns a non-zero exit code when missing or unused dependencies are
found.

``code-style``
--------------

Expand Down
24 changes: 9 additions & 15 deletions src/Command/AbstractCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,42 +64,36 @@ public function __construct(?Filesystem $filesystem = null)
*
* @param Process $command the configured process instance to run
* @param OutputInterface $output the output interface to log warnings or results
* @param bool $tty
*
* @return int the status code of the command execution
*/
protected function runProcess(Process $command, OutputInterface $output): int
protected function runProcess(Process $command, OutputInterface $output, bool $tty = true): int
{
/** @var ProcessHelper $processHelper */
$processHelper = $this->getHelper('process');

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

if (Process::isTtySupported()) {
$command->setTty(true);
} else {
try {
$command->setTty($tty);
} catch (RuntimeException) {
$output->writeln(
'<comment>Warning: TTY is not supported. The command may not display output as expected.</comment>'
);
$tty = false;
}

if (! $tty) {
$callback = function (string $type, string $buffer) use ($output): void {
$output->write($buffer);
};
}

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

if (! $process->isSuccessful()) {
$output->writeln(\sprintf(
'<error>Command "%s" failed with exit code %d. Please check the output above for details.</error>',
$command->getCommandLine(),
$command->getExitCode()
));

return self::FAILURE;
}

return self::SUCCESS;
return $process->isSuccessful() ? self::SUCCESS : self::FAILURE;
}

/**
Expand Down
83 changes: 83 additions & 0 deletions src/Command/DependenciesCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

/**
* This file is part of fast-forward/dev-tools.
*
* This source file is subject to the license bundled
* with this source code in the file LICENSE.
*
* @copyright Copyright (c) 2026 Felipe Sayão Lobato Abreu <github@mentordosnerds.com>
* @license https://opensource.org/licenses/MIT MIT License
*
* @see https://github.com/php-fast-forward/dev-tools
* @see https://github.com/php-fast-forward
* @see https://datatracker.ietf.org/doc/html/rfc2119
*/

namespace FastForward\DevTools\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;

/**
* Orchestrates dependency analysis across the supported Composer analyzers.
* This command MUST report missing and unused dependencies using a single,
* deterministic report that is friendly for local development and CI runs.
*/
final class DependenciesCommand extends AbstractCommand
{
/**
* Configures the dependency analysis command metadata.
*
* The command MUST expose the `dependencies` name so it can run via both
* Composer and the local `dev-tools` binary.
*
* @return void
*/
protected function configure(): void
{
$this
->setName('dependencies')
->setAliases(['deps'])
->setDescription('Analyzes missing and unused Composer dependencies.')
->setHelp(
'This command runs composer-dependency-analyser and composer-unused to report '
. 'missing and unused Composer dependencies.'
);
}

/**
* Executes the dependency analysis workflow.
*
* The command MUST verify the required binaries before executing the tools,
* SHOULD normalize their machine-readable output into a unified report, and
* SHALL return a non-zero exit code when findings or execution failures exist.
*
* @param InputInterface $input the runtime command input
* @param OutputInterface $output the console output stream
*
* @return int the command execution status code
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('<info>Running dependency analysis...</info>');

$composerJson = $this->getConfigFile('composer.json');

$results[] = $this->runProcess(
new Process(['vendor/bin/composer-unused', $composerJson, '--no-progress']),
$output
);
$results[] = $this->runProcess(new Process([
'vendor/bin/composer-dependency-analyser',
'--composer-json=' . $composerJson,
'--ignore-unused-deps',
'--ignore-prod-only-in-dev-deps',
]), $output);

return \in_array(self::FAILURE, $results, true) ? self::FAILURE : self::SUCCESS;
}
}
2 changes: 2 additions & 0 deletions src/Composer/Capability/DevToolsCommandProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use FastForward\DevTools\Command\AbstractCommand;
use Composer\Plugin\Capability\CommandProvider as CommandProviderCapability;
use FastForward\DevTools\Command\CodeStyleCommand;
use FastForward\DevTools\Command\DependenciesCommand;
use FastForward\DevTools\Command\DocsCommand;
use FastForward\DevTools\Command\GitIgnoreCommand;
use FastForward\DevTools\Command\PhpDocCommand;
Expand Down Expand Up @@ -52,6 +53,7 @@ public function getCommands()
new CodeStyleCommand(),
new RefactorCommand(),
new TestsCommand(),
new DependenciesCommand(),
new PhpDocCommand(),
new DocsCommand(),
new StandardsCommand(),
Expand Down
Loading
Loading