Skip to content

Commit 4ac87bd

Browse files
committed
Add github workflow to verify docs
1 parent 074528d commit 4ac87bd

File tree

5 files changed

+248
-1
lines changed

5 files changed

+248
-1
lines changed

.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ APP_SECRET=5dd8ffca252d95e8b4fb5b2d15310e92
2121

2222
SYMFONY_DOCS_SECRET=''
2323
SYMFONY_SECRET=''
24-
BOT_USERNAME='carsonbot-test'
24+
BOT_USERNAME='carsonbot'
2525
###> knplabs/github-api ###
2626
#GITHUB_TOKEN=XXX
2727
###< knplabs/github-api ###
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Verify docs
2+
3+
on:
4+
schedule:
5+
- cron: '58 6 * * *'
6+
7+
jobs:
8+
use:
9+
name: Invalid use statements
10+
runs-on: ubuntu-latest
11+
strategy:
12+
fail-fast: false
13+
14+
steps:
15+
- name: Set up PHP
16+
uses: shivammathur/setup-php@2.7.0
17+
with:
18+
php-version: 7.4
19+
coverage: none
20+
21+
- name: Checkout code
22+
uses: actions/checkout@v2
23+
24+
- name: Checkout Symfony repo
25+
run: git clone https://github.com/symfony/symfony .github/workflows/docs-invalid-use/symfony
26+
27+
- name: Checkout Symfony Docs repo
28+
run: git clone https://github.com/symfony/symfony-docs .github/workflows/docs-invalid-use/docs
29+
30+
- name: Get composer cache directory
31+
id: composer-cache
32+
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
33+
34+
- name: Cache dependencies
35+
uses: actions/cache@v2
36+
with:
37+
path: ${{ steps.composer-cache.outputs.dir }}
38+
key: composer-${{ runner.os }}-7.4-${{ hashFiles('composer.*') }}
39+
restore-keys: |
40+
composer-${{ runner.os }}-7.4-
41+
composer-${{ runner.os }}-
42+
composer-
43+
44+
- name: Download dependencies
45+
run: |
46+
composer install --no-interaction --optimize-autoloader
47+
cd .github/workflows/docs-invalid-use
48+
composer update --prefer-stable --no-interaction --optimize-autoloader
49+
cd symfony
50+
composer update --prefer-stable --no-interaction --optimize-autoloader
51+
52+
- name: Verify docs
53+
run: |
54+
cd .github/workflows/docs-invalid-use
55+
./run.php `pwd`/docs `pwd`/symfony > output.txt
56+
cat output.txt
57+
58+
- name: Open issue
59+
env:
60+
GITHUB_TOKEN: ${{ secrets.CARSONPROD_GITHUB_TOKEN }}
61+
run: |
62+
if [ ! -s .github/workflows/docs-invalid-use/output.txt ]; then
63+
echo "No issues to report"
64+
exit 0
65+
fi
66+
67+
echo -e "I've found some pages that have invalid class references. This is a list of use statements that I couldn't find the correct class to: \n\n\`\`\`" > issue.txt
68+
cat .github/workflows/docs-invalid-use/output.txt >> issue.txt
69+
echo -e "\n\`\`\`\n\nCould someone please verify these?" >> issue.txt
70+
bin/console app:issue:open symfony/symfony-docs "Invalid use statements" `pwd`/issue.txt
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "tobias/docs-checker",
3+
"type": "project",
4+
"authors": [
5+
{
6+
"name": "Nyholm",
7+
"email": "tobias.nyholm@gmail.com"
8+
}
9+
],
10+
"require": {
11+
"symfony/filesystem": "^5.1",
12+
"symfony/finder": "^5.1",
13+
"symfony/phpunit-bridge": "^5.1",
14+
"phpunit/phpunit": "^9.4",
15+
"defuse/php-encryption": "^2.2",
16+
"sensiolabs/ansi-to-html": "^1.2",
17+
"twig/twig": "^3.0",
18+
"symfony/mercure": "^0.4.0",
19+
"lcobucci/jwt": "^3.3",
20+
"symfony/psr-http-message-bridge": "^2.0",
21+
"ramsey/uuid": "^4.1",
22+
"twig/cssinliner-extra": "^3.0"
23+
}
24+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
if ($argc !== 3) {
5+
echo "./docs-invalid-use.php path-to-docs path-to-symfony\n";
6+
exit(1);
7+
}
8+
9+
// Input
10+
$docs = $argv[1];
11+
$symfony = $argv[2];
12+
13+
$autoload = [
14+
__DIR__.'/vendor/autoload.php',
15+
$symfony.'/vendor/autoload.php',
16+
];
17+
18+
foreach ($autoload as $autoloadPHP) {
19+
if (!file_exists($autoloadPHP)) {
20+
echo "File $autoloadPHP does not exist.\nMake sure to run 'composer update'\n";
21+
exit(1);
22+
}
23+
require $autoloadPHP;
24+
}
25+
26+
use Symfony\Component\Finder\Finder;
27+
$finder = new Finder();
28+
$finder->in($docs)->name('*.rst');
29+
30+
$blocklist = getBlocklist();
31+
$count = 0;
32+
foreach ($finder as $file) {
33+
$contents = $file->getContents();
34+
$matches = [];
35+
if (preg_match_all('|^ +use (.*\\\.*); *?$|im', $contents, $matches)) {
36+
foreach ($matches[1] as $class) {
37+
if (substr($class, 0, 3) === 'App' || substr($class, 0, 4) === 'Acme') {
38+
continue;
39+
}
40+
41+
if (false !== $pos = strpos($class, ' as ')) {
42+
$class = substr($class, 0, $pos);
43+
}
44+
45+
if (false !== $pos = strpos($class, 'function ')) {
46+
continue;
47+
}
48+
49+
if (in_array($class, $blocklist)) {
50+
continue;
51+
}
52+
53+
$explode = explode('\\', $class);
54+
if (count($explode) === 3 && $explode[0] === 'Symfony' && $explode[1] === 'Component') {
55+
continue;
56+
}
57+
58+
if (!class_exists($class) && !interface_exists($class) && !trait_exists($class)) {
59+
$count++;
60+
echo $file->getRelativePath().'/'.$file->getFilename(). ' - '.$class. PHP_EOL;
61+
}
62+
}
63+
}
64+
}
65+
66+
if ($count === 0) {
67+
error_log("We found nothing\n");
68+
}
69+
70+
exit(0);
71+
72+
function getBlocklist(): array
73+
{
74+
return [
75+
'Doctrine\ORM\Mapping',
76+
'Symfony\Component\Validator\Constraints',
77+
'Simplex\StringResponseListener',
78+
'Calendar\Model\LeapYear',
79+
'Symfony\Component\Security\Core\Validator\Constraints',
80+
'Simplex\Framework',
81+
'Calendar\Controller\LeapYearController',
82+
'ApiPlatform\Core\Annotation\ApiResource',
83+
'Other\Qux',
84+
'Doctrine\Bundle\FixturesBundle\Fixture',
85+
'Vendor\DependencyClass',
86+
'Example\Namespace\YourAwesomeCoolClass',
87+
'Your\Transport\YourTransportFactory',
88+
];
89+
}

src/Command/OpenIssueCommand.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Command;
6+
7+
use App\Api\Issue\IssueApi;
8+
use App\Service\RepositoryProvider;
9+
use Symfony\Component\Console\Command\Command;
10+
use Symfony\Component\Console\Input\InputArgument;
11+
use Symfony\Component\Console\Input\InputInterface;
12+
use Symfony\Component\Console\Output\OutputInterface;
13+
14+
/**
15+
* Open or update issues.
16+
*
17+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
18+
*/
19+
class OpenIssueCommand extends Command
20+
{
21+
protected static $defaultName = 'app:issue:open';
22+
private $issueApi;
23+
private $repositoryProvider;
24+
25+
public function __construct(RepositoryProvider $repositoryProvider, IssueApi $issueApi)
26+
{
27+
parent::__construct();
28+
$this->issueApi = $issueApi;
29+
$this->repositoryProvider = $repositoryProvider;
30+
}
31+
32+
protected function configure()
33+
{
34+
$this->addArgument('repository', InputArgument::REQUIRED, 'The full name to the repository, eg symfony/symfony-docs');
35+
$this->addArgument('title', InputArgument::REQUIRED, 'The title of the issue');
36+
$this->addArgument('file', InputArgument::REQUIRED, 'The path to the issue body text file');
37+
}
38+
39+
protected function execute(InputInterface $input, OutputInterface $output)
40+
{
41+
/** @var string $repositoryName */
42+
$repositoryName = $input->getArgument('repository');
43+
$repository = $this->repositoryProvider->getRepository($repositoryName);
44+
if (null === $repository) {
45+
$output->writeln('Repository not configured');
46+
47+
return 1;
48+
}
49+
50+
/** @var string $title */
51+
$title = $input->getArgument('title');
52+
/** @var string $filePath */
53+
$filePath = $input->getArgument('file');
54+
55+
$body = file_get_contents($filePath);
56+
if (false === $body) {
57+
return 1;
58+
}
59+
60+
$this->issueApi->open($repository, $title, $body, ['help wanted']);
61+
62+
return 0;
63+
}
64+
}

0 commit comments

Comments
 (0)