diff --git a/doc/BUILTINS.md b/doc/BUILTINS.md index 36ec990d..d5bc3b95 100644 --- a/doc/BUILTINS.md +++ b/doc/BUILTINS.md @@ -1567,6 +1567,24 @@ local sources = { null_ls.builtins.diagnostics.terraform_validate } - Command: `terraform` - Args: `{ "validate", "-json" }` +### [terragrunt_validate](https://terragrunt.gruntwork.io/) + +Terragrunt validate is is a subcommand of terragrunt to validate configuration files in a directory + +#### Usage + +```lua +local sources = { null_ls.builtins.diagnostics.terragrunt_validate } +``` + +#### Defaults + +- Filetypes: `{ "hcl" }` +- Method: `diagnostics_on_save` +- Command: `terragrunt` +- Args: `{ "hclvalidate", "--terragrunt-hclvalidate-json" }` + + ### [textidote](https://github.com/sylvainhalle/textidote) Spelling, grammar and style checking on LaTeX documents. diff --git a/doc/builtins.json b/doc/builtins.json index e9bccc96..4ce44cd4 100644 --- a/doc/builtins.json +++ b/doc/builtins.json @@ -433,6 +433,11 @@ "terraform-vars" ] }, + "terragrunt_validate": { + "filetypes": [ + "hcl", + ] + }, "textidote": { "filetypes": [ "markdown", diff --git a/lua/null-ls/builtins/_meta/diagnostics.lua b/lua/null-ls/builtins/_meta/diagnostics.lua index 4fe1829e..4246ee8f 100644 --- a/lua/null-ls/builtins/_meta/diagnostics.lua +++ b/lua/null-ls/builtins/_meta/diagnostics.lua @@ -1,259 +1,262 @@ -- THIS FILE IS GENERATED. DO NOT EDIT MANUALLY. -- stylua: ignore return { - actionlint = { - filetypes = { "yaml" } - }, - alex = { - filetypes = { "markdown" } - }, - ansiblelint = { - filetypes = { "yaml.ansible" } - }, - bean_check = { - filetypes = { "beancount" } - }, - bslint = { - filetypes = { "brs" } - }, - buf = { - filetypes = { "proto" } - }, - buildifier = { - filetypes = { "bzl" } - }, - cfn_lint = { - filetypes = { "yaml", "json" } - }, - checkmake = { - filetypes = { "make" } - }, - checkstyle = { - filetypes = { "java" } - }, - clazy = { - filetypes = { "cpp" } - }, - clj_kondo = { - filetypes = { "clojure" } - }, - cmake_lint = { - filetypes = { "cmake" } - }, - codespell = { - filetypes = {} - }, - commitlint = { - filetypes = { "gitcommit" } - }, - cppcheck = { - filetypes = { "cpp", "c" } - }, - credo = { - filetypes = { "elixir" } - }, - cue_fmt = { - filetypes = { "cue" } - }, - deadnix = { - filetypes = { "nix" } - }, - djlint = { - filetypes = { "django", "jinja.html", "htmldjango" } - }, - dotenv_linter = { - filetypes = { "sh" } - }, - editorconfig_checker = { - filetypes = {} - }, - erb_lint = { - filetypes = { "eruby" } - }, - fish = { - filetypes = { "fish" } - }, - gccdiag = { - filetypes = { "c", "cpp" } - }, - gdlint = { - filetypes = { "gdscript" } - }, - gitlint = { - filetypes = { "gitcommit" } - }, - glslc = { - filetypes = { "glsl" } - }, - golangci_lint = { - filetypes = { "go" } - }, - hadolint = { - filetypes = { "dockerfile" } - }, - haml_lint = { - filetypes = { "haml" } - }, - ktlint = { - filetypes = { "kotlin" } - }, - ltrs = { - filetypes = { "text", "markdown", "markdown" } - }, - markdownlint = { - filetypes = { "markdown" } - }, - markdownlint_cli2 = { - filetypes = { "markdown" } - }, - markuplint = { - filetypes = { "html" } - }, - mdl = { - filetypes = { "markdown" } - }, - mlint = { - filetypes = { "matlab", "octave" } - }, - mypy = { - filetypes = { "python" } - }, - npm_groovy_lint = { - filetypes = { "groovy", "java", "Jenkinsfile" } - }, - opacheck = { - filetypes = { "rego" } - }, - perlimports = { - filetypes = { "perl" } - }, - phpcs = { - filetypes = { "php" } - }, - phpmd = { - filetypes = { "php" } - }, - phpstan = { - filetypes = { "php" } - }, - pmd = { - filetypes = { "java", "jsp" } - }, - proselint = { - filetypes = { "markdown", "tex" } - }, - protolint = { - filetypes = { "proto" } - }, - puppet_lint = { - filetypes = { "puppet", "epuppet" } - }, - pylint = { - filetypes = { "python" } - }, - qmllint = { - filetypes = { "qml" } - }, - reek = { - filetypes = { "ruby" } - }, - regal = { - filetypes = { "rego" } - }, - revive = { - filetypes = { "go" } - }, - rpmspec = { - filetypes = { "spec" } - }, - rstcheck = { - filetypes = { "rst" } - }, - rubocop = { - filetypes = { "ruby" } - }, - saltlint = { - filetypes = { "sls" } - }, - selene = { - filetypes = { "lua", "luau" } - }, - semgrep = { - filetypes = { "typescript", "typescriptreact", "ruby", "python", "java", "go" } - }, - solhint = { - filetypes = { "solidity" } - }, - spectral = { - filetypes = { "yaml", "json" } - }, - sqlfluff = { - filetypes = { "sql" } - }, - staticcheck = { - filetypes = { "go" } - }, - statix = { - filetypes = { "nix" } - }, - stylelint = { - filetypes = { "scss", "less", "css", "sass" } - }, - stylint = { - filetypes = { "stylus" } - }, - swiftlint = { - filetypes = { "swift" } - }, - teal = { - filetypes = { "teal" } - }, - terraform_validate = { - filetypes = { "terraform", "tf", "terraform-vars" } - }, - textidote = { - filetypes = { "markdown", "tex" } - }, - textlint = { - filetypes = { "txt", "markdown" } - }, - tfsec = { - filetypes = { "terraform", "tf", "terraform-vars" } - }, - tidy = { - filetypes = { "html", "xml" } - }, - todo_comments = { - filetypes = {} - }, - trail_space = { - filetypes = {} - }, - trivy = { - filetypes = { "terraform", "tf", "terraform-vars" } - }, - twigcs = { - filetypes = { "twig" } - }, - vacuum = { - filetypes = { "yaml", "json" } - }, - vale = { - filetypes = { "markdown", "tex", "asciidoc" } - }, - verilator = { - filetypes = { "verilog", "systemverilog" } - }, - vint = { - filetypes = { "vim" } - }, - write_good = { - filetypes = { "markdown" } - }, - yamllint = { - filetypes = { "yaml" } - }, - zsh = { - filetypes = { "zsh" } - } + actionlint = { + filetypes = { "yaml" } + }, + alex = { + filetypes = { "markdown" } + }, + ansiblelint = { + filetypes = { "yaml.ansible" } + }, + bean_check = { + filetypes = { "beancount" } + }, + bslint = { + filetypes = { "brs" } + }, + buf = { + filetypes = { "proto" } + }, + buildifier = { + filetypes = { "bzl" } + }, + cfn_lint = { + filetypes = { "yaml", "json" } + }, + checkmake = { + filetypes = { "make" } + }, + checkstyle = { + filetypes = { "java" } + }, + clazy = { + filetypes = { "cpp" } + }, + clj_kondo = { + filetypes = { "clojure" } + }, + cmake_lint = { + filetypes = { "cmake" } + }, + codespell = { + filetypes = {} + }, + commitlint = { + filetypes = { "gitcommit" } + }, + cppcheck = { + filetypes = { "cpp", "c" } + }, + credo = { + filetypes = { "elixir" } + }, + cue_fmt = { + filetypes = { "cue" } + }, + deadnix = { + filetypes = { "nix" } + }, + djlint = { + filetypes = { "django", "jinja.html", "htmldjango" } + }, + dotenv_linter = { + filetypes = { "sh" } + }, + editorconfig_checker = { + filetypes = {} + }, + erb_lint = { + filetypes = { "eruby" } + }, + fish = { + filetypes = { "fish" } + }, + gccdiag = { + filetypes = { "c", "cpp" } + }, + gdlint = { + filetypes = { "gdscript" } + }, + gitlint = { + filetypes = { "gitcommit" } + }, + glslc = { + filetypes = { "glsl" } + }, + golangci_lint = { + filetypes = { "go" } + }, + hadolint = { + filetypes = { "dockerfile" } + }, + haml_lint = { + filetypes = { "haml" } + }, + ktlint = { + filetypes = { "kotlin" } + }, + ltrs = { + filetypes = { "text", "markdown", "markdown" } + }, + markdownlint = { + filetypes = { "markdown" } + }, + markdownlint_cli2 = { + filetypes = { "markdown" } + }, + markuplint = { + filetypes = { "html" } + }, + mdl = { + filetypes = { "markdown" } + }, + mlint = { + filetypes = { "matlab", "octave" } + }, + mypy = { + filetypes = { "python" } + }, + npm_groovy_lint = { + filetypes = { "groovy", "java", "Jenkinsfile" } + }, + opacheck = { + filetypes = { "rego" } + }, + perlimports = { + filetypes = { "perl" } + }, + phpcs = { + filetypes = { "php" } + }, + phpmd = { + filetypes = { "php" } + }, + phpstan = { + filetypes = { "php" } + }, + pmd = { + filetypes = { "java", "jsp" } + }, + proselint = { + filetypes = { "markdown", "tex" } + }, + protolint = { + filetypes = { "proto" } + }, + puppet_lint = { + filetypes = { "puppet", "epuppet" } + }, + pylint = { + filetypes = { "python" } + }, + qmllint = { + filetypes = { "qml" } + }, + reek = { + filetypes = { "ruby" } + }, + regal = { + filetypes = { "rego" } + }, + revive = { + filetypes = { "go" } + }, + rpmspec = { + filetypes = { "spec" } + }, + rstcheck = { + filetypes = { "rst" } + }, + rubocop = { + filetypes = { "ruby" } + }, + saltlint = { + filetypes = { "sls" } + }, + selene = { + filetypes = { "lua", "luau" } + }, + semgrep = { + filetypes = { "typescript", "typescriptreact", "ruby", "python", "java", "go" } + }, + solhint = { + filetypes = { "solidity" } + }, + spectral = { + filetypes = { "yaml", "json" } + }, + sqlfluff = { + filetypes = { "sql" } + }, + staticcheck = { + filetypes = { "go" } + }, + statix = { + filetypes = { "nix" } + }, + stylelint = { + filetypes = { "scss", "less", "css", "sass" } + }, + stylint = { + filetypes = { "stylus" } + }, + swiftlint = { + filetypes = { "swift" } + }, + teal = { + filetypes = { "teal" } + }, + terraform_validate = { + filetypes = { "terraform", "tf", "terraform-vars" } + }, + terragrunt_validate = { + filetypes = { "hcl" } + }, + textidote = { + filetypes = { "markdown", "tex" } + }, + textlint = { + filetypes = { "txt", "markdown" } + }, + tfsec = { + filetypes = { "terraform", "tf", "terraform-vars" } + }, + tidy = { + filetypes = { "html", "xml" } + }, + todo_comments = { + filetypes = {} + }, + trail_space = { + filetypes = {} + }, + trivy = { + filetypes = { "terraform", "tf", "terraform-vars" } + }, + twigcs = { + filetypes = { "twig" } + }, + vacuum = { + filetypes = { "yaml", "json" } + }, + vale = { + filetypes = { "markdown", "tex", "asciidoc" } + }, + verilator = { + filetypes = { "verilog", "systemverilog" } + }, + vint = { + filetypes = { "vim" } + }, + write_good = { + filetypes = { "markdown" } + }, + yamllint = { + filetypes = { "yaml" } + }, + zsh = { + filetypes = { "zsh" } + } } diff --git a/lua/null-ls/builtins/_meta/filetype_map.lua b/lua/null-ls/builtins/_meta/filetype_map.lua index d43519c7..04cd5097 100644 --- a/lua/null-ls/builtins/_meta/filetype_map.lua +++ b/lua/null-ls/builtins/_meta/filetype_map.lua @@ -174,6 +174,7 @@ return { formatting = { "haxe_formatter" } }, hcl = { + diagnostics = { "terragrunt_validate" }, formatting = { "hclfmt", "packer", "terragrunt_fmt" } }, html = { diff --git a/lua/null-ls/builtins/diagnostics/terragrunt_validate.lua b/lua/null-ls/builtins/diagnostics/terragrunt_validate.lua new file mode 100644 index 00000000..406452be --- /dev/null +++ b/lua/null-ls/builtins/diagnostics/terragrunt_validate.lua @@ -0,0 +1,71 @@ +local h = require("null-ls.helpers") +local methods = require("null-ls.methods") + +local DIAGNOSTICS_ON_SAVE = methods.internal.DIAGNOSTICS_ON_SAVE + +return h.make_builtin({ + name = "terragrunt_validate", + meta = { + url = "https://terragrunt.gruntwork.io/docs/reference/cli-options/#validate-inputs", + description = "Terragrunt validate is is a subcommand of terragrunt to validate configuration files in a directory", + }, + method = DIAGNOSTICS_ON_SAVE, + filetypes = { "hcl" }, + generator_opts = { + command = "terragrunt", + args = { + "hclvalidate", + "--terragrunt-hclvalidate-json", + }, + cwd = h.cache.by_bufnr(function(params) + return vim.fs.dirname(params.bufname) + end), + from_stderr = true, + to_stdin = false, + multiple_files = true, + format = "json", + check_exit_code = function(_code, _stderr) + -- check for warnings even if there are no errors + return false + end, + on_output = function(params) + local combined_diagnostics = {} + + -- keep diagnostics from other directories + if params.source_id ~= nil then + local namespace = require("null-ls.diagnostics").get_namespace(params.source_id) + local old_diagnostics = vim.diagnostic.get(nil, { namespace = namespace }) + for _, old_diagnostic in ipairs(old_diagnostics) do + if not vim.startswith(old_diagnostic.filename, params.cwd) then + table.insert(combined_diagnostics, old_diagnostic) + end + end + end + + for _, new_diagnostic in ipairs(params.output) do + local message = new_diagnostic.summary + if new_diagnostic.detail then + message = message .. " - " .. new_diagnostic.detail + end + local rewritten_diagnostic = { + message = message, + row = 0, + col = 0, + source = "terragrunt validate", + severity = h.diagnostics.severities[new_diagnostic.severity], + filename = params.bufname, + } + if new_diagnostic.range ~= nil then + rewritten_diagnostic.col = new_diagnostic.range.start.column + rewritten_diagnostic.end_col = new_diagnostic.range["end"].column + rewritten_diagnostic.row = new_diagnostic.range.start.line + rewritten_diagnostic.end_row = new_diagnostic.range["end"].line + rewritten_diagnostic.filename = new_diagnostic.range.filename + end + table.insert(combined_diagnostics, rewritten_diagnostic) + end + return combined_diagnostics + end, + }, + factory = h.generator_factory, +}) diff --git a/test/spec/builtins/diagnostics_spec.lua b/test/spec/builtins/diagnostics_spec.lua index a6a29e89..590628b1 100644 --- a/test/spec/builtins/diagnostics_spec.lua +++ b/test/spec/builtins/diagnostics_spec.lua @@ -1605,4 +1605,52 @@ INFO: Analysis cache updated]], }, diagnostic) end) end) + describe("terragrunt_validate", function() + local linter = diagnostics.terragrunt_validate + local parser = linter._opts.on_output + + it("should create a diagnostic for specific errors", function() + local output = vim.json.decode([[ + [{ + "severity": "error", + "summary": "Missing name for include", + "detail": "All include blocks must have 1 labels (name).", + "range": { + "filename": "/src/terragrunt.hcl", + "start": { + "line": 1, + "column": 9, + "byte": 8 + }, + "end": { + "line": 1, + "column": 10, + "byte": 9 + } + }, + "snippet": { + "context": "include", + "code": "include {", + "start_line": 1, + "highlight_start_offset": 8, + "highlight_end_offset": 9, + "values": null + } + }] + ]]) + local diagnostic = parser({ output = output }) + assert.same({ + { + col = 9, + end_col = 10, + end_row = 1, + message = "Missing name for include - All include blocks must have 1 labels (name).", + row = 1, + severity = 1, + source = "terragrunt validate", + filename = "/src/terragrunt.hcl", + }, + }, diagnostic) + end) + end) end)