Skip to content

Commit 86a9eb7

Browse files
committed
WIP Introduces reusable trait
1 parent ffbb7e4 commit 86a9eb7

File tree

7 files changed

+230
-8
lines changed

7 files changed

+230
-8
lines changed

.github/workflows/test-windows.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on: push
55
jobs:
66
build:
77
name: "PHPUnit (PHP ${{ matrix.php }})"
8-
runs-on: "windows-latest"
8+
runs-on: "windows-2022"
99

1010
strategy:
1111
matrix:
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Stolt\LeanPackage\Commands\Concerns;
6+
7+
use SplFileInfo;
8+
use Stolt\LeanPackage\Analyser;
9+
use Stolt\LeanPackage\Exceptions\InvalidGlobPattern;
10+
use Stolt\LeanPackage\Exceptions\InvalidGlobPatternFile;
11+
use Stolt\LeanPackage\Exceptions\NonExistentGlobPatternFile;
12+
use Symfony\Component\Console\Input\InputInterface;
13+
use Symfony\Component\Console\Input\InputOption;
14+
use Symfony\Component\Console\Output\OutputInterface;
15+
16+
trait GeneratesGitattributesOptions
17+
{
18+
protected string $defaultPreset = 'Php';
19+
20+
protected function getDefaultLpvFile(): string
21+
{
22+
$base = \defined('WORKING_DIRECTORY') ? WORKING_DIRECTORY : (\getcwd() ?: '.');
23+
24+
return $base . DIRECTORY_SEPARATOR . '.lpv';
25+
}
26+
27+
protected function addGenerationOptions(callable $addOption): void
28+
{
29+
// Glob/preset related
30+
$addOption('glob-pattern', null, InputOption::VALUE_REQUIRED, 'Use this glob pattern to match artifacts that should be export-ignored');
31+
$addOption('glob-pattern-file', null, InputOption::VALUE_OPTIONAL, 'Use this file with glob patterns to match export-ignored artifacts', $this->getDefaultLpvFile());
32+
$addOption('preset', null, InputOption::VALUE_OPTIONAL, 'Use the glob pattern of the given language preset', $this->defaultPreset);
33+
34+
// Keep rules
35+
$addOption('keep-license', null, InputOption::VALUE_NONE, 'Do not export-ignore the license file');
36+
$addOption('keep-readme', null, InputOption::VALUE_NONE, 'Do not export-ignore the README file');
37+
$addOption('keep-glob-pattern', null, InputOption::VALUE_REQUIRED, 'Do not export-ignore matching glob pattern e.g. {LICENSE.*,README.*,docs*}');
38+
39+
// Layout/ordering
40+
$addOption('align-export-ignores', 'a', InputOption::VALUE_NONE, 'Align export-ignores on create or overwrite');
41+
$addOption('sort-from-directories-to-files', 's', InputOption::VALUE_NONE, 'Sort export-ignores from directories to files');
42+
$addOption('enforce-strict-order', null, InputOption::VALUE_NONE, 'Enforce strict order comparison (useful for consistency)');
43+
}
44+
45+
/**
46+
* Apply generation-related options to the analyser.
47+
*/
48+
protected function applyGenerationOptions(InputInterface $input, OutputInterface $output, Analyser $analyser): bool
49+
{
50+
$globPattern = $input->getOption('glob-pattern');
51+
$globPatternFile = (string) $input->getOption('glob-pattern-file');
52+
$chosenPreset = (string) $input->getOption('preset');
53+
54+
$keepLicense = (bool) $input->getOption('keep-license');
55+
$keepReadme = (bool) $input->getOption('keep-readme');
56+
$keepGlobPattern = (string) ($input->getOption('keep-glob-pattern') ?? '');
57+
58+
$alignExportIgnores = (bool) $input->getOption('align-export-ignores');
59+
$sortFromDirectoriesToFiles = (bool) $input->getOption('sort-from-directories-to-files');
60+
$enforceStrictOrderComparison = (bool) $input->getOption('enforce-strict-order');
61+
62+
// Order comparison (for consistency in generation/validation flow)
63+
if ($enforceStrictOrderComparison && $sortFromDirectoriesToFiles === false) {
64+
$output->writeln('+ Enforcing strict order comparison.', OutputInterface::VERBOSITY_VERBOSE);
65+
$analyser->enableStrictOrderComparison();
66+
}
67+
68+
if ($sortFromDirectoriesToFiles) {
69+
$output->writeln('+ Sorting from files to directories.', OutputInterface::VERBOSITY_VERBOSE);
70+
$analyser->sortFromDirectoriesToFiles();
71+
}
72+
73+
if ($keepLicense) {
74+
$output->writeln('+ Keeping the license file.', OutputInterface::VERBOSITY_VERBOSE);
75+
$analyser->keepLicense();
76+
}
77+
78+
if ($keepReadme) {
79+
$output->writeln('+ Keeping the README file.', OutputInterface::VERBOSITY_VERBOSE);
80+
$analyser->keepReadme();
81+
}
82+
83+
if ($keepGlobPattern !== '') {
84+
$output->writeln(\sprintf('+ Keeping files matching the glob pattern <info>%s</info>.', $keepGlobPattern), OutputInterface::VERBOSITY_VERBOSE);
85+
try {
86+
$analyser->setKeepGlobPattern($keepGlobPattern);
87+
} catch (InvalidGlobPattern $e) {
88+
$warning = "Warning: The provided glob pattern '{$keepGlobPattern}' is considered invalid.";
89+
$output->writeln('<error>' . $warning . '</error>');
90+
$output->writeln($e->getMessage(), OutputInterface::VERBOSITY_DEBUG);
91+
92+
return false;
93+
}
94+
}
95+
96+
if ($alignExportIgnores) {
97+
$output->writeln('+ Aligning the export-ignores.', OutputInterface::VERBOSITY_VERBOSE);
98+
$analyser->alignExportIgnores();
99+
}
100+
101+
// Glob selection/override order: explicit pattern -> glob file -> preset
102+
if ($globPattern || $globPattern === '') {
103+
try {
104+
$output->writeln("+ Using glob pattern <info>{$globPattern}</info>.", OutputInterface::VERBOSITY_VERBOSE);
105+
$analyser->setGlobPattern((string) $globPattern);
106+
} catch (InvalidGlobPattern $e) {
107+
$warning = "Warning: The provided glob pattern '{$globPattern}' is considered invalid.";
108+
$output->writeln('<error>' . $warning . '</error>');
109+
$output->writeln($e->getMessage(), OutputInterface::VERBOSITY_DEBUG);
110+
111+
return false;
112+
}
113+
} elseif ($this->isGlobPatternFileSettable($globPatternFile)) {
114+
try {
115+
if ($this->isDefaultGlobPatternFilePresent()) {
116+
$analyser->setGlobPatternFromFile($globPatternFile);
117+
}
118+
if ($globPatternFile) {
119+
$globPatternFileInfo = new SplFileInfo($globPatternFile);
120+
$output->writeln('+ Using ' . $globPatternFileInfo->getBasename() . ' file as glob pattern input.', OutputInterface::VERBOSITY_VERBOSE);
121+
122+
$analyser->setGlobPatternFromFile($globPatternFile);
123+
}
124+
} catch (NonExistentGlobPatternFile $e) {
125+
$warning = "Warning: The provided glob pattern file '{$globPatternFile}' doesn't exist.";
126+
$output->writeln('<error>' . $warning . '</error>');
127+
$output->writeln($e->getMessage(), OutputInterface::VERBOSITY_DEBUG);
128+
129+
return false;
130+
} catch (InvalidGlobPatternFile $e) {
131+
$warning = "Warning: The provided glob pattern file '{$globPatternFile}' is considered invalid.";
132+
$output->writeln('<error>' . $warning . '</error>');
133+
$output->writeln($e->getMessage(), OutputInterface::VERBOSITY_DEBUG);
134+
135+
return false;
136+
}
137+
} elseif ($chosenPreset) {
138+
try {
139+
$output->writeln('+ Using the ' . $chosenPreset . ' language preset.', OutputInterface::VERBOSITY_VERBOSE);
140+
$analyser->setGlobPatternFromPreset($chosenPreset);
141+
} catch (\Stolt\LeanPackage\Exceptions\PresetNotAvailable $e) {
142+
$warning = 'Warning: The chosen language preset ' . $chosenPreset . ' is not available. Maybe contribute it?.';
143+
$output->writeln('<error>' . $warning . '</error>');
144+
145+
return false;
146+
}
147+
}
148+
149+
return true;
150+
}
151+
152+
// Minimal copies of helper checks used in ValidateCommand
153+
protected function isGlobPatternFileSettable(string $file): bool
154+
{
155+
if ($this->isGlobPatternFileProvided($file)) {
156+
return true;
157+
}
158+
159+
return $this->isDefaultGlobPatternFilePresent();
160+
}
161+
162+
protected function isGlobPatternFileProvided(string $file): bool
163+
{
164+
return $file !== $this->getDefaultLpvFile();
165+
}
166+
167+
protected function isDefaultGlobPatternFilePresent(): bool
168+
{
169+
return \file_exists($this->getDefaultLpvFile());
170+
}
171+
}

src/Commands/CreateCommand.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55
namespace Stolt\LeanPackage\Commands;
66

77
use Stolt\LeanPackage\Analyser;
8+
use Stolt\LeanPackage\Commands\Concerns\GeneratesGitattributesOptions;
89
use Stolt\LeanPackage\GitattributesFileRepository;
910
use Symfony\Component\Console\Command\Command;
1011
use Symfony\Component\Console\Input\InputArgument;
1112
use Symfony\Component\Console\Input\InputInterface;
13+
use Symfony\Component\Console\Input\InputOption;
1214
use Symfony\Component\Console\Output\OutputInterface;
1315

1416
final class CreateCommand extends Command
1517
{
18+
use GeneratesGitattributesOptions;
19+
1620
/**
1721
* @var string $defaultName
1822
*/
@@ -38,13 +42,23 @@ protected function configure(): void
3842
'The package directory to create the .gitattributes file in',
3943
\defined('WORKING_DIRECTORY') ? WORKING_DIRECTORY : \getcwd()
4044
)->setName(self::$defaultName)->setDescription(self::$defaultDescription);
45+
46+
// Add common generation options
47+
$this->addGenerationOptions(function (...$args) {
48+
$this->getDefinition()->addOption(new InputOption(...$args));
49+
});
4150
}
4251

4352
protected function execute(InputInterface $input, OutputInterface $output): int
4453
{
4554
$directory = (string) $input->getArgument('directory') ?: \getcwd();
4655
$this->analyser->setDirectory($directory);
4756

57+
// Apply options that influence generation
58+
if (!$this->applyGenerationOptions($input, $output, $this->analyser)) {
59+
return self::FAILURE;
60+
}
61+
4862
$gitattributesPath = $this->analyser->getGitattributesFilePath();
4963

5064
if (\file_exists($gitattributesPath)) {

src/Commands/UpdateCommand.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55
namespace Stolt\LeanPackage\Commands;
66

77
use Stolt\LeanPackage\Analyser;
8+
use Stolt\LeanPackage\Commands\Concerns\GeneratesGitattributesOptions;
89
use Stolt\LeanPackage\GitattributesFileRepository;
910
use Symfony\Component\Console\Command\Command;
1011
use Symfony\Component\Console\Input\InputArgument;
1112
use Symfony\Component\Console\Input\InputInterface;
13+
use Symfony\Component\Console\Input\InputOption;
1214
use Symfony\Component\Console\Output\OutputInterface;
1315

1416
final class UpdateCommand extends Command
1517
{
18+
use GeneratesGitattributesOptions;
19+
1620
/**
1721
* @var string $defaultName
1822
*/
@@ -38,13 +42,24 @@ protected function configure(): void
3842
'The package directory whose .gitattributes file should be updated',
3943
\defined('WORKING_DIRECTORY') ? WORKING_DIRECTORY : \getcwd()
4044
)->setName(self::$defaultName)->setDescription(self::$defaultDescription);
45+
46+
// Add common generation options
47+
$this->addGenerationOptions(function (...$args) {
48+
$this->getDefinition()->addOption(new InputOption(...$args));
49+
});
50+
4151
}
4252

4353
protected function execute(InputInterface $input, OutputInterface $output): int
4454
{
4555
$directory = (string) $input->getArgument('directory') ?: \getcwd();
4656
$this->analyser->setDirectory($directory);
4757

58+
// Apply options that influence generation
59+
if (!$this->applyGenerationOptions($input, $output, $this->analyser)) {
60+
return self::FAILURE;
61+
}
62+
4863
$gitattributesPath = $this->analyser->getGitattributesFilePath();
4964

5065
if (!\file_exists($gitattributesPath)) {

src/Commands/ValidateCommand.php

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use SplFileInfo;
1010
use Stolt\LeanPackage\Analyser;
1111
use Stolt\LeanPackage\Archive\Validator;
12+
use Stolt\LeanPackage\Commands\Concerns\GeneratesGitattributesOptions;
1213
use Stolt\LeanPackage\Exceptions\GitArchiveNotValidatedYet;
1314
use Stolt\LeanPackage\Exceptions\GitattributesCreationFailed;
1415
use Stolt\LeanPackage\Exceptions\GitHeadNotAvailable;
@@ -28,6 +29,8 @@
2829

2930
final class ValidateCommand extends Command
3031
{
32+
use GeneratesGitattributesOptions;
33+
3134
/**
3235
* Default glob pattern file.
3336
*
@@ -37,10 +40,6 @@ final class ValidateCommand extends Command
3740

3841
protected string $defaultPreset = 'Php';
3942

40-
protected string $generatedHeader = '# This file was generated by the lean package validator (http://git.io/lean-package-validator).';
41-
42-
protected string $modifiedHeader = '# This file was partly modified by the lean package validator (http://git.io/lean-package-validator).';
43-
4443
/**
4544
* Package analyser.
4645
*
@@ -284,16 +283,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int
284283
$verboseOutput = '+ Scanning directory ' . $directory . '.';
285284
$output->writeln($verboseOutput, OutputInterface::VERBOSITY_VERBOSE);
286285

287-
$createGitattributesFile = $input->getOption('create');
288-
$overwriteGitattributesFile = $input->getOption('overwrite');
289-
286+
// Print deprecation notices for legacy options but do NOT change exit code.
290287
if ($input->hasOption('create') && (bool) $input->getOption('create')) {
291288
$output->writeln('<comment>The --create option is deprecated. Please use the dedicated <info>create</info> command.</comment>');
292289
}
293290
if ($input->hasOption('overwrite') && (bool) $input->getOption('overwrite')) {
294291
$output->writeln('<comment>The --overwrite option is deprecated. Please use the dedicated <info>update</info> command.</comment>');
295292
}
296293

294+
// Apply shared generation-related options via the trait (glob/preset/keep/alignment/order)
295+
if (!$this->applyGenerationOptions($input, $output, $this->analyser)) {
296+
return Command::FAILURE;
297+
}
298+
299+
$createGitattributesFile = $input->getOption('create');
300+
$overwriteGitattributesFile = $input->getOption('overwrite');
297301
$validateArchive = $input->getOption('validate-git-archive');
298302
$globPattern = $input->getOption('glob-pattern');
299303
$globPatternFile = (string) $input->getOption('glob-pattern-file');

tests/Commands/CreateCommandTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Stolt\LeanPackage\Analyser;
99
use Stolt\LeanPackage\Commands\CreateCommand;
1010
use Stolt\LeanPackage\GitattributesFileRepository;
11+
use Stolt\LeanPackage\Helpers\Str as OsHelper;
1112
use Stolt\LeanPackage\Presets\Finder;
1213
use Stolt\LeanPackage\Presets\PhpPreset;
1314
use Stolt\LeanPackage\Tests\TestCase;
@@ -28,6 +29,10 @@ protected function tearDown(): void
2829
#[Test]
2930
public function createsNewGitattributesFileWithHeaderAndExpectedContent(): void
3031
{
32+
if ((new OsHelper())->isWindows()) {
33+
$this->markTestSkipped('Skipping test on Windows systems');
34+
}
35+
3136
$analyser = (new Analyser(new Finder(new PhpPreset())))->setDirectory($this->temporaryDirectory);
3237
$repository = new GitattributesFileRepository($analyser);
3338
$command = new CreateCommand($analyser, $repository);
@@ -65,6 +70,10 @@ public function createsNewGitattributesFileWithHeaderAndExpectedContent(): void
6570
#[Test]
6671
public function failsIfGitattributesAlreadyExists(): void
6772
{
73+
if ((new OsHelper())->isWindows()) {
74+
$this->markTestSkipped('Skipping test on Windows systems');
75+
}
76+
6877
$gitattributesContent = <<<CONTENT
6978
* text=auto eol=lf
7079

tests/Commands/UpdateCommandTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Stolt\LeanPackage\Analyser;
99
use Stolt\LeanPackage\Commands\UpdateCommand;
1010
use Stolt\LeanPackage\GitattributesFileRepository;
11+
use Stolt\LeanPackage\Helpers\Str as OsHelper;
1112
use Stolt\LeanPackage\Presets\Finder;
1213
use Stolt\LeanPackage\Presets\PhpPreset;
1314
use Stolt\LeanPackage\Tests\TestCase;
@@ -28,6 +29,10 @@ protected function tearDown(): void
2829
#[Test]
2930
public function updatesExistingGitattributesAndReplacesHeader(): void
3031
{
32+
if ((new OsHelper())->isWindows()) {
33+
$this->markTestSkipped('Skipping test on Windows systems');
34+
}
35+
3136
$analyser = (new Analyser(new Finder(new PhpPreset())))->setDirectory($this->temporaryDirectory);
3237
$repository = new GitattributesFileRepository($analyser);
3338
$command = new UpdateCommand($analyser, $repository);
@@ -78,6 +83,10 @@ public function updatesExistingGitattributesAndReplacesHeader(): void
7883
#[Test]
7984
public function failsWhenNoGitattributesFileIsPresent(): void
8085
{
86+
if ((new OsHelper())->isWindows()) {
87+
$this->markTestSkipped('Skipping test on Windows systems');
88+
}
89+
8190
// Remove the file
8291
@\unlink($this->temporaryDirectory . DIRECTORY_SEPARATOR . '.gitattributes');
8392

0 commit comments

Comments
 (0)