From 90ff5a0caec5c7cb96d60900c97e3d0ab901641e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 14 Feb 2020 08:20:27 +1000 Subject: [PATCH] gitlab CI: add a check-commit stage Taken from libinput, checks for signed-off-by and other things. Signed-off-by: Peter Hutterer --- .gitlab-ci.yml | 45 ++++++++++---- .gitlab-ci/check-commit.py | 116 +++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 11 deletions(-) create mode 100755 .gitlab-ci/check-commit.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9f42cac..43e854f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,7 +28,7 @@ include: file: '/templates/centos.yml' stages: - - container_prep # rebuild the container images if there is a change + - prep # rebuild the container images if there is a change - build # for actually building and testing things in a container - VM # for running the test suite in a VM - distro # distribs test @@ -79,6 +79,8 @@ variables: - test/test-suite.log expire_in: 1 week when: on_failure + reports: + junit: junit-*.xml .default_build: &default_build script: @@ -91,10 +93,31 @@ variables: ################################################################# # # -# container prep stage # +# prep stage # # # ################################################################# +check-commit: + image: golang:alpine + stage: prep + before_script: + - apk add python3 git + script: + - pip3 install GitPython + - pip3 install pytest + - | + pytest --junitxml=results.xml \ + --tb=line \ + --assert=plain \ + ./.gitlab-ci/check-commit.py + except: + - master@libinput/libinput + variables: + GIT_DEPTH: 100 + artifacts: + reports: + junit: results.xml + .pull_upstream_or_rebuild: &pull_upstream_or_rebuild before_script: # log in to the registry @@ -117,7 +140,7 @@ variables: fedora:30@container-prep: extends: .fedora@container-build - stage: container_prep + stage: prep variables: GIT_STRATEGY: none FEDORA_VERSION: 30 @@ -128,7 +151,7 @@ fedora:30@container-prep: fedora:31@container-prep: extends: .fedora@container-build - stage: container_prep + stage: prep variables: GIT_STRATEGY: none FEDORA_VERSION: 31 @@ -140,7 +163,7 @@ fedora:31@container-prep: ubuntu:19.10@container-prep: extends: .ubuntu@container-build - stage: container_prep + stage: prep variables: GIT_STRATEGY: none UBUNTU_VERSION: "19.10" @@ -151,7 +174,7 @@ ubuntu:19.10@container-prep: ubuntu:19.04@container-prep: extends: .ubuntu@container-build - stage: container_prep + stage: prep variables: GIT_STRATEGY: none UBUNTU_VERSION: "19.04" @@ -162,7 +185,7 @@ ubuntu:19.04@container-prep: debian:stable@container-prep: extends: .debian@container-build - stage: container_prep + stage: prep variables: GIT_STRATEGY: none DEBIAN_VERSION: stable @@ -173,7 +196,7 @@ debian:stable@container-prep: debian:sid@container-prep: extends: .debian@container-build - stage: container_prep + stage: prep variables: GIT_STRATEGY: none DEBIAN_VERSION: sid @@ -184,7 +207,7 @@ debian:sid@container-prep: .centos@container-prep: extends: .centos@container-build - stage: container_prep + stage: prep variables: GIT_STRATEGY: none CENTOS_VERSION: 7 @@ -205,7 +228,7 @@ centos:8@container-prep: arch:rolling@container-prep: extends: .arch@container-build - stage: container_prep + stage: prep variables: GIT_STRATEGY: none ARCH_VERSION: rolling @@ -216,7 +239,7 @@ arch:rolling@container-prep: alpine:latest@container-prep: extends: .alpine@container-build - stage: container_prep + stage: prep variables: GIT_STRATEGY: none ALPINE_VERSION: latest diff --git a/.gitlab-ci/check-commit.py b/.gitlab-ci/check-commit.py new file mode 100755 index 0000000..b533bc4 --- /dev/null +++ b/.gitlab-ci/check-commit.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# vim: set expandtab shiftwidth=4: +# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */ +# +# This script tests a few things against the commit messages, search for +# `def test_` to see the actual tests run. + +import git +import os +import pytest + +if os.environ.get('CI'): + # Environment variables set by gitlab + CI_COMMIT_SHA = os.environ['CI_COMMIT_SHA'] + # This is intentionally hardcoded to master. CI_MERGE_REQUEST_TARGET_BRANCH_NAME + # is only available when run with only: [merge_requests] + # but that generates a detached pipeline with only this job in it. + # Since merging into a non-master branch is not a thing in libevdev + # anyway, we can hardcode this here. + CI_MERGE_REQUEST_TARGET_BRANCH_NAME = 'master' + CI_SERVER_HOST = os.environ['CI_SERVER_HOST'] + UPSTREAM = 'upstream' +else: + # Local emulation mode when called directly + import argparse + + parser = argparse.ArgumentParser(description='Commit message checker - local emulation mode') + parser.add_argument('--sha', help='The commit message to start at (default: HEAD}', + default='HEAD') + parser.add_argument('--branch', help='The branch name to merge to (default: master)', + default='master') + parser.add_argument('--remote', help='The remote name (default: origin)', + default='origin') + args = parser.parse_args() + + CI_COMMIT_SHA = args.sha + CI_MERGE_REQUEST_TARGET_BRANCH_NAME = args.branch + CI_SERVER_HOST = None + UPSTREAM = 'origin' + print(f'Running in local testing mode.') + +print(f'Merging {CI_COMMIT_SHA} into {CI_MERGE_REQUEST_TARGET_BRANCH_NAME}') + +# We need to add the real libevdev as remote, our origin here is the user's +# fork. +repo = git.Repo('.') +if UPSTREAM not in repo.remotes: + upstream = repo.create_remote('upstream', f'https://{CI_SERVER_HOST}/libevdev/libevdev.git') + upstream.fetch() + +sha = CI_COMMIT_SHA +branch = CI_MERGE_REQUEST_TARGET_BRANCH_NAME + +commits = list(repo.iter_commits(f'{UPSTREAM}/{branch}..{sha}')) + + +def error(commit, message, long_message=''): + info = ('After correcting the above issue(s), force-push to the same branch.\n' + 'This will re-trigger the CI.\n\n' + 'A list of requirements for commit messages is available at\n' + 'https://gitlab.freedesktop.org/libevdev/libevdev/blob/master/CODING_STYLE.md') + + msg = (f'\n' + f'Commit message check failed: {message}\n\n' + f' commit: {str(commit)}\n' + f' author: {commit.author.name} <{commit.author.email}>\n' + f'\n' + f' {commit.summary}\n' + f'\n' + f'\n' + f'{long_message}\n\n' + f'{info}\n\n') + return msg + + +@pytest.mark.parametrize('commit', commits) +class TestCommits: + def test_author_email(self, commit): + assert '@users.noreply.gitlab.freedesktop.org' not in commit.author.email, \ + error(commit, 'git author email invalid', + ('Please set your name and email with the commands\n', + ' git config --global user.name Your Name\n' + ' git config --global user.email your.email@provider.com\n')) + + def test_signed_off_by(self, commit): + if not commit.message.startswith('Revert "'): + assert 'Signed-off-by:' in commit.message, \ + error(commit, 'missing Signed-off-by tag', + 'Please add the required "Signed-off-by: author information" line\n' + 'to the commit message') + + def test_fixup(self, commit): + assert not commit.message.startswith('fixup!'), \ + error(commit, 'Remove fixup! tag', + 'Leftover "fixup!" commit message detected, please squash') + assert not commit.message.startswith('squash!'), \ + error(commit, 'Remove squash! tag', + 'Leftover "squash!" commit message detected, please squash') + + def test_line_length(self, commit): + lines = commit.message.split('\n') + first_line = lines[0] + + assert len(first_line) < 85, \ + error(commit, 'Commit message subject line too long') + + try: + second_line = lines[1] + assert second_line == '', \ + error(commit, 'Second line in commit message must be emtpy') + except IndexError: + pass + + +if __name__ == '__main__': + pytest.main([__file__])