Add rpm spec file linter (rpmlint) (#486)

* Initial attempt at an rpmlint linter.

* Add some basic documentation.

* Play with indentation in the test file.

* Another attempt to fix the rpmlint test.

* Hopefully this does it.
This commit is contained in:
Jason Tibbitts 2017-04-19 17:40:58 -05:00 committed by w0rp
parent d8dcc56af5
commit 4eeb4783d3
5 changed files with 146 additions and 0 deletions

View File

@ -95,6 +95,7 @@ name. That seems to be the fairest way to arrange this table.
| Puppet | [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) | | Puppet | [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) |
| Python | [flake8](http://flake8.pycqa.org/en/latest/), [mypy](http://mypy-lang.org/), [pylint](https://www.pylint.org/) | | Python | [flake8](http://flake8.pycqa.org/en/latest/), [mypy](http://mypy-lang.org/), [pylint](https://www.pylint.org/) |
| reStructuredText | [proselint](http://proselint.com/)| | reStructuredText | [proselint](http://proselint.com/)|
| RPM spec | [rpmlint](https://github.com/rpm-software-management/rpmlint) |
| Ruby | [rubocop](https://github.com/bbatsov/rubocop), [ruby](https://www.ruby-lang.org) | | Ruby | [rubocop](https://github.com/bbatsov/rubocop), [ruby](https://www.ruby-lang.org) |
| Rust | [rustc](https://www.rust-lang.org/), cargo (see `:help ale-integration-rust` for configuration instructions) | | Rust | [rustc](https://www.rust-lang.org/), cargo (see `:help ale-integration-rust` for configuration instructions) |
| SASS | [sass-lint](https://www.npmjs.com/package/sass-lint), [stylelint](https://github.com/stylelint/stylelint) | | SASS | [sass-lint](https://www.npmjs.com/package/sass-lint), [stylelint](https://github.com/stylelint/stylelint) |

View File

@ -0,0 +1,85 @@
" Author: Jason Tibbitts <tibbs@math.uh.edu>
" Description: Adds support for checking RPM spec files with rpmlint
" rpmlint will produce varions types of output:
"
" Lines like the following are output when the file is simply not able to be
" parsed by rpmspec -P:
" apcupsd.spec: E: specfile-error warning: bogus date in %changelog: Mon Oct 1 2005 - Foo
" apcupsd.spec: E: specfile-error error: %changelog not in descending chronological order
" They do not contain a line number, and there's not a whole lot that can be
" done to locate them besides grep for them. rpmlint is just passing the
" output from rpm along with the filename, an error indicator, and an error
" type.
"
" Lines like the following:
" cyrus-imapd.spec:23: W: macro-in-comment %version
" cyrus-imapd.spec:18: E: hardcoded-library-path in %_prefix/lib/%name
" indicate warnings and errors, respectively. No column numbers are provided
"
" Lines like:
" apcupsd.spec: I: checking
" apcupsd.spec: I: checking-url https://downloads.sourceforge.net/apcupsd/apcupsd-3.14.14.tar.gz (timeout 10 seconds)
" are merely informational and are only output when -v is passed. But they
" may be useful in a log to know why things are taking so long.
"
" And this is always output at the end and should just be ignored:
" 0 packages and 1 specfiles checked; 4 errors, 0 warnings.
let g:ale_spec_rpmlint_executable =
\ get(g:, 'ale_spec_rpmlint_executable', 'rpmlint')
let g:ale_spec_rpmlint_options =
\ get(g:, 'ale_spec_rpmlint_options', '')
function! ale_linters#spec#rpmlint#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'spec_rpmlint_executable')
endfunction
function! ale_linters#spec#rpmlint#GetCommand(buffer) abort
return ale_linters#spec#rpmlint#GetExecutable(a:buffer)
\ . ' ' . ale#Var(a:buffer, 'spec_rpmlint_options')
\ . ' -o "NetworkEnabled False"'
\ . ' -v'
\ . ' %t'
endfunction
function! ale_linters#spec#rpmlint#Handle(buffer, lines) abort
" let l:pat_inform = '^.\+: I: \(.+\)'
let l:pat_errwarn = '^.\+:\(\d\+\): \([EW]\): \(.\+\)'
let l:pat_baderr = '^.\+: E: \(.\+\)'
let l:output = []
for l:line in a:lines
let l:match_errwarn = matchlist(l:line, l:pat_errwarn)
let l:match_baderr = matchlist(l:line, l:pat_baderr)
if len(l:match_errwarn) > 0
let l:text = l:match_errwarn[3]
let l:type = l:match_errwarn[2]
let l:lnum = l:match_errwarn[1] + 0
elseif len(l:match_baderr) > 0
let l:text = l:match_baderr[1]
let l:type = 'E'
let l:lnum = 1
else
continue
endif
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:lnum,
\ 'text': l:text,
\ 'type': l:type,
\})
endfor
return l:output
endfunction
call ale#linter#Define('spec', {
\ 'name': 'rpmlint',
\ 'executable_callback': 'ale_linters#spec#rpmlint#GetExecutable',
\ 'command_callback': 'ale_linters#spec#rpmlint#GetCommand',
\ 'callback': 'ale_linters#spec#rpmlint#Handle',
\})

28
doc/ale-spec.txt Normal file
View File

@ -0,0 +1,28 @@
===============================================================================
ALE RPM Spec Integration *ale-spec-options*
-------------------------------------------------------------------------------
rpmlint *ale-spec-rpmlint*
g:ale_spec_rpmlint_executable *g:ale_spec_rpmlint_executable*
Type: |String|
Default: `'rpmlint'`
This variable sets executable used for rpmlint.
g:ale_spec_rpmlint_options *g:ale_spec_rpmlint_options*
Type: |String|
Default: `''`
Set this to pass extra arguments to rpmlint.
For example, to instruct rpmlint to use a specific configuration file:
let g:ale_spec_rpmlint_options = '-f custom.cf'
-------------------------------------------------------------------------------
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View File

@ -71,6 +71,8 @@ CONTENTS *ale-contents*
sh....................................|ale-sh-options| sh....................................|ale-sh-options|
shell...............................|ale-sh-shell| shell...............................|ale-sh-shell|
shellcheck..........................|ale-sh-shellcheck| shellcheck..........................|ale-sh-shellcheck|
spec..................................|ale-spec-options|
rpmlint.............................|ale-spec-rpmlint|
tex...................................|ale-tex-options| tex...................................|ale-tex-options|
chktex..............................|ale-tex-chktex| chktex..............................|ale-tex-chktex|
lacheck.............................|ale-tex-lacheck| lacheck.............................|ale-tex-lacheck|
@ -151,6 +153,7 @@ The following languages and tools are supported.
* Puppet: 'puppet', 'puppet-lint' * Puppet: 'puppet', 'puppet-lint'
* Python: 'flake8', 'mypy', 'pylint' * Python: 'flake8', 'mypy', 'pylint'
* reStructuredText: 'proselint' * reStructuredText: 'proselint'
* RPM spec: 'spec'
* Rust: 'rustc' (see |ale-integration-rust|) * Rust: 'rustc' (see |ale-integration-rust|)
* Ruby: 'rubocop' * Ruby: 'rubocop'
* SASS: 'sasslint', 'stylelint' * SASS: 'sasslint', 'stylelint'

View File

@ -0,0 +1,29 @@
Execute(The rpmlint handler should parse error messages correctly):
runtime ale_linters/spec/rpmlint.vim
AssertEqual
\ [
\ {
\ 'bufnr': 42,
\ 'lnum': 23,
\ 'text': 'macro-in-comment %version',
\ 'type': 'W',
\ },
\ {
\ 'bufnr': 42,
\ 'lnum': 17,
\ 'text': 'hardcoded-library-path in %_prefix/lib/%name',
\ 'type': 'E',
\ },
\ {
\ 'bufnr': 42,
\ 'lnum': 1,
\ 'text': 'specfile-error warning: bogus date in %changelog: Mon Oct 1 2005 - Foo',
\ 'type': 'E',
\ },
\ ],
\ ale_linters#spec#rpmlint#Handle(42, [
\ 'cyrus-imapd.spec:23: W: macro-in-comment %version',
\ 'cyrus-imapd.spec:17: E: hardcoded-library-path in %_prefix/lib/%name',
\ 'apcupsd.spec: E: specfile-error warning: bogus date in %changelog: Mon Oct 1 2005 - Foo',
\ ])