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

Skip to content

Commit e5d4ec3

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

File tree

4 files changed

+70
-7
lines changed

4 files changed

+70
-7
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: 23 additions & 5 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();
@@ -50,7 +56,7 @@ protected function configure()
5056
{
5157
$this
5258
->setDescription(self::$defaultDescription)
53-
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
59+
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format')
5460
->addOption('show-deprecations', null, InputOption::VALUE_NONE, 'Show deprecations as errors')
5561
->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')
5662
->setHelp(<<<'EOF'
@@ -80,6 +86,11 @@ 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 (null === $this->format) {
92+
$this->format = class_exists(GithubActionReporter::class) && GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt';
93+
}
8394

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

170181
private function display(InputInterface $input, OutputInterface $output, SymfonyStyle $io, array $files)
171182
{
172-
switch ($input->getOption('format')) {
183+
switch ($this->format) {
173184
case 'txt':
174185
return $this->displayTxt($output, $io, $files);
175186
case 'json':
176187
return $this->displayJson($output, $files);
188+
case 'github':
189+
return $this->displayTxt($output, $io, $files, true);
177190
default:
178191
throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format')));
179192
}
180193
}
181194

182-
private function displayTxt(OutputInterface $output, SymfonyStyle $io, array $filesInfo)
195+
private function displayTxt(OutputInterface $output, SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = false)
183196
{
184197
$errors = 0;
198+
$githubReporter = $errorAsGithubAnnotations ? new GithubActionReporter($output) : null;
185199

186200
foreach ($filesInfo as $info) {
187201
if ($info['valid'] && $output->isVerbose()) {
188202
$io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
189203
} elseif (!$info['valid']) {
190204
++$errors;
191-
$this->renderException($io, $info['template'], $info['exception'], $info['file']);
205+
$this->renderException($io, $info['template'], $info['exception'], $info['file'], $githubReporter);
192206
}
193207
}
194208

@@ -220,10 +234,14 @@ private function displayJson(OutputInterface $output, array $filesInfo)
220234
return min($errors, 1);
221235
}
222236

223-
private function renderException(SymfonyStyle $output, string $template, Error $exception, string $file = null)
237+
private function renderException(SymfonyStyle $output, string $template, Error $exception, string $file = null, ?GithubActionReporter $githubReporter = null)
224238
{
225239
$line = $exception->getTemplateLine();
226240

241+
if ($githubReporter) {
242+
$githubReporter->error($exception->getRawMessage(), $file, $line <= 0 ? null : $line);
243+
}
244+
227245
if ($file) {
228246
$output->text(sprintf('<error> ERROR </error> in %s (line %s)', $file, $line));
229247
} else {

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

Lines changed: 39 additions & 0 deletions
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,44 @@ 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+
$prev = getenv('GITHUB_ACTIONS');
134+
putenv('GITHUB_ACTIONS');
135+
136+
try {
137+
putenv('GITHUB_ACTIONS=1');
138+
139+
$filename = $this->createFile('{{ foo');
140+
$tester = $this->createCommandTester();
141+
142+
$tester->execute(['filename' => [$filename]], ['decorated' => false]);
143+
self::assertStringMatchesFormat('%A::error file=%s,line=1,col=0::Unexpected token "end of template" ("end of print statement" expected).%A', trim($tester->getDisplay()));
144+
} finally {
145+
putenv('GITHUB_ACTIONS'.($prev ? "=$prev" : ''));
146+
}
147+
}
148+
110149
private function createCommandTester(): CommandTester
111150
{
112151
$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