Skip to content

Commit

Permalink
[1.x] Adds --dirty option (#130)
Browse files Browse the repository at this point in the history
* Introduce `--dirty` for Git projects

* Fix path case

* Correct docblocks

* Removes extra line on .gitignore

* Applies minor coding style changes

* Updates description

* Use `app` instead of `resolve`

* Ignores excluded files

* Includes new files

* Removes `cut` and avoids styling deleted files

* Filter deleted and renamed files

* Update DefaultCommand.php

* Update DefaultCommand.php

Co-authored-by: Jason McCreary <jason@pureconcepts.net>
Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
3 people committed Jan 4, 2023
1 parent a93c2ed commit f2ce5f7
Show file tree
Hide file tree
Showing 13 changed files with 211 additions and 32 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@
/vendor
composer.lock
.phpunit.result.cache

1 change: 1 addition & 0 deletions app/Commands/DefaultCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ protected function configure()
new InputOption('config', '', InputOption::VALUE_REQUIRED, 'The configuration that should be used'),
new InputOption('preset', '', InputOption::VALUE_REQUIRED, 'The preset that should be used'),
new InputOption('test', '', InputOption::VALUE_NONE, 'Test for code style errors without fixing them'),
new InputOption('dirty', '', InputOption::VALUE_NONE, 'Only fix files that have uncommitted changes'),
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'The output format that should be used'),
]
);
Expand Down
13 changes: 13 additions & 0 deletions app/Contracts/PathsRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace App\Contracts;

interface PathsRepository
{
/**
* Determine the "dirty" files.
*
* @return array<int, string>
*/
public function dirty();
}
28 changes: 19 additions & 9 deletions app/Factories/ConfigurationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ class ConfigurationFactory
* @return \PhpCsFixer\ConfigInterface
*/
public static function preset($rules)
{
return (new Config())
->setFinder(self::finder())
->setRules(array_merge($rules, resolve(ConfigurationJsonRepository::class)->rules()))
->setRiskyAllowed(true)
->setUsingCache(true)
->registerCustomFixers([
// Laravel...
new LaravelPhpdocAlignmentFixer(),
]);
}

/**
* Creates the finder instance.
*
* @return \PhpCsFixer\Finder
*/
public static function finder()
{
$localConfiguration = resolve(ConfigurationJsonRepository::class);

Expand All @@ -58,14 +76,6 @@ public static function preset($rules)
$finder->{$method}($arguments);
}

return (new Config())
->setFinder($finder)
->setRules(array_merge($rules, $localConfiguration->rules()))
->setRiskyAllowed(true)
->setUsingCache(true)
->registerCustomFixers([
// Laravel...
new LaravelPhpdocAlignmentFixer(),
]);
return $finder;
}
}
4 changes: 2 additions & 2 deletions app/Factories/ConfigurationResolverFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace App\Factories;

use App\Project;
use App\Repositories\ConfigurationJsonRepository;
use App\Support\Project;
use ArrayIterator;
use PhpCsFixer\Config;
use PhpCsFixer\Console\ConfigurationResolver;
Expand Down Expand Up @@ -32,7 +32,7 @@ class ConfigurationResolverFactory
*/
public static function fromIO($input, $output)
{
$path = $input->getArgument('path');
$path = Project::paths($input);

$localConfiguration = resolve(ConfigurationJsonRepository::class);

Expand Down
2 changes: 1 addition & 1 deletion app/Output/SummaryOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace App\Output;

use App\Output\Concerns\InteractsWithSymbols;
use App\Support\Project;
use App\Project;
use App\ValueObjects\Issue;
use PhpCsFixer\FixerFileProcessedEvent;
use function Termwind\render;
Expand Down
49 changes: 49 additions & 0 deletions app/Project.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace App;

use App\Contracts\PathsRepository;

class Project
{
/**
* Determine the project paths to apply the code style based on the options and arguments passed.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @return array<int, string>
*/
public static function paths($input)
{
if ($input->getOption('dirty')) {
return static::resolveDirtyPaths();
}

return $input->getArgument('path');
}

/**
* The project being analysed path.
*
* @return string
*/
public static function path()
{
return getcwd();
}

/**
* Resolves the dirty paths, if any.
*
* @return array<int, string>
*/
public static function resolveDirtyPaths()
{
$files = app(PathsRepository::class)->dirty();

if (empty($files)) {
abort(1, 'No dirty files found.');
}

return $files;
}
}
10 changes: 9 additions & 1 deletion app/Providers/RepositoriesServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace App\Providers;

use App\Contracts\PathsRepository;
use App\Project;
use App\Repositories\ConfigurationJsonRepository;
use App\Support\Project;
use App\Repositories\GitPathsRepository;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\Console\Input\InputInterface;

Expand Down Expand Up @@ -34,5 +36,11 @@ public function register()
$input->getOption('preset'),
);
});

$this->app->singleton(PathsRepository::class, function () {
return new GitPathsRepository(
Project::path(),
);
});
}
}
57 changes: 57 additions & 0 deletions app/Repositories/GitPathsRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace App\Repositories;

use App\Contracts\PathsRepository;
use App\Factories\ConfigurationFactory;
use Illuminate\Support\Str;
use Symfony\Component\Process\Process;

class GitPathsRepository implements PathsRepository
{
/**
* The project path.
*
* @var string
*/
protected $path;

/**
* Creates a new Paths Repository instance.
*
* @param string $path
*/
public function __construct($path)
{
$this->path = $path;
}

/**
* {@inheritDoc}
*/
public function dirty()
{
$process = tap(new Process(['git', 'status', '--short', '--', '*.php']))->run();

if (! $process->isSuccessful()) {
abort(1, 'The [--dirty] option is only available when using Git.');
}

$dirtyFiles = collect(preg_split('/\R+/', $process->getOutput(), flags: PREG_SPLIT_NO_EMPTY))
->mapWithKeys(fn ($file) => [substr($file, 3) => trim(substr($file, 0, 3))])
->reject(fn ($status) => $status === 'D')
->map(fn ($status, $file) => $status === 'R' ? Str::after($file, ' -> ') : $file)
->map(fn ($file) => $this->path.DIRECTORY_SEPARATOR.$file)
->values()
->all();

$files = array_values(array_map(function ($splFile) {
return $splFile->getPathname();
}, iterator_to_array(ConfigurationFactory::finder()
->in($this->path)
->files()
)));

return array_values(array_intersect($files, $dirtyFiles));
}
}
16 changes: 0 additions & 16 deletions app/Support/Project.php

This file was deleted.

2 changes: 2 additions & 0 deletions storage/framework/cache/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
57 changes: 57 additions & 0 deletions tests/Feature/DirtyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

use App\Contracts\PathsRepository;

it('determines dirty files', function () {
$paths = Mockery::mock(PathsRepository::class);

$paths
->shouldReceive('dirty')
->once()
->andReturn([
base_path('tests/Fixtures/without-issues/file.php'),
]);

$this->swap(PathsRepository::class, $paths);

[$statusCode, $output] = run('default', ['--dirty' => true]);

expect($statusCode)->toBe(0)
->and($output)
->toContain('── Laravel', ' 1 file');
});

it('ignores the path argument', function () {
$paths = Mockery::mock(PathsRepository::class);

$paths
->shouldReceive('dirty')
->once()
->andReturn([
base_path('tests/Fixtures/without-issues/file.php'),
]);

$this->swap(PathsRepository::class, $paths);

[$statusCode, $output] = run('default', [
'--dirty' => true,
'path' => base_path(),
]);

expect($statusCode)->toBe(0)
->and($output)
->toContain('── Laravel', ' 1 file');
});

it('aborts when there are no dirty files', function () {
$paths = Mockery::mock(PathsRepository::class);

$paths
->shouldReceive('dirty')
->once()
->andReturn([]);

$this->swap(PathsRepository::class, $paths);

run('default', ['--dirty' => true]);
})->throws(Exception::class, 'No dirty files found.');
3 changes: 1 addition & 2 deletions tests/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,9 @@ function run($command, $arguments)

if (isset($arguments['path'])) {
$arguments['--config'] = $arguments['path'].'/pint.json';
$arguments['path'] = [$arguments['path']];
}

$arguments['path'] = [$arguments['path']];

$commandInstance = match ($command) {
'default' => resolve(DefaultCommand::class),
};
Expand Down

0 comments on commit f2ce5f7

Please sign in to comment.