From 0eb892fd74e3e129ca308393545e9f82b87dbaf3 Mon Sep 17 00:00:00 2001 From: Nikolay Martyanov Date: Tue, 27 Aug 2024 02:30:46 +0200 Subject: [PATCH] github: Add commit message linting workflow. Introduced a GitHub Actions workflow (`commit-messages.yml`) to lint commit messages on `master`, version branches, and stable branches. Added `check_commit_messages.py` script to validate commit messages against recommended format (subject, empty line, body). Signed-off-by: Nikolay Martyanov --- .github/workflows/commit-messages.yml | 44 +++++++++++ tools/check-commit-messages/.gitignore | 1 + .../check_commit_messages.py | 78 +++++++++++++++++++ tools/check-commit-messages/requirements.txt | 3 + 4 files changed, 126 insertions(+) create mode 100644 .github/workflows/commit-messages.yml create mode 100644 tools/check-commit-messages/.gitignore create mode 100644 tools/check-commit-messages/check_commit_messages.py create mode 100644 tools/check-commit-messages/requirements.txt diff --git a/.github/workflows/commit-messages.yml b/.github/workflows/commit-messages.yml new file mode 100644 index 0000000000..f70cdbea7d --- /dev/null +++ b/.github/workflows/commit-messages.yml @@ -0,0 +1,44 @@ +# Copyright (c) 2024, Zededa, Inc. +# SPDX-License-Identifier: Apache-2.0 +--- +name: Lint Commit Messages + +on: # yamllint disable-line rule:truthy + push: + branches: + - "master" + - "[0-9]+.[0-9]+" + - "[0-9]+.[0-9]+-stable" + pull_request: + branches: + - "master" + - "[0-9]+.[0-9]+" + - "[0-9]+.[0-9]+-stable" + +jobs: + commitlint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.base_ref }} + fetch-depth: 0 + + - name: Fetch the PR's head ref + run: | + git fetch origin ${{ github.event.pull_request.head.sha }}:${{ github.event.pull_request.head.ref }} + git checkout ${{ github.event.pull_request.head.ref }} + + - name: Create virtual environment + run: | + cd tools/check-commit-messages + python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt + + - name: Lint commit messages + run: | + cd tools/check-commit-messages + source .venv/bin/activate + python check_commit_messages.py ${{ github.event.pull_request.base.sha }} diff --git a/tools/check-commit-messages/.gitignore b/tools/check-commit-messages/.gitignore new file mode 100644 index 0000000000..5ceb3864c2 --- /dev/null +++ b/tools/check-commit-messages/.gitignore @@ -0,0 +1 @@ +venv diff --git a/tools/check-commit-messages/check_commit_messages.py b/tools/check-commit-messages/check_commit_messages.py new file mode 100644 index 0000000000..ff7ebad26f --- /dev/null +++ b/tools/check-commit-messages/check_commit_messages.py @@ -0,0 +1,78 @@ +""" +Copyright (c) 2024, Zededa Inc. +SPDX-License-Identifier: Apache-2.0 + +This script checks the commit messages in the current branch to ensure they follow the +recommended format. The recommended format is as follows: +* Commit message should have a subject and body +* An empty line should separate the subject and body +* Body should start with a capital letter +* The body should not be empty (it should contain more than just "Signed-off-by") +""" + +import sys + +import git + +def check_commit_message(commit): + """ + Check if a commit message follows the recommended format. + """ + message_lines = commit.message.strip().splitlines() + + if len(message_lines) < 2: + return False, f"Commit {commit.hexsha} has no body." + + subject = message_lines[0].strip() + second_line = message_lines[1].strip() + + if not subject: + return False, f"Commit {commit.hexsha} has no subject." + + # Check if the second line is empty (indicating a blank line between subject and body) + if second_line: + return False, f"Commit {commit.hexsha} does not have an empty line after the subject." + + # Remove the subject and empty line to get the body + body_lines = [line.strip() for line in message_lines[2:] if line.strip()] + + # Remove lines that are just "Signed-off-by" + body_lines = [line for line in body_lines if not line.lower().startswith("signed-off-by")] + + if not body_lines: + return False, f"Commit {commit.hexsha} has a body but only contains Signed-off-by." + + # Check if the body starts with a capital letter + if not body_lines[0][0].isupper(): + return False, f"Body of commit {commit.hexsha} does not start with a capital letter." + + return True, None + + +def main(): + """ + Main function to check the commit messages in the current branch. + """ + repo = git.Repo(search_parent_directories=True) + + # Base hash should be provided as an argument + base_hash = sys.argv[1] + + commits = list(repo.iter_commits(f'{base_hash}..HEAD')) + + if not commits: + print(f"No commits between {base_hash} and HEAD.") + sys.exit(1) + + print(f"Checking {len(commits)} commits between {base_hash} and HEAD...") + + for commit in commits: + valid, error_message = check_commit_message(commit) + if not valid: + print(error_message) + sys.exit(1) + + print("All commits are valid.") + +if __name__ == "__main__": + main() diff --git a/tools/check-commit-messages/requirements.txt b/tools/check-commit-messages/requirements.txt new file mode 100644 index 0000000000..dba1edf678 --- /dev/null +++ b/tools/check-commit-messages/requirements.txt @@ -0,0 +1,3 @@ +gitdb==4.0.11 +GitPython==3.1.43 +smmap==5.0.1