Skip to content

Commit 47b80f8

Browse files
committed
Add GitHub action to check related validators
This commit ensures that if validator A has a direct link to validator B, validator B will have a direct link to validator A too. We are already doing that, but this commit will add a GitHub action with a command that will fail if there are any changes required.
1 parent 8642c8b commit 47b80f8

5 files changed

Lines changed: 148 additions & 2 deletions

File tree

.github/workflows/continuous-integration-docs.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@ jobs:
3434

3535
- name: Validator headers
3636
run: bin/console docs:validators:headers
37+
38+
- name: Related validators
39+
run: bin/console docs:validators:related

bin/console

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require __DIR__ . '/../vendor/autoload.php';
1313
use Respect\Dev\Commands\CreateMixinCommand;
1414
use Respect\Dev\Commands\DocsValidatorsHeadersCommand;
1515
use Respect\Dev\Commands\DocsValidatorsListCommand;
16+
use Respect\Dev\Commands\DocsValidatorsRelatedCommand;
1617
use Respect\Dev\Commands\UpdateDomainSuffixesCommand;
1718
use Respect\Dev\Commands\UpdateDomainToplevelCommand;
1819
use Respect\Dev\Commands\UpdatePostalCodesCommand;
@@ -28,6 +29,7 @@ return (static function () {
2829
$application->addCommand(new CreateMixinCommand());
2930
$application->addCommand(new DocsValidatorsHeadersCommand($differ));
3031
$application->addCommand(new DocsValidatorsListCommand($differ));
32+
$application->addCommand(new DocsValidatorsRelatedCommand($differ));
3133
$application->addCommand(new UpdateDomainSuffixesCommand());
3234
$application->addCommand(new UpdateDomainToplevelCommand());
3335
$application->addCommand(new UpdatePostalCodesCommand());

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,16 @@
7474
"scripts": {
7575
"docs:validators:headers": "bin/console docs:validators:headers",
7676
"docs:validators:index": "bin/console docs:validators:index",
77+
"docs:validators:related": "bin/console docs:validators:related",
7778
"docheader": "vendor/bin/docheader check library/ tests/",
7879
"phpcs": "vendor/bin/phpcs",
7980
"phpstan": "vendor/bin/phpstan analyze",
8081
"phpunit": "vendor/bin/phpunit --testsuite=unit",
8182
"pest": "vendor/bin/pest --testsuite=feature --compact",
8283
"docs": [
8384
"@docs:validators:headers",
84-
"@docs:validators:index"
85+
"@docs:validators:index",
86+
"@docs:validators:related"
8587
],
8688
"qa": [
8789
"@docheader",

docs/validators/IterableVal.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ v::iterableVal()->isValid('string'); // false
1313

1414
## Note
1515

16-
This validator doesn't behave as PHP's [is_iterable() function because it considers that you can iterate over any object.
16+
This validator doesn't behave as PHP's [is_iterable()][] function because it considers that you can iterate over any object.
1717

1818
## Templates
1919

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<?php
2+
3+
/*
4+
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
5+
* SPDX-License-Identifier: MIT
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Respect\Dev\Commands;
11+
12+
use Respect\Dev\Markdown\Builder;
13+
use Respect\Dev\Markdown\Differ;
14+
use Symfony\Component\Console\Attribute\AsCommand;
15+
use Symfony\Component\Console\Command\Command;
16+
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Output\OutputInterface;
18+
use Symfony\Component\Finder\Finder;
19+
20+
use function array_keys;
21+
use function array_unique;
22+
use function count;
23+
use function dirname;
24+
use function file_get_contents;
25+
use function implode;
26+
use function in_array;
27+
use function preg_match_all;
28+
use function preg_split;
29+
use function sort;
30+
use function sprintf;
31+
use function str_contains;
32+
33+
use const PHP_EOL;
34+
35+
#[AsCommand(
36+
name: 'docs:validators:related',
37+
description: 'Update the list of related validators of each validator documentation file',
38+
)]
39+
final class DocsValidatorsRelatedCommand extends Command
40+
{
41+
public function __construct(
42+
private readonly Differ $differ,
43+
) {
44+
parent::__construct();
45+
}
46+
47+
protected function execute(InputInterface $input, OutputInterface $output): int
48+
{
49+
$diffs = [];
50+
foreach ($this->getAllRelatedValidators() as $validator => $relatedValidators) {
51+
$diff = $this->updateMarkdown($validator, $relatedValidators);
52+
if ($diff === null) {
53+
continue;
54+
}
55+
56+
$diffs[] = $diff;
57+
}
58+
59+
if ($diffs === []) {
60+
$output->writeln('<info>No changes needed.</info>');
61+
62+
return Command::SUCCESS;
63+
}
64+
65+
$output->writeln(sprintf('<comment>Changed needed in %d files.</comment>', count($diffs)));
66+
$output->writeln(implode(PHP_EOL, $diffs));
67+
68+
return Command::FAILURE;
69+
}
70+
71+
/** @param array<string, array<string>> $relatedValidators */
72+
private function updateMarkdown(string $validator, array $relatedValidators): string|null
73+
{
74+
$builder = Builder::fromDocs(sprintf('validators/%s.md', $validator));
75+
76+
// Extract links at the bottom
77+
preg_match_all('/^\[.+\]: .+$/m', $builder->existingContent, $linkMatches);
78+
$references = implode(PHP_EOL, $linkMatches[0] ?? []);
79+
80+
// Get content before "See also" or "---"
81+
$sections = preg_split('/(^## See also:|^---)/m', $builder->existingContent);
82+
83+
$builder->raw($sections[0] ?? $builder->existingContent);
84+
$builder->hr();
85+
$builder->paragraph('See also:');
86+
$builder->newLine();
87+
foreach ($relatedValidators as $relatedValidator) {
88+
$builder->anchorListItem($relatedValidator, $relatedValidator . '.md');
89+
}
90+
91+
$builder->newLine();
92+
$builder->raw($references);
93+
94+
$diff = $this->differ->diff($builder);
95+
if ($diff !== null) {
96+
$builder->save();
97+
}
98+
99+
return $diff;
100+
}
101+
102+
/** @return array<string, array<string>> */
103+
private function getAllRelatedValidators(): array
104+
{
105+
$docsDirectory = dirname(__DIR__, 2) . '/docs';
106+
107+
$finder = new Finder();
108+
$finder->in(dirname(__DIR__, 2) . '/library/Validators')->depth(0)->files()->name('*.php')->sortByName();
109+
110+
$allRelatedValidators = [];
111+
foreach ($finder as $file) {
112+
$validator = $file->getBasename('.php');
113+
$filename = sprintf('%s/validators/%s.md', $docsDirectory, $validator);
114+
$content = file_get_contents($filename);
115+
if ($content === false) {
116+
continue;
117+
}
118+
119+
$relatedValidators = [];
120+
121+
preg_match_all('/\[([^\]]+)\]\(([^\)]+\.md)\)/', $content, $matches);
122+
foreach (array_keys($matches[0]) as $key) {
123+
$related = $matches[1][$key];
124+
$document = $matches[2][$key];
125+
if (str_contains($document, '/') || in_array($related, $relatedValidators)) {
126+
continue;
127+
}
128+
129+
$relatedValidators[] = $related;
130+
}
131+
132+
$allRelatedValidators[$validator] = array_unique($relatedValidators);
133+
134+
sort($allRelatedValidators[$validator]);
135+
}
136+
137+
return $allRelatedValidators;
138+
}
139+
}

0 commit comments

Comments
 (0)