Add the dockerfile_lint linter for Dockerfiles (#1971)

* Add the dockerfile_lint linter for Dockerfiles
This commit is contained in:
Alexander "Ananace" Olofsson 2018-10-26 18:34:32 +02:00 committed by w0rp
parent 34318aedf4
commit 7af33637e8
6 changed files with 214 additions and 2 deletions

View File

@ -117,7 +117,7 @@ formatting.
| D | [dls](https://github.com/d-language-server/dls), [dmd](https://dlang.org/dmd-linux.html), [uncrustify](https://github.com/uncrustify/uncrustify) |
| Dafny | [dafny](https://rise4fun.com/Dafny) !! |
| Dart | [dartanalyzer](https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_cli) !!, [language_server](https://github.com/natebosch/dart_language_server), [dartfmt](https://github.com/dart-lang/sdk/tree/master/utils/dartfmt) |
| Dockerfile | [hadolint](https://github.com/hadolint/hadolint) |
| Dockerfile | [dockerfile_lint](https://github.com/projectatomic/dockerfile_lint), [hadolint](https://github.com/hadolint/hadolint) |
| Elixir | [credo](https://github.com/rrrene/credo), [dialyxir](https://github.com/jeremyjh/dialyxir), [dogma](https://github.com/lpil/dogma), [mix](https://hexdocs.pm/mix/Mix.html) !!, [elixir-ls](https://github.com/JakeBecker/elixir-ls) |
| Elm | [elm-format](https://github.com/avh4/elm-format), [elm-make](https://github.com/elm-lang/elm-make) |
| Erb | [erb](https://apidock.com/ruby/ERB), [erubi](https://github.com/jeremyevans/erubi), [erubis](https://github.com/kwatch/erubis) |

View File

@ -0,0 +1,61 @@
" Author: Alexander Olofsson <alexander.olofsson@liu.se>
call ale#Set('dockerfile_dockerfile_lint_executable', 'dockerfile_lint')
call ale#Set('dockerfile_dockerfile_lint_options', '')
function! ale_linters#dockerfile#dockerfile_lint#GetType(type) abort
if a:type is? 'error'
return 'E'
elseif a:type is? 'warn'
return 'W'
endif
return 'I'
endfunction
function! ale_linters#dockerfile#dockerfile_lint#Handle(buffer, lines) abort
try
let l:data = json_decode(join(a:lines, ''))
catch
return []
endtry
if empty(l:data)
" Should never happen, but it's better to be on the safe side
return []
endif
let l:messages = []
for l:type in ['error', 'warn', 'info']
for l:object in l:data[l:type]['data']
let l:line = get(l:object, 'line', -1)
let l:message = l:object['message']
if get(l:object, 'description', 'None') isnot# 'None'
let l:message = l:message . '. ' . l:object['description']
endif
call add(l:messages, {
\ 'lnum': l:line,
\ 'text': l:message,
\ 'type': ale_linters#dockerfile#dockerfile_lint#GetType(l:type),
\})
endfor
endfor
return l:messages
endfunction
function! ale_linters#dockerfile#dockerfile_lint#GetCommand(buffer) abort
return '%e' . ale#Pad(ale#Var(a:buffer, 'dockerfile_dockerfile_lint_options'))
\ . ' -p -j -f'
\ . ' %t'
endfunction
call ale#linter#Define('dockerfile', {
\ 'name': 'dockerfile_lint',
\ 'executable_callback': ale#VarFunc('dockerfile_dockerfile_lint_executable'),
\ 'command_callback': 'ale_linters#dockerfile#dockerfile_lint#GetCommand',
\ 'callback': 'ale_linters#dockerfile#dockerfile_lint#Handle',
\})

View File

@ -2,6 +2,29 @@
ALE Dockerfile Integration *ale-dockerfile-options*
===============================================================================
dockerfile_lint *ale-dockerfile-dockerfile_lint*
g:ale_dockerfile_dockerfile_lint_executable
*g:ale_dockerfile_dockerfile_lint_executable*
*b:ale_dockerfile_dockerfile_lint_executable*
Type: |String|
Default: `'dockerfile_lint'`
This variable can be changed to specify the executable used to run
dockerfile_lint.
g:ale_dockerfile_dockerfile_lint_options
*g:ale_dockerfile_dockerfile_lint_options*
*b:ale_dockerfile_dockerfile_lint_options*
Type: |String|
Default: `''`
This variable can be changed to add additional command-line arguments to
the dockerfile lint invocation - like custom rule file definitions.
===============================================================================
hadolint *ale-dockerfile-hadolint*

View File

@ -75,6 +75,7 @@ CONTENTS *ale-contents*
dartanalyzer........................|ale-dart-dartanalyzer|
dartfmt.............................|ale-dart-dartfmt|
dockerfile............................|ale-dockerfile-options|
dockerfile_lint.....................|ale-dockerfile-dockerfile_lint|
hadolint............................|ale-dockerfile-hadolint|
elixir................................|ale-elixir-options|
mix.................................|ale-elixir-mix|
@ -407,7 +408,7 @@ Notes:
* D: `dls`, `dmd`, `uncrustify`
* Dafny: `dafny`!!
* Dart: `dartanalyzer`!!, `language_server`, dartfmt!!
* Dockerfile: `hadolint`
* Dockerfile: `dockerfile_lint`, `hadolint`
* Elixir: `credo`, `dialyxir`, `dogma`, `mix`!!, `elixir-ls`
* Elm: `elm-format, elm-make`
* Erb: `erb`, `erubi`, `erubis`

View File

@ -0,0 +1,19 @@
Before:
call ale#assert#SetUpLinterTest('dockerfile', 'dockerfile_lint')
After:
call ale#assert#TearDownLinterTest()
Execute(The default command should be correct):
AssertLinter 'dockerfile_lint', ale#Escape('dockerfile_lint') . ' -p -j -f %t'
Execute(The executable should be configurable):
let b:ale_dockerfile_dockerfile_lint_executable = 'foobar'
AssertLinter 'foobar', ale#Escape('foobar') . ' -p -j -f %t'
Execute(The options should be configurable):
let b:ale_dockerfile_dockerfile_lint_options = '-r additional.yaml'
AssertLinter 'dockerfile_lint', ale#Escape('dockerfile_lint') . ' -r additional.yaml -p -j -f %t'

View File

@ -0,0 +1,108 @@
Before:
runtime ale_linters/dockerfile/dockerfile_lint.vim
After:
call ale#linter#Reset()
Execute(The dockerfile_lint handler should handle broken JSON):
AssertEqual
\ [],
\ ale_linters#dockerfile#dockerfile_lint#Handle(bufnr(''), ["{asdf"])
Execute(The dockerfile_lint handler should handle an empty string response):
AssertEqual
\ [],
\ ale_linters#dockerfile#dockerfile_lint#Handle(bufnr(''), [])
Execute(The dockerfile_lint handler should handle an empty result, even if it shouldn't happen):
AssertEqual
\ [],
\ ale_linters#dockerfile#dockerfile_lint#Handle(bufnr(''), ["{}"])
Execute(The dockerfile_lint handler should handle a normal example):
AssertEqual
\ [
\ {
\ 'lnum': -1,
\ 'type': 'E',
\ 'text': "Required LABEL name/key 'Name' is not defined",
\ },
\ {
\ 'lnum': -1,
\ 'type': 'E',
\ 'text': "Required LABEL name/key 'Version' is not defined",
\ },
\ {
\ 'lnum': 3,
\ 'type': 'I',
\ 'text': "the MAINTAINER command is deprecated. MAINTAINER is deprecated in favor of using LABEL since Docker v1.13.0",
\ },
\ {
\ 'lnum': -1,
\ 'type': 'I',
\ 'text': "There is no 'CMD' instruction",
\ },
\ ],
\ ale_linters#dockerfile#dockerfile_lint#Handle(bufnr(''), [
\ '{',
\ ' "error": {',
\ ' "count": 2,',
\ ' "data": [',
\ ' {',
\ " \"message\": \"Required LABEL name/key 'Name' is not defined\",",
\ ' "line": -1,',
\ ' "level": "error",',
\ ' "lineContent": "",',
\ ' "reference_url": [',
\ ' "http://docs.projectatomic.io/container-best-practices/#",',
\ ' "_recommended_labels_for_your_project"',
\ ' ]',
\ ' },',
\ ' {',
\ " \"message\": \"Required LABEL name/key 'Version' is not defined\",",
\ ' "line": -1,',
\ ' "level": "error",',
\ ' "lineContent": "",',
\ ' "reference_url": [',
\ ' "http://docs.projectatomic.io/container-best-practices/#",',
\ ' "_recommended_labels_for_your_project"',
\ ' ]',
\ ' }',
\ ' ]',
\ ' },',
\ ' "warn": {',
\ ' "count": 0,',
\ ' "data": []',
\ ' },',
\ ' "info": {',
\ ' "count": 2,',
\ ' "data": [',
\ ' {',
\ ' "label": "maintainer_deprecated",',
\ ' "regex": {},',
\ ' "level": "info",',
\ ' "message": "the MAINTAINER command is deprecated",',
\ ' "description": "MAINTAINER is deprecated in favor of using LABEL since Docker v1.13.0",',
\ ' "reference_url": [',
\ ' "https://github.com/docker/cli/blob/master/docs/deprecated.md",',
\ ' "#maintainer-in-dockerfile"',
\ ' ],',
\ ' "lineContent": "MAINTAINER Alexander Olofsson <ace@haxalot.com>",',
\ ' "line": 3',
\ ' },',
\ ' {',
\ ' "instruction": "CMD",',
\ ' "count": 1,',
\ ' "level": "info",',
\ " \"message\": \"There is no 'CMD' instruction\",",
\ ' "description": "None",',
\ ' "reference_url": [',
\ ' "https://docs.docker.com/engine/reference/builder/",',
\ ' "#cmd"',
\ ' ]',
\ ' }',
\ ' ]',
\ ' },',
\ ' "summary": []',
\ '}',
\ ])