From a46121a532b2baaa339016ab910c59f1cded46e5 Mon Sep 17 00:00:00 2001 From: Shad Date: Mon, 22 May 2023 04:58:59 +0200 Subject: [PATCH] Dockerlinter (#4518) * dockerlinter support * Tests & ShellCheck reference * sort and align docs --- ale_linters/dockerfile/dockerlinter.vim | 69 ++++++++++++++++++ doc/ale-dockerfile.txt | 25 +++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + test/handler/test_dockerlinter_handler.vader | 77 ++++++++++++++++++++ test/linter/test_dockerlinter.vader | 19 +++++ 7 files changed, 193 insertions(+) create mode 100644 ale_linters/dockerfile/dockerlinter.vim create mode 100644 test/handler/test_dockerlinter_handler.vader create mode 100644 test/linter/test_dockerlinter.vader diff --git a/ale_linters/dockerfile/dockerlinter.vim b/ale_linters/dockerfile/dockerlinter.vim new file mode 100644 index 00000000..6b35438f --- /dev/null +++ b/ale_linters/dockerfile/dockerlinter.vim @@ -0,0 +1,69 @@ +" Author: Shad +" Description: dockerlinter linter for dockerfile + +call ale#Set('dockerfile_dockerlinter_executable', 'dockerlinter') +call ale#Set('dockerfile_dockerlinter_options', '') + +function! ale_linters#dockerfile#dockerlinter#GetType(type) abort + if a:type is? 'error' + return 'E' + elseif a:type is? 'warning' + return 'W' + endif + + return 'I' +endfunction + +function! ale_linters#dockerfile#dockerlinter#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:object in l:data + let l:line = get(l:object, 'lineNumber', -1) + let l:message = l:object['message'] + let l:type = l:object['level'] + let l:detail = l:message + let l:code = l:object['code'] + + if l:code =~# '^SC' + let l:link = 'https://www.shellcheck.net/wiki/' . l:code + else + let l:link = 'https://github.com/buddy-works/dockerfile-linter/blob/master/Rules.md#' . l:code + endif + + let l:detail = l:message . "\n\n" . l:link + + call add(l:messages, { + \ 'lnum': l:line, + \ 'code': l:code, + \ 'text': l:message, + \ 'type': ale_linters#dockerfile#dockerlinter#GetType(l:type), + \ 'detail': l:detail, + \}) + endfor + + return l:messages +endfunction + +function! ale_linters#dockerfile#dockerlinter#GetCommand(buffer) abort + return '%e' . ale#Pad(ale#Var(a:buffer, 'dockerfile_dockerlinter_options')) + \ . ' -j -f' + \ . ' %t' +endfunction + +call ale#linter#Define('dockerfile', { +\ 'name': 'dockerlinter', +\ 'executable': {b -> ale#Var(b, 'dockerfile_dockerlinter_executable')}, +\ 'command': function('ale_linters#dockerfile#dockerlinter#GetCommand'), +\ 'callback': 'ale_linters#dockerfile#dockerlinter#Handle', +\}) diff --git a/doc/ale-dockerfile.txt b/doc/ale-dockerfile.txt index 51b9acc1..5d955404 100644 --- a/doc/ale-dockerfile.txt +++ b/doc/ale-dockerfile.txt @@ -25,6 +25,31 @@ g:ale_dockerfile_dockerfile_lint_options the dockerfile lint invocation - like custom rule file definitions. +=============================================================================== +dockerlinter *ale-dockerfile-dockerlinter* + +g:ale_dockerfile_dockerlinter_executable + *g:ale_dockerfile_dockerlinter_executable* + *b:ale_dockerfile_dockerlinter_executable* + Type: |String| + Default: `'dockerlinter'` + + This variable can be changed to specify the executable used to run + dockerlinter. + + +g:ale_dockerfile_dockerlinter_options + *g:ale_dockerfile_dockerlinter_options* + *b:ale_dockerfile_dockerlinter_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. + + dockerlinter + + =============================================================================== dprint *ale-dockerfile-dprint* diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index 571322f4..4009b731 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -159,6 +159,7 @@ Notes: * `dhall-lint` * Dockerfile * `dockerfile_lint` + * `dockerlinter` * `dprint` * `hadolint` * Elixir diff --git a/doc/ale.txt b/doc/ale.txt index a167f214..9253c398 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2941,6 +2941,7 @@ documented in additional help files. dhall-lint............................|ale-dhall-lint| dockerfile..............................|ale-dockerfile-options| dockerfile_lint.......................|ale-dockerfile-dockerfile_lint| + dockerlinter..........................|ale-dockerfile-dockerlinter| dprint................................|ale-dockerfile-dprint| hadolint..............................|ale-dockerfile-hadolint| elixir..................................|ale-elixir-options| diff --git a/supported-tools.md b/supported-tools.md index fd906093..2a976b45 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -168,6 +168,7 @@ formatting. * [dhall-lint](https://github.com/dhall-lang/dhall-lang) * Dockerfile * [dockerfile_lint](https://github.com/projectatomic/dockerfile_lint) + * [dockerlinter](https://github.com/buddy-works/dockerfile-linter) * [dprint](https://dprint.dev) * [hadolint](https://github.com/hadolint/hadolint) * Elixir diff --git a/test/handler/test_dockerlinter_handler.vader b/test/handler/test_dockerlinter_handler.vader new file mode 100644 index 00000000..de59faac --- /dev/null +++ b/test/handler/test_dockerlinter_handler.vader @@ -0,0 +1,77 @@ +Before: + runtime ale_linters/dockerfile/dockerlinter.vim + +After: + call ale#linter#Reset() + +Execute(The dockerlinter handler should handle broken JSON): + AssertEqual + \ [], + \ ale_linters#dockerfile#dockerlinter#Handle(bufnr(''), ["{asdf"]) + +Execute(The dockerlinter handler should handle an empty string response): + AssertEqual + \ [], + \ ale_linters#dockerfile#dockerlinter#Handle(bufnr(''), []) + +Execute(The dockerlinter handler should handle an empty result, even if it shouldn't happen): + AssertEqual + \ [], + \ ale_linters#dockerfile#dockerlinter#Handle(bufnr(''), ["{}"]) + +Execute(The dockerlinter handler should handle a normal example): + AssertEqual + \ [ + \ { + \ 'lnum': 11, + \ 'type': 'I', + \ 'code': 'ER0002', + \ 'text': "Delete the apt-get lists after installing something", + \ 'detail': "Delete the apt-get lists after installing something\n\nhttps://github.com/buddy-works/dockerfile-linter/blob/master/Rules.md#ER0002", + \ }, + \ { + \ 'lnum': 11, + \ 'type': 'I', + \ 'code': 'ER0010', + \ 'text': "Avoid additional packages by specifying --no-install-recommends", + \ 'detail': "Avoid additional packages by specifying --no-install-recommends\n\nhttps://github.com/buddy-works/dockerfile-linter/blob/master/Rules.md#ER0010", + \ }, + \ { + \ 'lnum': 11, + \ 'type': 'I', + \ 'code': 'ER0012', + \ 'text': "Pin versions in apt get install", + \ 'detail': "Pin versions in apt get install\n\nhttps://github.com/buddy-works/dockerfile-linter/blob/master/Rules.md#ER0012", + \ }, + \ { + \ 'lnum': 30, + \ 'type': 'W', + \ 'code': 'SC2155', + \ 'text': "Declare and assign separately to avoid masking return values.", + \ 'detail': "Declare and assign separately to avoid masking return values.\n\nhttps://www.shellcheck.net/wiki/SC2155", + \ }, + \ { + \ 'lnum': 30, + \ 'type': 'W', + \ 'code': 'SC2046', + \ 'text': "Quote this to prevent word splitting.", + \ 'detail': "Quote this to prevent word splitting.\n\nhttps://www.shellcheck.net/wiki/SC2046", + \ }, + \ { + \ 'lnum': 30, + \ 'type': 'I', + \ 'code': 'SC2086', + \ 'text': "Double quote to prevent globbing and word splitting.", + \ 'detail': "Double quote to prevent globbing and word splitting.\n\nhttps://www.shellcheck.net/wiki/SC2086", + \ }, + \ { + \ 'lnum': 31, + \ 'type': 'W', + \ 'code': 'SC2046', + \ 'text': "Quote this to prevent word splitting.", + \ 'detail': "Quote this to prevent word splitting.\n\nhttps://www.shellcheck.net/wiki/SC2046", + \ }, + \ ], + \ ale_linters#dockerfile#dockerlinter#Handle(bufnr(''), [ + \ '[{"lineNumber":11,"message":"Delete the apt-get lists after installing something","level":"info","code":"ER0002"},{"lineNumber":11,"message":"Avoid additional packages by specifying --no-install-recommends","level":"info","code":"ER0010"},{"lineNumber":11,"message":"Pin versions in apt get install","level":"info","code":"ER0012"},{"lineNumber":30,"message":"Declare and assign separately to avoid masking return values.","level":"warning","code":"SC2155"},{"lineNumber":30,"message":"Quote this to prevent word splitting.","level":"warning","code":"SC2046"},{"lineNumber":30,"message":"Double quote to prevent globbing and word splitting.","level":"info","code":"SC2086"},{"lineNumber":31,"message":"Quote this to prevent word splitting.","level":"warning","code":"SC2046"}]', + \ ]) diff --git a/test/linter/test_dockerlinter.vader b/test/linter/test_dockerlinter.vader new file mode 100644 index 00000000..a7077221 --- /dev/null +++ b/test/linter/test_dockerlinter.vader @@ -0,0 +1,19 @@ +Before: + call ale#assert#SetUpLinterTest('dockerfile', 'dockerlinter') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct): + AssertLinter 'dockerlinter', ale#Escape('dockerlinter') . ' -j -f %t' + +Execute(The executable should be configurable): + let b:ale_dockerfile_dockerlinter_executable = 'foobar' + + AssertLinter 'foobar', ale#Escape('foobar') . ' -j -f %t' + +Execute(The options should be configurable): + let b:ale_dockerfile_dockerlinter_options = '-r additional.yaml' + + AssertLinter 'dockerlinter', ale#Escape('dockerlinter') . ' -r additional.yaml -j -f %t' +