Commit 18194044 authored by Florian Muellner's avatar Florian Muellner Committed by Benjamin Tissoires
Browse files

ci-fairy: Support checking commits for required/disallowed patterns



In mutter and gnome-shell, we use a custom script to check that commit
messages conform to a particular pattern/style. This seems generally
useful, so add the option to provide additional patterns in a YAML file.
Signed-off-by: Florian Muellner's avatarFlorian Müllner <fmuellner@gnome.org>
parent 2b3d05bc
Pipeline #246059 passed with stages
in 1 minute and 37 seconds
......@@ -154,7 +154,7 @@ ci-fairy images:
.ci-fairy-tag:
variables:
FDO_DISTRIBUTION_TAG: sha256-936060119966b261c07af69d9422ee970cb2f37a355ba8509dceeaee70633349
FDO_DISTRIBUTION_TAG: sha256-754a2c72cf4a7e258ba19bc895ad749ddac120842b7121355d9a3586cb9ea942
.ci-fairy-local-image:
extends:
......@@ -292,7 +292,7 @@ test published images:
script:
- skopeo inspect docker://quay.io/freedesktop.org/ci-templates:buildah-aarch64-2020-12-18.2
- skopeo inspect docker://quay.io/freedesktop.org/ci-templates:buildah-x86_64-2020-12-18.2
- skopeo inspect docker://quay.io/freedesktop.org/ci-templates:ci-fairy-sha256-936060119966b261c07af69d9422ee970cb2f37a355ba8509dceeaee70633349
- skopeo inspect docker://quay.io/freedesktop.org/ci-templates:ci-fairy-sha256-754a2c72cf4a7e258ba19bc895ad749ddac120842b7121355d9a3586cb9ea942
- skopeo inspect docker://quay.io/freedesktop.org/ci-templates:qemu-base-x86_64-2020-12-18.2
- skopeo inspect docker://quay.io/freedesktop.org/ci-templates:qemu-mkosi-base-x86_64-2020-12-18.2
rules:
......
......@@ -23,7 +23,7 @@ ci-fairy-base-image:
.ci-fairy-tag:
variables:
FDO_DISTRIBUTION_TAG: sha256-936060119966b261c07af69d9422ee970cb2f37a355ba8509dceeaee70633349
FDO_DISTRIBUTION_TAG: sha256-754a2c72cf4a7e258ba19bc895ad749ddac120842b7121355d9a3586cb9ea942
# The actual ci-fairy image with ci-fairy installed
# This image uses the sha of the ci-fairy script itself as tag.
......
......@@ -264,6 +264,20 @@ some cosmetic requirements. Other options to check for is whether a
a line is **not** included (``--no-signed-off-by``). See the ``--help``
output for a list fo checks available.
It is possible to provide custom rule definitions in YAML format, either in
``.gitlab-ci/commitrules.yml`` if it exists, or by specifying a file with the
``--rules-file`` option. An example rules files:
.. code-block:: yaml
patterns:
require:
- regex: ($CI_MERGE_REQUEST_PROJECT_URL/(-/)?(issues|merge_requests)/[0-9]+)
message: Commit message must contain a link to an issue or merge request
deny:
- regex: '^\S*\.[ch]:'
message: Commit message subject prefix should not include .c, .h etc.
where: subject
``ci-fairy`` uses the CI environment where available and will do the right
thing without requiring options. It does need the ``$FDO_UPSTREAM_REPO``
variable to be set to the upstream project's full name. An example
......
......@@ -31,6 +31,6 @@
# Variables provided by this template should be considered read-only.
#
.fdo.ci-fairy:
image: quay.io/freedesktop.org/ci-templates:ci-fairy-sha256-936060119966b261c07af69d9422ee970cb2f37a355ba8509dceeaee70633349
image: quay.io/freedesktop.org/ci-templates:ci-fairy-sha256-754a2c72cf4a7e258ba19bc895ad749ddac120842b7121355d9a3586cb9ea942
variables:
FDO_DISTRIBUTION_IMAGE: quay.io/freedesktop.org/ci-templates:ci-fairy-sha256-936060119966b261c07af69d9422ee970cb2f37a355ba8509dceeaee70633349
\ No newline at end of file
FDO_DISTRIBUTION_IMAGE: quay.io/freedesktop.org/ci-templates:ci-fairy-sha256-754a2c72cf4a7e258ba19bc895ad749ddac120842b7121355d9a3586cb9ea942
\ No newline at end of file
......@@ -15,6 +15,7 @@ import json
import fnmatch
import logging
import os
import re
import shutil
import sys
import time
......@@ -220,6 +221,27 @@ class GitCommitValidator(object):
if not is_revert:
self.__error(f'Commit message subject must not exceed {tw} characters')
def _match_pattern_rule(self, subject, rule):
if rule.get('where', 'message') == 'subject':
msg = subject
else:
msg = self.commit.message
regex = str(rule['regex'])
return re.search(regex, msg, re.MULTILINE)
def check_patterns(self, rules):
subject = self.commit.message.split('\n')[0]
patterns = rules.get('patterns', {})
for rule in patterns.get('require', []):
if self._match_pattern_rule(subject, rule) is None:
self.__error(rule.get('message', f'Required pattern not found: {rule["regex"]}'))
for rule in patterns.get('deny', []):
if self._match_pattern_rule(subject, rule) is not None:
self.__error(rule.get('message', f'Disallowed pattern found: {rule["regex"]}'))
@property
def failed(self):
return bool(self.errors)
......@@ -1011,9 +1033,11 @@ def generate_template(ctx, config, root, template, output_file, verify):
help='Require a Signed-off-by tag to be (or not be) present')
@click.option('--textwidth', type=int, default=80,
help='Require the commit message subject to be less than N characters wide.')
@click.option('--rules-file', type=click.File(), required=False,
help='Custom rules definitions in YAML format')
@click.option('--junit-xml', help='junit output file to write to')
@click.pass_context
def check_commits(ctx, commit_range, branch, signed_off_by, textwidth, junit_xml):
def check_commits(ctx, commit_range, branch, signed_off_by, textwidth, rules_file, junit_xml):
'''
Check a commit range for some properties. Some checks are always
performed, others can be changed with commandline arguments.
......@@ -1061,6 +1085,17 @@ def check_commits(ctx, commit_range, branch, signed_off_by, textwidth, junit_xml
commit_range = f'{remote_prefix}{target_branch}..{sha}'
logger.debug(f'Using commit range {commit_range}')
if rules_file is None:
default_rules = Path('.gitlab-ci/commitrules.yml')
if default_rules.exists():
rules_file = default_rules.open()
if rules_file is not None:
yml = os.path.expandvars(rules_file.read())
rules = yaml.safe_load(yml)
else:
rules = None
failures = []
for commit in repo.iter_commits(commit_range):
validator = GitCommitValidator(commit)
......@@ -1069,6 +1104,8 @@ def check_commits(ctx, commit_range, branch, signed_off_by, textwidth, junit_xml
validator.check_sob(signed_off_by)
if textwidth > 0:
validator.check_text_width(textwidth)
if rules is not None:
validator.check_patterns(rules)
if validator.failed:
failures.append(validator)
......
......@@ -834,6 +834,15 @@ def test_commits_sob(caplog, gitlab_default_env):
assert 'Missing "Signed-off-by: author information"' in ''.join(caplog.messages)
FORMAT_RULES = '''
patterns:
deny:
- regex: '^[^:]*: [a-z]'
message: Commit message subject should be properly Capitalized.
where: subject
'''
def test_commits_msgformat(caplog, gitlab_default_env):
env = gitlab_default_env
env['CI'] = ''
......@@ -915,6 +924,29 @@ def test_commits_msgformat(caplog, gitlab_default_env):
# Revert line may have a longer line than allowed
assert result.exit_code == 0
b = repo.create_head('mybranch5', 'master')
repo.head.reference = b
repo.head.reset(index=True, working_tree=True)
with open('test', 'w') as fd:
fd.write('bar')
repo.index.add(['test'])
repo.index.commit('foo: bar')
result = runner.invoke(ci_fairy.ci_fairy, args + ['--rules-file', '-'], input=FORMAT_RULES)
assert result.exit_code == 1
assert 'Commit message subject should be properly Capitalized.' in ''.join(caplog.messages)
b = repo.create_head('mybranch6', 'master')
repo.head.reference = b
repo.head.reset(index=True, working_tree=True)
with open('test', 'w') as fd:
fd.write('bar')
repo.index.add(['test'])
repo.index.commit('foo: Bar\n\nfoo: bar')
result = runner.invoke(ci_fairy.ci_fairy, args + ['--rules-file', '-'], input=FORMAT_RULES)
assert result.exit_code == 0
def test_commits_gitlabemail(caplog, gitlab_default_env):
env = gitlab_default_env
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment