8000 Add Yaml LintCommand · symfony/symfony@b15d23d · GitHub
[go: up one dir, main page]

Skip to content

Commit b15d23d

Browse files
committed
Add Yaml LintCommand
Add tests Fix tests & YamlLintCommand help formatting fabbot fixes
1 parent 1298ce5 commit b15d23d

File tree

5 files changed

+455
-127
lines changed

5 files changed

+455
-127
lines changed

src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php

Lines changed: 18 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -11,155 +11,46 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Command;
1313

14-
use Symfony\Component\Console\Command\Command;
15-
use Symfony\Component\Console\Input\InputInterface;
16-
use Symfony\Component\Console\Input\InputOption;
17-
use Symfony\Component\Console\Output\OutputInterface;
18-
use Symfony\Component\Console\Style\SymfonyStyle;
1914
use Symfony\Component\Finder\Finder;
20-
use Symfony\Component\Yaml\Exception\ParseException;
21-
use Symfony\Component\Yaml\Parser;
15+
use Symfony\Component\Yaml\Command\LintCommand as BaseLintCommand;
2216

2317
/**
2418
* Validates YAML files syntax and outputs encountered errors.
2519
*
2620
* @author Grégoire Pineau <lyrixx@lyrixx.info>
21+
* @author Robin Chalas <robin.chalas@gmail.com>
2722
*/
28-
class YamlLintCommand extends Command
23+
class YamlLintCommand extends BaseLintCommand
2924
{
25+
/**
26+
* {@inheritdoc}
27+
*/
3028
protected function configure()
3129
{
32-
$this
33-
->setName('lint:yaml')
34-
->setDescription('Lints a file and outputs encountered errors')
35-
->addArgument('filename', null, 'A file or a directory or STDIN')
36-
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
37-
->setHelp(<<<EOF
38-
The <info>%command.name%</info> command lints a YAML file and outputs to STDOUT
39-
the first encountered syntax error.
30+
parent::configure();
4031

41-
You can validate the syntax of a file:
32+
$this->setHelp(
33+
$this->getHelp().<<<EOF
4234
43-
<info>php %command.full_name% filename</info>
35+
Or find all files in a bundle:
4436
45-
Or of a whole directory:
46-
47-
<info>php %command.full_name% dirname</info>
48-
<info>php %command.full_name% dirname --format=json</info>
49-
50-
Or all YAML files in a bundle:
51-
52-
<info>php %command.full_name% @AcmeDemoBundle</info>
53-
54-
You can also pass the YAML contents from STDIN:
55-
56-
<info>cat filename | php %command.full_name%</info>
37+
<info>php %command.full_name% @AcmeDemoBundle</info>
5738
5839
EOF
59-
)
60-
;
61-
}
62-
63-
protected function execute(InputInterface $input, OutputInterface $output)
64-
{
65-
$io = new SymfonyStyle($input, $output);
66-
$filename = $input->getArgument('filename');
67-
68-
if (!$filename) {
69-
if (0 !== ftell(STDIN)) {
70-
throw new \RuntimeException('Please provide a filename or pipe file content to STDIN.');
71-
}
72-
73-
$content = '';
74-
while (!feof(STDIN)) {
75-
$content .= fread(STDIN, 1024);
76-
}
77-
78-
return $this->display($input, $output, $io, array($this->validate($content)));
79-
}
80-
81-
if (0 !== strpos($filename, '@') && !is_readable($filename)) {
82-
throw new \RuntimeException(sprintf('File or directory "%s" is not readable', $filename));
83-
}
84-
85-
$files = array();
86-
if (is_file($filename)) {
87-
$files = array($filename);
88-
} elseif (is_dir($filename)) {
89-
$files = Finder::create()->files()->in($filename)->name('*.yml');
90-
} else {
91-
$dir = $this->getApplication()->getKernel()->locateResource($filename);
92-
$files = Finder::create()->files()->in($dir)->name('*.yml');
93-
}
94-
95-
$filesInfo = array();
96-
foreach ($files as $file) {
97-
$filesInfo[] = $this->validate(file_get_contents($file), $file);
98-
}
99-
100-
return $this->display($input, $output, $io, $filesInfo);
40+
);
10141
}
10242

103-
private function validate($content, $file = null)
43+
protected function findFilesInDirectory($directory)
10444
{
105-
$parser = new Parser();
106-
try {
107-
$parser->parse($content);
108-
} catch (ParseException $e) {
109-
return array('file' => $file, 'valid' => false, 'message' => $e->getMessage());
45+
if (!is_dir($directory)) {
46+
$directory = $this->getApplication()->getKernel()->locateResource($directory);
11047
}
11148

112-
return array('file' => $file, 'valid' => true);
49+
return Finder::create()->files()->in($directory)->name('*.yml');
11350
}
11451

115-
private function display(InputInterface $input, OutputInterface $output, SymfonyStyle $io, $files)
52+
protected function isReadable($fileOrDirectory)
11653
{
117-
switch ($input->getOption('format')) {
118-
case 'txt':
119-
return $this->displayTxt($output, $io, $files);
120-
case 'json':
121-
return $this->displayJson($io, $files);
122-
default:
123-
throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format')));
124-
}
125-
}
126-
127-
private function displayTxt(OutputInterface $output, SymfonyStyle $io, $filesInfo)
128-
{
129-
$errors = 0;
130-
131-
foreach ($filesInfo as $info) {
132-
if ($info['valid'] && $output->isVerbose()) {
133-
$io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
134-
} elseif (!$info['valid']) {
135-
++$errors;
136-
$io->text(sprintf('<error> ERROR </error> in %s', $info['file']));
137-
$io->text(sprintf('<error> >> %s</error>', $info['message']));
138-
}
139-
}
140-
141-
if ($errors === 0) {
142-
$io->success(sprintf('All %d YAML files contain valid syntax.', count($filesInfo)));
143-
} else {
144-
$io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.', count($filesInfo) - $errors, $errors));
145-
}
146-
147-
return min($errors, 1);
148-
}
149-
150-
private function displayJson(OutputInterface $output, $filesInfo)
151-
{
152-
$errors = 0;
153-
154-
array_walk($filesInfo, function (&$v) use (&$errors) {
155-
$v['file'] = (string) $v['file'];
156-
if (!$v['valid']) {
157-
++$errors;
158-
}
159-
});
160-
161-
$output->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT));
162-
163-
return min($errors, 1);
54+
return 0 === strpos($fileOrDirectory, '@') || parent::isReadable($fileOrDirectory);
16455
}
16556
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
13+
14+
use Symfony\Bundle\FrameworkBundle\Command\YamlLintCommand;
15+
use Symfony\Component\Console\Application;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
use Symfony\Component\Console\Tester\CommandTester;
18+
19+
/**
20+
* Tests the YamlLintCommand.
21+
*
22+
* @author Robin Chalas <robin.chalas@gmail.com>
23+
*/
24+
class YamlLintCommandTest extends \PHPUnit_Framework_TestCase
25+
{
26+
private $files;
27+
28+
public function testLintCorrectFile()
29+
{
30+
$tester = $this->createCommandTester();
31+
$filename = $this->createFile('foo: bar');
32+
33+
$ret = $tester->execute(array('filename' => $filename), array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false));
34+
35+
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
36+
$this->assertRegExp('/^\/\/ OK in /', trim($tester->getDisplay()));
37+
}
38+
39+
public function testLintIncorrectFile()
40+
{
41+
$incorrectContent = '
42+
foo:
43+
bar';
44+
$tester = $this->createCommandTester();
45+
$filename = $this->createFile($incorrectContent);
46+
47+
$ret = $tester->execute(array('filename' => $filename), array('decorated' => false));
48+
49+
$this->assertEquals(1, $ret, 'Returns 1 in case of error');
50+
$this->assertContains('Unable to parse at line 3 (near "bar").', trim($tester->getDisplay()));
51+
}
52+
53+
/**
54+
* @expectedException \RuntimeException
55+
*/
56+
public function testLintFileNotReadable()
57+
{
58+
$tester = $this->createCommandTester();
59+
$filename = $this->createFile('');
60+
unlink($filename);
61+
62+
$ret = $tester->execute(array('filename' => $filename), array('decorated' => false));
63+
}
64+
65+
public function testGetHelp()
66+
{
67+
$command = new YamlLintCommand();
68+
$expected = <<<EOF
69+
The <info>%command.name%</info> command lints a YAML file and outputs to STDOUT
70+
the first encountered syntax error.
71+
72+
You can validates YAML contents passed from STDIN:
73+
74+
<info>cat filename | php %command.full_name%</info>
75+
76+
You can also validate the syntax of a file:
77+
78+
<info>php %command.full_name% filename</info>
79+
80+
Or of a whole directory:
81+
82+
<info>php %command.full_name% dirname</info>
83+
<info>php %command.full_name% dirname --format=json</info>
84+
85+
Or find all files in a bundle:
86+
87+
<info>php %command.full_name% @AcmeDemoBundle</info>
88+
89+
EOF;
90+
91+
$this->assertEquals($expected, $command->getHelp());
92+
}
93+
94+
/**
95+
* @return string Path to the new file
96+
*/
97+
private function createFile($content)
98+
{
99+
$filename = tempnam(sys_get_temp_dir(), 'sf-');
100+
file_put_contents($filename, $content);
101+
102+
$this->files[] = $filename;
103+
104+
return $filename;
105+
}
106+
107+
/**
108+
* @return CommandTester
109+
*/
110+
private function createCommandTester()
111+
{
112+
$command = new YamlLintCommand();
113+
$application = new Application();
114+
$application->add($command);
115+
$command = $application->find('lint:yaml');
116+
117+
return new CommandTester($command);
118+
}
119+
120+
protected function setUp()
121+
{
122+
$this->files = array();
123+
}
124+
125+
protected function tearDown()
126+
{
127+
foreach ($this->files as $file) {
128+
if (file_exists($file)) {
129+
unlink($file);
130+
}
131+
}
132+
}
133+
}

0 commit comments

Comments
 (0)
0