0001-New-short-codes-added-for-each-linter.patch
1
From b6a6141343002f24e03b70b5c6cefc4787480401 Mon Sep 17 00:00:00 2001
2
From: Philip Roche <phil.roche@canonical.com>
3
Date: Wed, 19 Aug 2020 18:00:57 +0100
4
Subject: [PATCH 1/3] New short codes added for each linter
5
6
Short codes can be used to select or ignore specific tests when linting.
7
8
Short codes are included in the failure output.
9
10
New click subcommand added to list all linters and their short codes
11
12
`jenkins-job-linter list-linters`
13
---
14
 jenkins_job_linter/__init__.py | 53 ++++++++++++++++++++++++++++++++--
15
 jenkins_job_linter/linters.py  | 11 +++++++
16
 2 files changed, 61 insertions(+), 3 deletions(-)
17
18
diff --git a/jenkins_job_linter/__init__.py b/jenkins_job_linter/__init__.py
19
index 833563c67fc0238c444b3816f830654b9adcbd1f..840bb46f5dbf13ebda7d323e21ad278e134aa7f2 100755
20
--- a/jenkins_job_linter/__init__.py
21
+++ b/jenkins_job_linter/__init__.py
22
@@ -23,7 +23,7 @@ import click
23
 import jenkins
24
 
25
 from jenkins_job_linter.config import _filter_config, GetListConfigParser
26
-from jenkins_job_linter.linters import LINTERS
27
+from jenkins_job_linter.linters import LINTERS, LINTER_SHORT_CODES
28
 from jenkins_job_linter.models import LintContext, RunContext
29
 
30
 
31
@@ -41,7 +41,8 @@ def lint_job_xml(ctx: RunContext, job_name: str, tree: ElementTree.ElementTree,
32
         result, text = linter(LintContext(section, ctx, tree)).check()
33
         if not result.value:
34
             success = False
35
-            output = '{}: {}: FAIL'.format(job_name, linter.description)
36
+            output = '{}: ({}) {}: FAIL'.format(job_name, linter.short_code,
37
+                                                linter.description)
38
             if text is not None:
39
                 output += ': {}'.format(text)
40
             print(output)
41
@@ -82,15 +83,61 @@ def lint_jobs_from_running_jenkins(jenkins_url: str, jenkins_username: str,
42
 
43
 @click.group()
44
 @click.option('--conf', type=click.Path(exists=True, dir_okay=False))
45
+@click.option('--select', default=[], metavar='<lint_short_code>',
46
+              type=click.STRING,
47
+              help='Select which linters, by short code, to run. '
48
+                   'A comma separated list is accepted'
49
+                   'If conf option is used then --select is not used.'
50
+                   'Use `list-linters` sub command to view list')
51
+@click.option('--ignore', default=[], metavar='<lint_short_code>',
52
+              type=click.STRING,
53
+              help='Select which linters, by short code, to ignore. '
54
+                   'A comma separated list is accepted'
55
+                   'If conf option is used then --ignore is not used'
56
+                   'Use `list-linters` sub command to view list')
57
 @click.pass_context
58
-def main(ctx: click.Context, conf: Optional[str] = None) -> None:
59
+def main(ctx: click.Context, conf: Optional[str] = None,
60
+         select: Optional[str] = None,
61
+         ignore: Optional[str] = None) -> None:
62
     """jenkins-job-linter: check your Jenkins jobs for common errors."""
63
     config = ConfigParser()
64
     if conf is not None:
65
         config.read(conf)
66
+    else:
67
+        if select:
68
+            config.add_section('job_linter')
69
+            only_run = []
70
+            for linter_short_code in select.split(','):
71
+                if linter_short_code in LINTER_SHORT_CODES:
72
+                    only_run.append(LINTER_SHORT_CODES[linter_short_code])
73
+
74
+            config.set('job_linter', 'only_run', ','.join(only_run))
75
+        if ignore:
76
+            config.add_section('job_linter')
77
+            disable_linters = []
78
+            for linter_short_code in ignore.split(','):
79
+                if linter_short_code in LINTER_SHORT_CODES:
80
+                    disable_linters.append(
81
+                            LINTER_SHORT_CODES[linter_short_code])
82
+
83
+            config.set('job_linter', 'disable_linters',
84
+                       ','.join(disable_linters))
85
+
86
     ctx.obj = config
87
 
88
 
89
+@main.command(name='list-linters')
90
+@click.pass_context
91
+def list_linters(ctx: click.Context) -> None:
92
+    """List linters."""
93
+    for linter_name, linter in LINTERS.items():
94
+        print('* {}'.format(linter_name))
95
+        print('\tShort code:\n\t\t{}'.format(linter.short_code))
96
+        print('\tDescription:\n\t\t{}'.format(linter.description))
97
+        if linter.default_config:
98
+            print('\tDefault Config:\n\t\t{}'.format(linter.default_config))
99
+
100
+
101
 @main.command(name='lint-directory')
102
 @click.argument('compiled_job_directory',
103
                 type=click.Path(exists=True, file_okay=False))
104
diff --git a/jenkins_job_linter/linters.py b/jenkins_job_linter/linters.py
105
index f69eeddc5c1f1fd7f3ae6df09cff3757c9a90849..c2d055b9771d11734205cd31f644b568e907c51a 100644
106
--- a/jenkins_job_linter/linters.py
107
+++ b/jenkins_job_linter/linters.py
108
@@ -41,6 +41,7 @@ class Linter:
109
     """A super-class capturing the common linting pattern."""
110
 
111
     default_config = {}  # type: Dict[str, Any]
112
+    short_code = None  # type: bool
113
 
114
     def __init__(self, ctx: LintContext) -> None:
115
         """
116
@@ -87,6 +88,7 @@ class ListViewLinter(Linter):
117
 class EnsureTimestamps(JobLinter):
118
     """Ensure that a job is configured with timestamp output."""
119
 
120
+    short_code = 'L001'
121
     description = 'checking for timestamps'
122
     _xpath = (
123
         './buildWrappers/hudson.plugins.timestamper.TimestamperBuildWrapper')
124
@@ -102,6 +104,7 @@ class EnsureTimestamps(JobLinter):
125
 class EnsureWorkspaceCleanup(JobLinter):
126
     """Ensure that a job workspace is cleaned before execution."""
127
 
128
+    short_code = 'L002'
129
     description = 'checking for workspace cleanup'
130
     _xpath = (
131
         './buildWrappers/hudson.plugins.ws__cleanup.PreBuildCleanup')
132
@@ -117,6 +120,7 @@ class EnsureWorkspaceCleanup(JobLinter):
133
 class CheckEnvInject(JobLinter):
134
     """Ensure that required environment variables are injected."""
135
 
136
+    short_code = 'L003'
137
     default_config = {
138
         'required_environment_settings': '',
139
     }
140
@@ -160,6 +164,7 @@ class CheckEnvInject(JobLinter):
141
 class CheckJobReferences(JobLinter):
142
     """Ensure that jobs referenced for triggering exist."""
143
 
144
+    short_code = 'L004'
145
     description = 'checking job references'
146
     _xpath = (
147
         './builders/hudson.plugins.parameterizedtrigger.TriggerBuilder/configs'
148
@@ -183,6 +188,7 @@ class CheckJobReferences(JobLinter):
149
 class CheckColumnConfiguration(ListViewLinter):
150
     """Ensure that each list view has at least one column configured."""
151
 
152
+    short_code = 'L005'
153
     description = 'checking column configuration'
154
     _xpath = './columns/*'
155
 
156
@@ -197,6 +203,7 @@ class CheckColumnConfiguration(ListViewLinter):
157
 class ShellBuilderLinter(JobLinter):
158
     """A linter that operates on the shell builders of jobs."""
159
 
160
+    short_code = 'L006'
161
     _xpath = './builders/hudson.tasks.Shell/command'
162
 
163
     def actual_check(self) -> LintCheckResult:
164
@@ -225,6 +232,7 @@ class ShellBuilderLinter(JobLinter):
165
 class CheckForEmptyShell(ShellBuilderLinter):
166
     """Ensure that shell builders in a job have some content."""
167
 
168
+    short_code = 'L006'
169
     description = 'checking shell builder shell scripts are not empty'
170
 
171
     def shell_check(self, shell_script: Optional[str]) -> Tuple[LintResult,
172
@@ -245,6 +253,7 @@ class CheckShebang(ShellBuilderLinter):
173
     Shell builders with no shebang or a non-shell shebang are skipped.
174
     """
175
 
176
+    short_code = 'L007'
177
     default_config = {
178
         'allow_default_shebang': True,
179
         'required_shell_options': 'eux',
180
@@ -296,3 +305,5 @@ class CheckShebang(ShellBuilderLinter):
181
 
182
 extension_manager = ExtensionManager(namespace='jjl.linters')
183
 LINTERS = {ext.name: ext.plugin for ext in extension_manager}
184
+LINTER_SHORT_CODES = {linter.short_code: linter_name
185
+                      for linter_name, linter in LINTERS.items()}
186
-- 
187
2.25.1
Loading...