generated from bazel-contrib/rules-template
-
-
Notifications
You must be signed in to change notification settings - Fork 60
/
Copy pathshellcheck.bzl
122 lines (101 loc) · 4.59 KB
/
shellcheck.bzl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""API for declaring a shellcheck lint aspect that visits sh_library rules.
Typical usage:
Use [shellcheck_aspect](#shellcheck_aspect) to declare the shellcheck linter aspect, typically in in `tools/lint/linters.bzl`:
```
load("@aspect_rules_lint//lint:shellcheck.bzl", "shellcheck_aspect")
shellcheck = shellcheck_aspect(
binary = "@multitool//tools/shellcheck",
config = "@@//:.shellcheckrc",
)
```
"""
load("//lint/private:lint_aspect.bzl", "LintOptionsInfo", "filter_srcs", "noop_lint_action", "output_files", "patch_and_output_files", "should_visit")
_MNEMONIC = "AspectRulesLintShellCheck"
_OUTFILE_FORMAT = "{label}.{mnemonic}.{suffix}"
def shellcheck_action(ctx, executable, srcs, config, stdout, exit_code = None, options = []):
"""Run shellcheck as an action under Bazel.
Based on https://github.com/koalaman/shellcheck/blob/master/shellcheck.1.md
Args:
ctx: Bazel Rule or Aspect evaluation context
executable: label of the the shellcheck program
srcs: bash files to be linted
config: label of the .shellcheckrc file
stdout: output file containing stdout of shellcheck
exit_code: output file containing shellcheck exit code.
If None, then fail the build when vale exits non-zero.
See https://github.com/koalaman/shellcheck/blob/master/shellcheck.1.md#return-values
options: additional command-line options, see https://github.com/koalaman/shellcheck/blob/master/shellcheck.hs#L95
"""
inputs = srcs + [config]
# Wire command-line options, see
# https://github.com/koalaman/shellcheck/blob/master/shellcheck.1.md#options
args = ctx.actions.args()
args.add_all(options)
args.add_all(srcs)
outputs = [stdout]
if exit_code:
command = "{shellcheck} $@ >{stdout}; echo $? >" + exit_code.path
outputs.append(exit_code)
else:
# Create empty file on success, as Bazel expects one
command = "{shellcheck} $@ && touch {stdout}"
ctx.actions.run_shell(
inputs = inputs,
outputs = outputs,
command = command.format(
shellcheck = executable.path,
stdout = stdout.path,
),
arguments = [args],
mnemonic = _MNEMONIC,
progress_message = "Linting %{label} with ShellCheck",
tools = [executable],
)
# buildifier: disable=function-docstring
def _shellcheck_aspect_impl(target, ctx):
if not should_visit(ctx.rule, ctx.attr._rule_kinds):
return []
files_to_lint = filter_srcs(ctx.rule)
if ctx.attr._options[LintOptionsInfo].fix:
outputs, info = patch_and_output_files(_MNEMONIC, target, ctx)
else:
outputs, info = output_files(_MNEMONIC, target, ctx)
if len(files_to_lint) == 0:
noop_lint_action(ctx, outputs)
return [info]
color_options = ["--color"] if ctx.attr._options[LintOptionsInfo].color else []
# shellcheck does not have a --fix mode that applies fixes for some violations while reporting others.
# So we must run an action to generate the report separately from an action that writes the human-readable report.
if hasattr(outputs, "patch"):
discard_exit_code = ctx.actions.declare_file(_OUTFILE_FORMAT.format(label = target.label.name, mnemonic = _MNEMONIC, suffix = "patch_exit_code"))
shellcheck_action(ctx, ctx.executable._shellcheck, files_to_lint, ctx.file._config_file, outputs.patch, discard_exit_code, ["--format", "diff"])
shellcheck_action(ctx, ctx.executable._shellcheck, files_to_lint, ctx.file._config_file, outputs.human.out, outputs.human.exit_code, color_options)
shellcheck_action(ctx, ctx.executable._shellcheck, files_to_lint, ctx.file._config_file, outputs.machine.out, outputs.machine.exit_code)
return [info]
def lint_shellcheck_aspect(binary, config, rule_kinds = ["sh_binary", "sh_library", "sh_test"]):
"""A factory function to create a linter aspect.
Attrs:
binary: a shellcheck executable.
config: the .shellcheckrc file
"""
return aspect(
implementation = _shellcheck_aspect_impl,
attrs = {
"_options": attr.label(
default = "//lint:options",
providers = [LintOptionsInfo],
),
"_shellcheck": attr.label(
default = binary,
executable = True,
cfg = "exec",
),
"_config_file": attr.label(
default = config,
allow_single_file = True,
),
"_rule_kinds": attr.string_list(
default = rule_kinds,
),
},
)