Skip to content

Commit 89edb6b

Browse files
authored
Adding an issue manager (#4)
* Adding an issue manager * CS * Fixed tests
1 parent ccc0266 commit 89edb6b

File tree

5 files changed

+117
-35
lines changed

5 files changed

+117
-35
lines changed

src/Command/CheckDocsCommand.php

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
use Doctrine\RST\Builder\Documents;
88
use Doctrine\RST\Builder\ParseQueue;
99
use Doctrine\RST\Builder\ParseQueueProcessor;
10-
use Doctrine\RST\ErrorManager;
1110
use Doctrine\RST\Event\PostNodeCreateEvent;
1211
use Doctrine\RST\Meta\Metas;
12+
use Symfony\CodeBlockChecker\Issue\IssueManger;
1313
use Symfony\CodeBlockChecker\Listener\ValidCodeNodeListener;
1414
use Symfony\Component\Console\Command\Command;
1515
use Symfony\Component\Console\Input\InputArgument;
1616
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Input\InputOption;
1718
use Symfony\Component\Console\Output\OutputInterface;
1819
use Symfony\Component\Console\Style\SymfonyStyle;
1920
use Symfony\Component\Filesystem\Filesystem;
@@ -24,7 +25,7 @@ class CheckDocsCommand extends Command
2425
protected static $defaultName = 'verify:docs';
2526

2627
private SymfonyStyle $io;
27-
private ErrorManager $errorManager;
28+
private IssueManger $errorManager;
2829
private ParseQueueProcessor $queueProcessor;
2930

3031
public function __construct()
@@ -37,6 +38,7 @@ protected function configure()
3738
$this
3839
->addArgument('source-dir', InputArgument::REQUIRED, 'RST files Source directory')
3940
->addArgument('files', InputArgument::IS_ARRAY + InputArgument::REQUIRED, 'RST files that should be verified.')
41+
->addOption('output-format', null, InputOption::VALUE_OPTIONAL, 'Valid options are github and console', 'github')
4042
->setDescription('Make sure the docs blocks are valid')
4143

4244
;
@@ -56,7 +58,7 @@ protected function initialize(InputInterface $input, OutputInterface $output)
5658
$kernel = \SymfonyDocsBuilder\KernelFactory::createKernel($buildConfig);
5759
$configuration = $kernel->getConfiguration();
5860
$configuration->silentOnError(true);
59-
$this->errorManager = new ErrorManager($configuration);
61+
$this->errorManager = new IssueManger($configuration);
6062
$eventManager = $configuration->getEventManager();
6163
$eventManager->addEventListener(PostNodeCreateEvent::POST_NODE_CREATE, new ValidCodeNodeListener($this->errorManager));
6264

@@ -80,9 +82,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int
8082

8183
$this->queueProcessor->process($parseQueue);
8284

83-
$errorCount = count($this->errorManager->getErrors());
84-
if ($errorCount > 0) {
85-
$this->io->error(sprintf('Build completed with %s errors', $errorCount));
85+
$issues = $this->errorManager->getIssues();
86+
$issueCount = count($issues);
87+
if ($issueCount > 0) {
88+
$format = $input->getOption('output-format');
89+
if ('console' === $format) {
90+
foreach ($issues as $issue) {
91+
$this->io->writeln($issue->__toString());
92+
}
93+
} elseif ('github' === $format) {
94+
foreach ($issues as $issue) {
95+
$this->io->writeln(sprintf('::error file=%s,line=%s::[%s] %s', $issue->getFile(), $issue->getLine(), $issue->getType(), $issue->getText()));
96+
}
97+
}
98+
99+
$this->io->error(sprintf('Build completed with %s errors', $issueCount));
86100

87101
return Command::FAILURE;
88102
}

src/Issue/Issue.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace Symfony\CodeBlockChecker\Issue;
4+
5+
/**
6+
* Represent an error with some code.
7+
*
8+
* @author Tobias Nyholm <[email protected]>
9+
*/
10+
class Issue implements \Stringable
11+
{
12+
private string $text;
13+
private string $type;
14+
private string $file;
15+
private int $line;
16+
17+
public function __construct(string $text, string $type, string $file, int $line)
18+
{
19+
$this->text = $text;
20+
$this->type = $type;
21+
$this->file = $file;
22+
$this->line = $line;
23+
}
24+
25+
public function getText(): string
26+
{
27+
return $this->text;
28+
}
29+
30+
public function getType(): string
31+
{
32+
return $this->type;
33+
}
34+
35+
public function getFile(): string
36+
{
37+
return $this->file;
38+
}
39+
40+
public function getLine(): int
41+
{
42+
return $this->line;
43+
}
44+
45+
public function __toString()
46+
{
47+
return sprintf('[%s] %s in file %s at %d', $this->getType(), $this->getText(), $this->getFile(), $this->getLine());
48+
}
49+
}

src/Issue/IssueManger.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Symfony\CodeBlockChecker\Issue;
4+
5+
use Doctrine\RST\ErrorManager;
6+
7+
class IssueManger extends ErrorManager
8+
{
9+
/**
10+
* @var list<Issue>
11+
*/
12+
private array $issues = [];
13+
14+
public function addIssue(Issue $issue)
15+
{
16+
$this->issues[] = $issue;
17+
parent::error($issue->__toString());
18+
}
19+
20+
/**
21+
* @return list<Issue>
22+
*/
23+
public function getIssues(): array
24+
{
25+
return $this->issues;
26+
}
27+
}

src/Listener/ValidCodeNodeListener.php

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
namespace Symfony\CodeBlockChecker\Listener;
66

7-
use Doctrine\RST\ErrorManager;
87
use Doctrine\RST\Event\PostNodeCreateEvent;
98
use Doctrine\RST\Nodes\CodeNode;
9+
use Symfony\CodeBlockChecker\Issue\Issue;
10+
use Symfony\CodeBlockChecker\Issue\IssueManger;
1011
use Symfony\CodeBlockChecker\Twig\DummyExtension;
1112
use Symfony\Component\Process\Process;
1213
use Symfony\Component\Yaml\Exception\ParseException;
@@ -26,7 +27,7 @@ class ValidCodeNodeListener
2627
private $errorManager;
2728
private $twig;
2829

29-
public function __construct(ErrorManager $errorManager)
30+
public function __construct(IssueManger $errorManager)
3031
{
3132
$this->errorManager = $errorManager;
3233
}
@@ -70,11 +71,13 @@ private function validatePhp(CodeNode $node)
7071
return;
7172
}
7273

73-
$this->errorManager->error(sprintf(
74-
'Invalid PHP syntax in "%s": %s',
75-
$node->getEnvironment()->getCurrentFileName(),
76-
str_replace($file, 'example.php', $process->getErrorOutput())
77-
));
74+
$line = 0;
75+
$text = str_replace($file, 'example.php', $process->getErrorOutput());
76+
if (preg_match('| in example.php on line ([0-9]+)|s', $text, $matches)) {
77+
$text = str_replace($matches[0], '', $text);
78+
$line = (int) $matches[1];
79+
}
80+
$this->errorManager->addIssue(new Issue($text, 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), $line));
7881
}
7982

8083
private function validateXml(CodeNode $node)
@@ -97,11 +100,8 @@ private function validateXml(CodeNode $node)
97100
if ('SimpleXMLElement::__construct(): namespace error : Namespace prefix' === substr($e->getMessage(), 0, 67)) {
98101
return;
99102
}
100-
$this->errorManager->error(sprintf(
101-
'Invalid Xml in "%s": %s',
102-
$node->getEnvironment()->getCurrentFileName(),
103-
$e->getMessage()
104-
));
103+
104+
$this->errorManager->addIssue(new Issue($e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
105105
}
106106
}
107107

@@ -116,11 +116,7 @@ private function validateYaml(CodeNode $node)
116116
return;
117117
}
118118

119-
$this->errorManager->error(sprintf(
120-
'Invalid Yaml in "%s": %s',
121-
$node->getEnvironment()->getCurrentFileName(),
122-
$e->getMessage()
123-
));
119+
$this->errorManager->addIssue(new Issue($e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
124120
}
125121
}
126122

@@ -136,21 +132,16 @@ private function validateTwig(CodeNode $node)
136132
// We cannot parse the TokenStream because we dont have all extensions loaded.
137133
$this->twig->parse($tokens);
138134
} catch (SyntaxError $e) {
139-
$this->errorManager->error(sprintf(
140-
'Invalid Twig syntax: %s',
141-
$e->getMessage()
142-
));
135+
$this->errorManager->addIssue(new Issue($e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
143136
}
144137
}
145138

146139
private function validateJson(CodeNode $node)
147140
{
148-
$data = json_decode($node->getValue(), true);
149-
if (null === $data) {
150-
$this->errorManager->error(sprintf(
151-
'Invalid Json in "%s"',
152-
$node->getEnvironment()->getCurrentFileName()
153-
));
141+
try {
142+
$data = json_decode($node->getValue(), true, 512, JSON_THROW_ON_ERROR);
143+
} catch (\JsonException $e) {
144+
$this->errorManager->addIssue(new Issue($e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
154145
}
155146
}
156147
}

tests/Listener/ValidCodeNodeListenerTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Doctrine\RST\Event\PostNodeCreateEvent;
99
use Doctrine\RST\Nodes\CodeNode;
1010
use PHPUnit\Framework\TestCase;
11+
use Symfony\CodeBlockChecker\Issue\IssueManger;
1112
use Symfony\CodeBlockChecker\Listener\ValidCodeNodeListener;
1213

1314
class ValidCodeNodeListenerTest extends TestCase
@@ -22,7 +23,7 @@ protected function setUp(): void
2223
$config->silentOnError();
2324
$config->abortOnError(false);
2425

25-
$this->errorManager = new ErrorManager($config);
26+
$this->errorManager = new IssueManger($config);
2627
$this->listener = new ValidCodeNodeListener($this->errorManager);
2728
$this->environment = new Environment($config);
2829
}
@@ -37,7 +38,7 @@ public function testInvalidYaml()
3738
$errors = $this->errorManager->getErrors();
3839
$this->assertCount(1, $errors);
3940

40-
$this->assertStringContainsString('Invalid Yaml', $errors[0]);
41+
$this->assertStringContainsString('Malformed inline YAML', $errors[0]);
4142
}
4243

4344
public function testParseTwig()

0 commit comments

Comments
 (0)