diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index adbbe21..13c6bea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -52,6 +52,14 @@ stages: image: $FEDORA_IMAGE +verify_signoff: + stage: pre-commit + script: + - git fetch origin master + - python3 contrib/git-signoff.py + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + pre_commit: stage: pre-commit script: diff --git a/contrib/git-signoff.py b/contrib/git-signoff.py new file mode 100755 index 0000000..1b1d028 --- /dev/null +++ b/contrib/git-signoff.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Verify that all commits added on the current branch (compared to +# origin/master) contain a properly formatted Signed-off-by tag. + +from __future__ import annotations + +import re +import subprocess +import sys + +SIGNOFF_RE = re.compile(r"^Signed-off-by: .+ <.+@.+\..+>$") + + +def get_added_commits() -> list[tuple[str, str]]: + result = subprocess.run( + ["git", "log", "--format=%H %s", "origin/master..HEAD"], + capture_output=True, + text=True, + check=True, + ) + commits = [] + for line in result.stdout.strip().splitlines(): + sha, subject = line.split(" ", 1) + commits.append((sha, subject)) + return commits + + +def get_commit_message(sha: str) -> str: + result = subprocess.run( + ["git", "log", "-1", "--format=%B", sha], + capture_output=True, + text=True, + check=True, + ) + return result.stdout + + +def main() -> int: + commits = get_added_commits() + if not commits: + print("No commits found between origin/master and HEAD.") + return 0 + + retval = 0 + for sha, subject in commits: + message = get_commit_message(sha) + has_signoff = any(SIGNOFF_RE.match(line) for line in message.splitlines()) + if not has_signoff: + print( + f"ERROR: Signed-off-by tag cannot be found in commit " + f'{sha[:12]} ("{subject}")' + ) + retval = 1 + + return retval + + +if __name__ == "__main__": + raise SystemExit(main())