golangci-lint: add job
This patch adds a roles and jobs to run golangci-lint against a a Golang project. It's a very popular tool for linting go code. It also adds a simple framework which allows us to create dynamic tests for file comments by defining a simple YAML file. Change-Id: Ic8358541adaf7c3279383f0279cd3da7b446a6e0
This commit is contained in:
parent
7e2b931332
commit
23236c12fa
@ -3,3 +3,4 @@ Go Jobs
|
||||
|
||||
.. zuul:autojob:: golang-go
|
||||
.. zuul:autojob:: golang-go-test
|
||||
.. zuul:autojob:: golangci-lint
|
||||
|
@ -1,5 +1,7 @@
|
||||
Go Roles
|
||||
========
|
||||
|
||||
.. zuul:autorole:: ensure-golangci-lint
|
||||
.. zuul:autorole:: install-go
|
||||
.. zuul:autorole:: go
|
||||
.. zuul:autorole:: golangci-lint
|
||||
|
5
playbooks/golangci-lint/pre.yaml
Normal file
5
playbooks/golangci-lint/pre.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
- hosts: all
|
||||
roles:
|
||||
- install-go
|
||||
- ensure-golangci-lint
|
4
playbooks/golangci-lint/run.yaml
Normal file
4
playbooks/golangci-lint/run.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
- hosts: all
|
||||
roles:
|
||||
- golangci-lint
|
7
roles/ensure-golangci-lint/README.rst
Normal file
7
roles/ensure-golangci-lint/README.rst
Normal file
@ -0,0 +1,7 @@
|
||||
Ensure golangci-lint is installed
|
||||
|
||||
**Role Variables**
|
||||
|
||||
.. zuul:rolevar:: golangci_lint_version
|
||||
|
||||
Version of golangci-lint to install
|
5
roles/ensure-golangci-lint/defaults/main.yaml
Normal file
5
roles/ensure-golangci-lint/defaults/main.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
# NOTE(mnaser): We are pinning to 1.23.8 due to the fact that at the time of
|
||||
# writing this role, 1.24.0 has memory issues
|
||||
# https://github.com/golangci/golangci-lint/issues/994
|
||||
golangci_lint_version: 1.23.8
|
9
roles/ensure-golangci-lint/tasks/main.yaml
Normal file
9
roles/ensure-golangci-lint/tasks/main.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
- name: Install golangci-lint
|
||||
become: true
|
||||
unarchive:
|
||||
remote_src: true
|
||||
src: "https://github.com/golangci/golangci-lint/releases/download/v{{ golangci_lint_version }}/golangci-lint-{{ golangci_lint_version }}-linux-amd64.tar.gz"
|
||||
dest: /usr/local/bin
|
||||
extra_opts:
|
||||
- --strip-components=1
|
12
roles/golangci-lint/README.rst
Normal file
12
roles/golangci-lint/README.rst
Normal file
@ -0,0 +1,12 @@
|
||||
Run golangci-lint
|
||||
|
||||
**Role Variables**
|
||||
|
||||
.. zuul:rolevar:: zuul_work_dir
|
||||
:default: {{ zuul.project.src_dir }}
|
||||
|
||||
The location of the main working directory of the job.
|
||||
|
||||
.. zuul:rolevar:: golangci_lint_options
|
||||
|
||||
Arguments passed to golangci-lint
|
0
roles/golangci-lint/__init__.py
Normal file
0
roles/golangci-lint/__init__.py
Normal file
4
roles/golangci-lint/defaults/main.yaml
Normal file
4
roles/golangci-lint/defaults/main.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
zuul_work_dir: "{{ zuul.project.src_dir }}"
|
||||
golangci_lint_options: ""
|
||||
go_install_dir: "/usr/local"
|
||||
go_bin_path: "{{ go_install_dir }}/go/bin"
|
0
roles/golangci-lint/library/__init__.py
Normal file
0
roles/golangci-lint/library/__init__.py
Normal file
87
roles/golangci-lint/library/golangci_lint_parse_output.py
Normal file
87
roles/golangci-lint/library/golangci_lint_parse_output.py
Normal file
@ -0,0 +1,87 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Copyright 2020 VEXXHOST, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: golangci_lint_parse_output
|
||||
short_description: Parse the output of golangci-lint and return comments
|
||||
author: Mohammed Naser (@mnaser)
|
||||
description:
|
||||
- Parse the output of golangci-lint and return content for inline comments.
|
||||
requirements:
|
||||
- "python >= 3.5"
|
||||
options:
|
||||
workdir:
|
||||
description:
|
||||
- Path for the project to strip for comments
|
||||
required: true
|
||||
type: str
|
||||
output:
|
||||
description:
|
||||
- Output from the golangci-lint command run
|
||||
required: true
|
||||
type: str
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
BUILD_RE = re.compile(r'^.*\[(.*)\]"$')
|
||||
RE = re.compile(r"^(.*):(\d+):\d+: (.*)$")
|
||||
|
||||
|
||||
def parse_output(output, workdir):
|
||||
comments = {}
|
||||
for line in output.split('\n'):
|
||||
# If we have a build failure, we need to match that first and extract
|
||||
# the error message. We'll also need to remove 'workdir' as it
|
||||
# contains the full path.
|
||||
m = BUILD_RE.match(line)
|
||||
if m:
|
||||
line = re.sub(r'\\(.)', r'\1', m.group(1))
|
||||
# We find everything up until workdir, strip the length of workdir
|
||||
# and then remove one extra character to remove leading slash.
|
||||
index = line.find(workdir) + len(workdir) + 1
|
||||
line = line[index:].replace(workdir, '')
|
||||
m = RE.match(line)
|
||||
if m:
|
||||
file_path = m.group(1)
|
||||
start_line = m.group(2)
|
||||
message = m.group(3)
|
||||
|
||||
comments.setdefault(file_path, [])
|
||||
comments[file_path].append(dict(line=int(start_line),
|
||||
message=message))
|
||||
return comments
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
output=dict(required=True, type='str', no_log=True),
|
||||
workdir=dict(required=True, str='str'),
|
||||
)
|
||||
)
|
||||
|
||||
comments = parse_output(module.params['output'], module.params['workdir'])
|
||||
module.exit_json(changed=False, comments=comments)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
11
roles/golangci-lint/library/test-cases/failed_build.yaml
Normal file
11
roles/golangci-lint/library/test-cases/failed_build.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
workdir: src/opendev.org/vexxhost/openstack-operator
|
||||
output: |
|
||||
level=warning msg="[runner] Can't run linter unused: buildssa: analysis skipped: errors in package: [/home/zuul/src/opendev.org/vexxhost/openstack-operator/builders/pod_metrics_endpoint.go:4:2: \"k8s.io/apimachinery/pkg/util/intstr\" imported but not used]"
|
||||
level=warning msg="[runner] Can't run linter goanalysis_metalinter: S1009: failed prerequisites: inspect@opendev.org/vexxhost/openstack-operator/builders, isgenerated@opendev.org/vexxhost/openstack-operator/builders"
|
||||
level=error msg="Running error: S1009: failed prerequisites: inspect@opendev.org/vexxhost/openstack-operator/builders, isgenerated@opendev.org/vexxhost/openstack-operator/builders"
|
||||
|
||||
comments:
|
||||
builders/pod_metrics_endpoint.go:
|
||||
- line: 4
|
||||
message: '"k8s.io/apimachinery/pkg/util/intstr" imported but not used'
|
11
roles/golangci-lint/library/test-cases/multiple_lines.yaml
Normal file
11
roles/golangci-lint/library/test-cases/multiple_lines.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
workdir: src/opendev.org/vexxhost/openstack-operator
|
||||
output: |
|
||||
builders/pod_metrics_endpoint.go:27:2: SA1019: pme.obj.TargetPort is deprecated: Use 'port' instead. (staticcheck)
|
||||
pme.obj.TargetPort = &targetPort
|
||||
^
|
||||
|
||||
comments:
|
||||
builders/pod_metrics_endpoint.go:
|
||||
- line: 27
|
||||
message: "SA1019: pme.obj.TargetPort is deprecated: Use 'port' instead. (staticcheck)"
|
5
roles/golangci-lint/library/test-cases/no_output.yaml
Normal file
5
roles/golangci-lint/library/test-cases/no_output.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
workdir: src/opendev.org/vexxhost/openstack-operator
|
||||
output: ""
|
||||
|
||||
comments: {}
|
28
roles/golangci-lint/library/test-cases/single_line.yaml
Normal file
28
roles/golangci-lint/library/test-cases/single_line.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
workdir: src/opendev.org/vexxhost/openstack-operator
|
||||
output: |
|
||||
builders/pod_metrics_endpoint.go:27:2: SA1019: pme.obj.TargetPort is deprecated: Use 'port' instead. (staticcheck)
|
||||
pme.obj.TargetPort = &targetPort
|
||||
^
|
||||
controllers/mcrouter_controller.go:133:15: S1039: unnecessary use of fmt.Sprintf (gosimple)
|
||||
Name: fmt.Sprintf("mcrouter-podmonitor"),
|
||||
^
|
||||
controllers/mcrouter_controller.go:163:15: S1039: unnecessary use of fmt.Sprintf (gosimple)
|
||||
Name: fmt.Sprintf("mcrouter-alertrule"),
|
||||
^
|
||||
controllers/memcached_controller.go:130:15: S1039: unnecessary use of fmt.Sprintf (gosimple)
|
||||
Name: fmt.Sprintf("memcached-podmonitor"),
|
||||
|
||||
comments:
|
||||
builders/pod_metrics_endpoint.go:
|
||||
- line: 27
|
||||
message: "SA1019: pme.obj.TargetPort is deprecated: Use 'port' instead. (staticcheck)"
|
||||
controllers/mcrouter_controller.go:
|
||||
- line: 133
|
||||
message: "S1039: unnecessary use of fmt.Sprintf (gosimple)"
|
||||
- line: 163
|
||||
message: "S1039: unnecessary use of fmt.Sprintf (gosimple)"
|
||||
controllers/memcached_controller.go:
|
||||
- line: 130
|
||||
message: "S1039: unnecessary use of fmt.Sprintf (gosimple)"
|
||||
|
@ -0,0 +1,33 @@
|
||||
# Copyright (C) 2020 VEXXHOST, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
#
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
|
||||
import testtools
|
||||
|
||||
from tests import generate_dynamic_comments_tests
|
||||
from .golangci_lint_parse_output import parse_output
|
||||
|
||||
TESTS_DIR = os.path.join(os.path.dirname(__file__),
|
||||
'test-cases')
|
||||
|
||||
|
||||
class TestGolangciLintParseOutput(testtools.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
generate_dynamic_comments_tests(TestGolangciLintParseOutput, TESTS_DIR,
|
||||
parse_output)
|
27
roles/golangci-lint/tasks/main.yaml
Normal file
27
roles/golangci-lint/tasks/main.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
- name: Run golangci-lint
|
||||
command: "golangci-lint run {{ golangci_lint_options }}"
|
||||
args:
|
||||
chdir: "{{ zuul_work_dir }}"
|
||||
environment:
|
||||
PATH: "{{ ansible_env.PATH }}:{{ go_bin_path }}"
|
||||
ignore_errors: true
|
||||
register: _golangci_lint
|
||||
|
||||
- name: Look for output
|
||||
golangci_lint_parse_output:
|
||||
workdir: '{{ zuul_work_dir }}'
|
||||
output: '{{ _golangci_lint.stdout }}'
|
||||
register: _golangci_lint_parse_output
|
||||
|
||||
- name: Return file comments to Zuul
|
||||
when: _golangci_lint_parse_output.comments
|
||||
delegate_to: localhost
|
||||
zuul_return:
|
||||
data:
|
||||
zuul:
|
||||
file_comments: '{{ _golangci_lint_parse_output.comments }}'
|
||||
|
||||
- name: Return golangci-lint status
|
||||
fail:
|
||||
msg: 'golangci-lint exited with return code {{ _golangci_lint.rc }}'
|
||||
when: _golangci_lint.rc != 0
|
@ -0,0 +1,35 @@
|
||||
# Copyright (C) 2020 VEXXHOST, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
#
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
def generate_dynamic_comments_tests(cls, test_path, func):
|
||||
def _create_test_using_file(name):
|
||||
def test(self):
|
||||
path = "%s/%s" % (test_path, name)
|
||||
with open(path) as fd:
|
||||
data = yaml.load(fd, Loader=yaml.FullLoader)
|
||||
comments = func(data['output'], data['workdir'])
|
||||
self.assertEqual(data['comments'], comments)
|
||||
return test
|
||||
|
||||
for t in os.listdir(test_path):
|
||||
test = _create_test_using_file(t)
|
||||
test.__name__ = "test_%s" % t.split('.')[0]
|
||||
setattr(cls, test.__name__, test)
|
@ -90,3 +90,11 @@
|
||||
Path to operate in.
|
||||
vars:
|
||||
go_command: test
|
||||
|
||||
- job:
|
||||
name: golangci-lint
|
||||
parent: unittests
|
||||
description: |
|
||||
Run golangci-lint on a Go project
|
||||
pre-run: playbooks/golangci-lint/pre.yaml
|
||||
run: playbooks/golangci-lint/run.yaml
|
||||
|
Loading…
Reference in New Issue
Block a user