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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,14 @@ vendor/bin/structarmed analyze --report=json
vendor/bin/structarmed analyse --disable-parallel
```

## CLI version commands

```bash
# Print the installed StructArmed version
vendor/bin/structarmed --version
vendor/bin/structarmed -V
```

## Tips

### Rule key constants
Expand Down
7 changes: 4 additions & 3 deletions src/Cache/AnalysisCacheMetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Boundwize\StructArmed\Cache;

use Boundwize\StructArmed\Version;
use Composer\InstalledVersions;

use function array_map;
Expand Down Expand Up @@ -68,10 +69,10 @@ private function filesHash(array $files): string

public function composerGeneratedVersionHash(): string
{
$version = InstalledVersions::isInstalled('boundwize/structarmed')
$version = InstalledVersions::isInstalled(Version::PACKAGE_NAME)
? [
'prettyVersion' => InstalledVersions::getPrettyVersion('boundwize/structarmed'),
'reference' => InstalledVersions::getReference('boundwize/structarmed'),
'prettyVersion' => InstalledVersions::getPrettyVersion(Version::PACKAGE_NAME),
'reference' => InstalledVersions::getReference(Version::PACKAGE_NAME),
]
: InstalledVersions::getRootPackage();

Expand Down
7 changes: 7 additions & 0 deletions src/Cli/StructArmedApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Boundwize\StructArmed\Cli;

use Boundwize\StructArmed\Analyser\Parallel\ClassNodeWorker;
use Boundwize\StructArmed\Version;

use function array_slice;
use function getcwd;
Expand All @@ -25,6 +26,12 @@ public function run(array $argv, ?string $basePath = null): int
return ClassNodeWorker::run($argv[2] ?? '', $argv[3] ?? '');
}

if (in_array($command, ['--version', '-V'], true)) {
echo sprintf("StructArmed %s\n", Version::current());

return 0;
}

if (in_array($command, [null, '--help', '-h'], true)) {
echo Usage::render();

Expand Down
1 change: 1 addition & 0 deletions src/Cli/Usage.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public static function render(): string
{
return <<<'TXT'
Usage:
structarmed --version
structarmed init [--preset=ddd|mvc|psr1|psr4|all]
structarmed analyse|analyze [path ...] [--config=path/to/structarmed.php]
[--report=console|json] [--no-progress] [--clear-cache] [--disable-parallel]
Expand Down
16 changes: 10 additions & 6 deletions src/Report/Reports/ConsoleReport.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@
use Boundwize\StructArmed\Cli\ColorSupport;
use Boundwize\StructArmed\Report\ReportInterface;
use Boundwize\StructArmed\Rule\RuleViolationCollection;
use Boundwize\StructArmed\Version;

use function implode;
use function sprintf;
use function str_repeat;
use function strlen;

use const PHP_EOL;

final class ConsoleReport implements ReportInterface
{
public function render(RuleViolationCollection $ruleViolationCollection, float $elapsedSeconds): string
{
$useColor = ColorSupport::detect();
$lines = [];
$useColor = ColorSupport::detect();
$lines = [];
$heading = sprintf('StructArmed %s — Architecture Enforcement', Version::current());
$lineWidth = strlen($heading);

$lines[] = '';
$lines[] = 'StructArmed — Architecture Enforcement';
$lines[] = str_repeat('=', 42);
$lines[] = $heading;
$lines[] = str_repeat('=', $lineWidth);

if ($ruleViolationCollection->isEmpty()) {
$lines[] = '';
Expand All @@ -35,7 +39,7 @@ public function render(RuleViolationCollection $ruleViolationCollection, float $

$lines[] = '';
$lines[] = sprintf('Found %d violation(s):', $ruleViolationCollection->count());
$lines[] = str_repeat('─', 42);
$lines[] = str_repeat('─', $lineWidth);

foreach ($ruleViolationCollection as $violation) {
$lines[] = '';
Expand All @@ -49,7 +53,7 @@ public function render(RuleViolationCollection $ruleViolationCollection, float $
}

$lines[] = '';
$lines[] = str_repeat('─', 42);
$lines[] = str_repeat('─', $lineWidth);
$lines[] = sprintf(
'%d violation(s) found • %.2fs',
$ruleViolationCollection->count(),
Expand Down
28 changes: 28 additions & 0 deletions src/Version.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Boundwize\StructArmed;

use Composer\InstalledVersions;

final class Version
{
public const PACKAGE_NAME = 'boundwize/structarmed';

public static function current(): string
{
if (InstalledVersions::isInstalled(self::PACKAGE_NAME)) {
return InstalledVersions::getPrettyVersion(self::PACKAGE_NAME) ?? 'unknown';
}

return self::rootPackageVersion();
}

private static function rootPackageVersion(): string
{
$rootPackage = InstalledVersions::getRootPackage();

return $rootPackage['pretty_version'];
}
}
15 changes: 15 additions & 0 deletions tests/Cli/StructArmedApplicationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Boundwize\StructArmed\Cli\StructArmedApplication;
use Boundwize\StructArmed\Cli\Usage;
use Boundwize\StructArmed\Progress\ProgressHandlerInterface;
use Boundwize\StructArmed\Version;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
Expand All @@ -29,6 +30,7 @@
use function random_bytes;
use function rmdir;
use function serialize;
use function sprintf;
use function str_replace;
use function sys_get_temp_dir;
use function tempnam;
Expand All @@ -39,6 +41,7 @@
#[CoversClass(InitCommand::class)]
#[CoversClass(StructArmedApplication::class)]
#[CoversClass(Usage::class)]
#[CoversClass(Version::class)]
#[CoversClass(Baseline::class)]
final class StructArmedApplicationTest extends TestCase
{
Expand All @@ -47,10 +50,22 @@ public function testApplicationPrintsUsageWithoutCommand(): void
[$exitCode, $output] = $this->runApplication(['structarmed']);

$this->assertSame(0, $exitCode);
$this->assertStringContainsString('structarmed --version', $output);
$this->assertStringContainsString('structarmed init', $output);
$this->assertStringContainsString('structarmed analyse|analyze', $output);
}

public function testApplicationPrintsVersion(): void
{
[$exitCode, $output] = $this->runApplication(['structarmed', '--version']);

$this->assertSame(0, $exitCode);
$this->assertSame(
sprintf("StructArmed %s\n", Version::current()),
$output
);
}

public function testApplicationRejectsUnknownCommand(): void
{
[$exitCode, $output] = $this->runApplication(['structarmed', 'unknown']);
Expand Down
4 changes: 3 additions & 1 deletion tests/Report/ReportTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Boundwize\StructArmed\Report\Reports\JsonReport;
use Boundwize\StructArmed\Rule\RuleViolation;
use Boundwize\StructArmed\Rule\RuleViolationCollection;
use Boundwize\StructArmed\Version;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;

Expand All @@ -17,13 +18,14 @@

#[CoversClass(ConsoleReport::class)]
#[CoversClass(JsonReport::class)]
#[CoversClass(Version::class)]
final class ReportTest extends TestCase
{
public function testConsoleReportRendersPassingResult(): void
{
$report = (new ConsoleReport())->render(new RuleViolationCollection(), 0.12);

$this->assertStringContainsString('StructArmed', $report);
$this->assertStringContainsString('StructArmed ' . Version::current(), $report);
$this->assertStringContainsString('No violations found', $report);
}

Expand Down
139 changes: 139 additions & 0 deletions tests/VersionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php

declare(strict_types=1);

namespace Boundwize\StructArmed\Tests;

use Boundwize\StructArmed\Version;
use Composer\InstalledVersions;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use ReflectionProperty;

/**
* @phpstan-type InstalledRootPackage array{
* name: string,
* pretty_version: string,
* version: string,
* reference: string|null,
* type: string,
* install_path: string,
* aliases: array<string>,
* dev: bool
* }
* @phpstan-type InstalledPackageVersion array{
* pretty_version?: string,
* version?: string,
* reference?: string|null,
* type?: string,
* install_path?: string,
* aliases?: array<string>,
* dev_requirement: bool,
* replaced?: array<string>,
* provided?: array<string>
* }
* @phpstan-type InstalledVersionsData array{
* root: InstalledRootPackage,
* versions: array<string, InstalledPackageVersion>
* }
*/
#[CoversClass(Version::class)]
final class VersionTest extends TestCase
{
public function testCurrentUsesInstalledPackagePrettyVersion(): void
{
$this->withInstalledVersions([
'root' => $this->rootPackage('some/project', 'dev-root'),
'versions' => [
'boundwize/structarmed' => [
'pretty_version' => '1.2.3',
'version' => '1.2.3.0',
'reference' => 'abc123',
'type' => 'library',
'install_path' => __DIR__ . '/..',
'aliases' => [],
'dev_requirement' => false,
],
],
], static function (): void {
self::assertSame('1.2.3', Version::current());
});
}

public function testCurrentUsesUnknownWhenInstalledPackageHasNoPrettyVersion(): void
{
$this->withInstalledVersions([
'root' => $this->rootPackage('some/project', 'dev-root'),
'versions' => [
'boundwize/structarmed' => [
'version' => '1.2.3.0',
'reference' => 'abc123',
'type' => 'library',
'install_path' => __DIR__ . '/..',
'aliases' => [],
'dev_requirement' => false,
],
],
], static function (): void {
self::assertSame('unknown', Version::current());
});
}

public function testCurrentUsesRootPackageVersionWhenStructArmedIsNotInstalled(): void
{
$this->withInstalledVersions([
'root' => $this->rootPackage('some/project', 'dev-root'),
'versions' => [],
], static function (): void {
self::assertSame('dev-root', Version::current());
});
}

/**
* @param array<string, mixed> $installedVersions
* @phpstan-param InstalledVersionsData $installedVersions
* @param callable(): void $callback
*/
private function withInstalledVersions(array $installedVersions, callable $callback): void
{
$canGetVendors = new ReflectionProperty(InstalledVersions::class, 'canGetVendors');
$installed = new ReflectionProperty(InstalledVersions::class, 'installed');
$installedByVendor = new ReflectionProperty(InstalledVersions::class, 'installedByVendor');
$isLocalDir = new ReflectionProperty(InstalledVersions::class, 'installedIsLocalDir');

$origCanGetVendors = $canGetVendors->getValue();
$origInstalled = $installed->getValue();
$origInstalledByVendor = $installedByVendor->getValue();
$origIsLocalDir = $isLocalDir->getValue();

try {
$canGetVendors->setValue(null, false);
InstalledVersions::reload($installedVersions);

$callback();
} finally {
$installed->setValue(null, $origInstalled);
$installedByVendor->setValue(null, $origInstalledByVendor);
$isLocalDir->setValue(null, $origIsLocalDir);
$canGetVendors->setValue(null, $origCanGetVendors);
}
}

/**
* @phpstan-return InstalledRootPackage
* @return array<string, string|mixed[]|null|bool>
*/
private function rootPackage(string $name, string $version): array
{
return [
'name' => $name,
'pretty_version' => $version,
'version' => $version,
'reference' => null,
'type' => 'project',
'install_path' => __DIR__,
'aliases' => [],
'dev' => true,
];
}
}
Loading