8000 [TwigBridge] LintCommand supports Github Actions annotations · symfony/symfony@78f4dff · GitHub
[go: up one dir, main page]

Skip to content

Commit 78f4dff

Browse files
committed
[TwigBridge] LintCommand supports Github Actions annotations
1 parent e574a24 commit 78f4dff

File tree

4 files changed

+77
-6
lines changed

4 files changed

+77
-6
lines changed

src/Symfony/Bridge/Twig/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
CHANGELOG
22
=========
33

4+
5.4
5+
---
6+
7+
* Add `github` format & autodetection to render errors as annotations when
8+
running the Twig linter command in a Github Actions environment.
9+
410
5.3
511
---
612

src/Symfony/Bridge/Twig/Command/LintCommand.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bridge\Twig\Command;
1313

14+
use Symfony\Component\Console\CI\GithubActionReporter;
1415
use Symfony\Component\Console\Command\Command;
1516
use Symfony\Component\Console\Exception\InvalidArgumentException;
1617
use Symfony\Component\Console\Exception\RuntimeException;
@@ -39,6 +40,11 @@ class LintCommand extends Command
3940

4041
private $twig;
4142

43+
/**
44+
* @var string|null
45+
*/
46+
private $format;
47+
4248
public function __construct(Environment $twig)
4349
{
4450
parent::__construct();
@@ -80,6 +86,15 @@ protected function execute(InputInterface $input, OutputInterface $output)
8086
$io = new SymfonyStyle($input, $output);
8187
$filenames = $input->getArgument('filename');
8288
$showDeprecations = $input->getOption('show-deprecations');
89+
$this->format = $input->getOption('format');
90+
91+
if ('github' === $this->format && !class_exists(GithubActionReporter::class)) {
92+
throw new \InvalidArgumentException('The "github" format is only available since "symfony/console" >= 5.3.');
93+
}
94+
95+
if (null === $this->format) {
96+
$this->format = class_exists(GithubActionReporter::class) && GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt';
97+
}
8398

8499
if (['-'] === $filenames) {
85100
return $this->display($input, $output, $io, [$this->validate(file_get_contents('php://stdin'), uniqid('sf_', true))]);
@@ -169,26 +184,29 @@ private function validate(string $template, string $file): array
169184

170185
private function display(InputInterface $input, OutputInterface $output, SymfonyStyle $io, array $files)
171186
{
172-
switch ($input->getOption('format')) {
187+
switch ($this->format) {
173188
case 'txt':
174189
return $this->displayTxt($output, $io, $files);
175190
case 'json':
176191
return $this->displayJson($output, $files);
192+
case 'github':
193+
return $this->displayTxt($output, $io, $files, true);
177194
default:
178195
throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format')));
179196
}
180197
}
181198

182-
private function displayTxt(OutputInterface $output, SymfonyStyle $io, array $filesInfo)
199+
private function displayTxt(OutputInterface $output, SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = false)
183200
{
184201
$errors = 0;
202+
$githubReporter = $errorAsGithubAnnotations ? new GithubActionReporter($output) : null;
185203

186204
foreach ($filesInfo as $info) {
187205
if ($info['valid'] && $output->isVerbose()) {
188206
$io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
189207
} elseif (!$info['valid']) {
190208
++$errors;
191-
$this->renderException($io, $info['template'], $info['exception'], $info['file']);
209+
$this->renderException($io, $info['template'], $info['exception'], $info['file'], $githubReporter);
192210
}
193211
}
194212

@@ -220,10 +238,14 @@ private function displayJson(OutputInterface $output, array $filesInfo)
220238
return min($errors, 1);
221239
}
222240

223-
private function renderException(SymfonyStyle $output, string $template, Error $exception, string $file = null)
241+
private function renderException(SymfonyStyle $output, string $template, Error $exception, string $file = null, ?GithubActionReporter $githubReporter = null)
224242
{
225243
$line = $exception->getTemplateLine();
226244

245+
if ($githubReporter) {
246+
$githubReporter->error($exception->getRawMessage(), $file, $line <= 0 ? null : $line);
247+
}
248+
227249
if ($file) {
228250
$output->text(sprintf('<error> ERROR </error> in %s (line %s)', $file, $line));
229251
} else {

src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php

Lines changed: 43 additions & 0 deletions
B4D9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Bridge\Twig\Command\LintCommand;
1616
use Symfony\Component\Console\Application;
17+
use Symfony\Component\Console\CI\GithubActionReporter;
1718
use Symfony\Component\Console\Output\OutputInterface;
1819
use Symfony\Component\Console\Tester\CommandTester;
1920
use Twig\Environment;
@@ -107,6 +108,48 @@ public function testLintDefaultPaths()
107108
self::assertStringContainsString('OK in', trim($tester->getDisplay()));
108109
}
109110

111+
public function testLintIncorrectFileWithGithubFormat()
112+
{
113+
if (!class_exists(GithubActionReporter::class)) {
114+
$this->expectException(\InvalidArgumentException::class);
115+
$this->expectExceptionMessage('The "github" format is only available since "symfony/console" >= 5.3');
116+
}
117+
118+
$filename = $this->createFile('{{ foo');
119+
$tester = $this->createCommandTester();
120+
121+
$tester->execute(['filename' => [$filename], '--format' => 'github'], ['decorated' => false]);
122+
123+
if (!class_exists(GithubActionReporter::class)) {
124+
return;
125+
}
126+
127+
self::assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
128+
self::assertStringMatchesFormat('%A::error file=%s, line=1, col=0::Unexpected token "end of template" ("end of print statement" expected).%A', trim($tester->getDisplay()));
129+
}
130+
131+
public function testLintAutodetectsGithubActionEnvironment()
132+
{
133+
// if (!class_exists(GithubActionReporter::class)) {
134+
// $this->markTestSkipped('The "github" format is only available since "symfony/console" >= 5.3.');
135+
// }
136+
137+
$prev = getenv('GITHUB_ACTIONS');
138+
putenv('GITHUB_ACTIONS');
139+
140+
try {
141+
putenv('GITHUB_ACTIONS=1');
142+
143+
$filename = $this->createFile('{{ foo');
144+
$tester = $this->createCommandTester();
145+
146+
$tester->execute(['filename' => [$filename]], ['decorated' => false]);
147+
self::assertStringMatchesFormat('%A::error file=%s, line=1, col=0::Unexpected token "end of template" ("end of print statement" expected).%A', trim($tester->getDisplay()));
148+
} finally {
149+
putenv('GITHUB_ACTIONS'.($prev ? "=$prev" : ''));
150+
}
151+
}
152+
110153
private function createCommandTester(): CommandTester
111154
{
112155
$environment = new Environment(new FilesystemLoader(\dirname(__DIR__).'/Fixtures/templates/'));

src/Symfony/Bridge/Twig/composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"symfony/security-http": "^4.4|^5.0|^6.0",
4545
"symfony/serializer": "^5.2|^6.0",
4646
"symfony/stopwatch": "^4.4|^5.0|^6.0",
47-
"symfony/console": "^4.4|^5.0|^6.0",
47+
"symfony/console": "^5.3|^6.0",
4848
"symfony/expression-language": "^4.4|^5.0|^6.0",
4949
"symfony/web-link": "^4.4|^5.0|^6.0",
5050
"symfony/workflow": "^5.2|^6.0",
@@ -55,7 +55,7 @@
5555
"conflict": {
5656
"phpdocumentor/reflection-docblock": "<3.2.2",
5757
"phpdocumentor/type-resolver": "<1.4.0",
58-
"symfony/console": "<4.4",
58+
"symfony/console": "<5.3",
5959
"symfony/form": "<5.3",
6060
"symfony/http-foundation": "<5.3",
6161
"symfony/http-kernel": "<4.4",

0 commit comments

Comments
 (0)
0