Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PR] commitcheck (PR #22260)
@ 2026-02-23  2:47 Jun Zhao via ffmpeg-devel
  0 siblings, 0 replies; only message in thread
From: Jun Zhao via ffmpeg-devel @ 2026-02-23  2:47 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jun Zhao

PR #22260 opened by Jun Zhao (mypopydev)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22260
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22260.patch

This patch series adds automated commit message validation:

  1. **tools/check_commit_msg**: add commit message validation script — A POSIX shell script that checks subject format
  (component: description), blank line separation, duplicate Signed-off-by, and squash-mess detection. Supports stdin,
  file argument, and revision range input modes.
  2. **.forgejo/workflows**: add commit message validation CI workflow — Runs the script on every pull request over
  origin/master..HEAD.
  3. **.forgejo/pre-commit**: add local commit-msg hook — Registers the script as a commit-msg hook so developers catch issues
   locally before pushing.

Tested against the most recent 1000 commits on master: 995/1000 passed. The 5 flagged commits are all genuinely
  non-conforming (e.g. **Fix overflow in STSD parser** missing a component prefix, **avutil/dovi_meta - fix ...** using a dash
  instead of a colon, a duplicate Signed-off-by in 677cf95ea4be805fa326adb082eff666a2e790ea, and a missing blank line).


From fc2e44caf1f81b35f673d954e560893ec5d523ab Mon Sep 17 00:00:00 2001
From: Jun Zhao <barryjzhao@tencent.com>
Date: Mon, 23 Feb 2026 09:59:01 +0800
Subject: [PATCH 1/3] tools/check_commit_msg: add commit message validation
 script

Add a shell script that validates commit messages against FFmpeg
conventions. Supports three entry points: stdin (for CI pipelines),
file argument (for pre-commit commit-msg hook), and revision range
(for manual batch checking).

Checks enforced (errors fail CI):
  - Subject matches "component[/module]: description" pattern
  - Blank line between subject and body
  - No duplicate Signed-off-by from same person
  - No multiple subject-like lines in body (squash-mess detection)

Checks that only warn:
  - Subject line > 100 characters
  - Trailing whitespace on subject line

Usage:
  echo "avcodec/vvc: fix pred" | sh tools/check_commit_msg.sh
  sh tools/check_commit_msg.sh .git/COMMIT_EDITMSG
  sh tools/check_commit_msg.sh HEAD~5..HEAD

Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
---
 tools/check_commit_msg.sh | 187 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 187 insertions(+)
 create mode 100755 tools/check_commit_msg.sh

diff --git a/tools/check_commit_msg.sh b/tools/check_commit_msg.sh
new file mode 100755
index 0000000000..7594748727
--- /dev/null
+++ b/tools/check_commit_msg.sh
@@ -0,0 +1,187 @@
+#!/bin/sh
+#
+# Validate commit messages.
+# Exit code 0 = all pass, non-zero = errors found.
+#
+# Usage:
+#   Single message from stdin:
+#     git log -1 --format="%B" <commit> | sh tools/check_commit_msg.sh
+#
+#   Single message from file (pre-commit commit-msg hook):
+#     sh tools/check_commit_msg.sh .git/COMMIT_EDITMSG
+#
+#   Multiple commits via revision range:
+#     sh tools/check_commit_msg.sh HEAD~5..HEAD
+#     sh tools/check_commit_msg.sh origin/master..my-branch
+#     sh tools/check_commit_msg.sh -10          # last 10 commits
+#
+# Examples:
+#   echo "avcodec/vvc: fix intra prediction" | sh tools/check_commit_msg.sh
+#   # => commit message OK
+#
+#   echo "fix bug" | sh tools/check_commit_msg.sh
+#   # => ERROR: subject does not match 'component: description' pattern: fix bug
+#
+# Checks (errors fail CI, warnings only print):
+#   - Subject matches "component[/module]: description" pattern   (error)
+#   - Subject line <= 100 characters                              (warning)
+#   - Blank line between subject and body (if body exists)        (error)
+#   - No trailing whitespace on subject line                      (warning)
+#   - No duplicate Signed-off-by from same person                 (error)
+#   - No multiple subject-like lines (2+) in body (squash-mess)   (error)
+
+RED='\033[1;31m'
+YEL='\033[1;33m'
+RST='\033[0m'
+
+# --- Single-message validation (operates on a temp file) ---
+
+check_message() {
+    msgfile=$1
+    err=0
+    warn_count=0
+
+    error() {
+        printf "${RED}ERROR:${RST} %s\n" "$1" >&2
+        err=1
+    }
+
+    warning() {
+        printf "${YEL}WARNING:${RST} %s\n" "$1" >&2
+        warn_count=$((warn_count + 1))
+    }
+
+    # Check for empty message
+    if ! grep -q '[^[:space:]]' "$msgfile"; then
+        error "commit message is empty"
+        return $err
+    fi
+
+    # Split into subject (first line) and the rest
+    subject=$(head -n 1 "$msgfile")
+
+    # --- Subject checks ---
+
+    # Trailing whitespace on subject (second pattern is a literal tab)
+    case "$subject" in
+        *" "|*"	")
+            warning "trailing whitespace on subject line"
+            ;;
+    esac
+
+    # Subject format: component/module: description
+    # Allow nested paths like avcodec/vvc/inter: or single component like doc:
+    # Also allow "Merge" and "Revert" subjects used by forges
+    # Patterns supported:
+    #   component: desc                           (avcodec/vvc: fix)
+    #   component modifier: desc                  (avcodec/vvc decode: fix)
+    #   component, component: desc                (avformat/a, avcodec/b: fix)
+    #   .component: desc                          (.forgejo/CODEOWNERS: add)
+    #   {component}: desc                         ({lib{a,b}/x86/,}Makefile: fix)
+    # COMP: a component token starting with a letter, at least 2 characters.
+    COMP='[a-zA-Z][]a-zA-Z0-9_./{},*?|()[-]+'
+    case "$subject" in
+        Merge\ *|Revert\ *)
+            # Merge and revert commits get a pass on the component: format
+            ;;
+        *)
+            if ! echo "$subject" | grep -qE "^[{.]?${COMP}(, *${COMP})*( +[a-zA-Z0-9_]+)*: "; then
+                error "subject does not match 'component: description' pattern: $subject"
+            fi
+            ;;
+    esac
+
+    # Subject length
+    subj_len=${#subject}
+    if [ "$subj_len" -gt 100 ]; then
+        warning "subject is $subj_len characters (> 100)"
+    fi
+
+    # --- Blank line between subject and body ---
+    # Use sed to check line 2 directly; works regardless of trailing newline.
+    second_line=$(sed -n '2p' "$msgfile")
+    rest=$(sed -n '2,$p' "$msgfile")
+    if [ -n "$rest" ]; then
+        if [ -n "$second_line" ]; then
+            error "missing blank line between subject and body"
+        fi
+
+        # Body is everything after the blank separator line
+        body=$(tail -n +3 "$msgfile")
+    else
+        body=""
+    fi
+
+    # --- Squash-mess detection ---
+
+    if [ -n "$body" ]; then
+        # Multiple Signed-off-by from the same person
+        sob_dups=$(echo "$body" | grep -i '^Signed-off-by:' | sort | uniq -d)
+        if [ -n "$sob_dups" ]; then
+            error "duplicate Signed-off-by: $(echo "$sob_dups" | head -n 1)"
+        fi
+
+        # Multiple subject-like lines in body: lines matching "component/module: text"
+        # that look like additional commit subjects from a squashed merge.
+        # Require a '/' in the component (e.g. avcodec/vvc:) to distinguish from
+        # prose definitions (e.g. "maximum: No restriction") or data labels.
+        # Exclude known trailer tags.
+        subj_like_count=$(echo "$body" \
+            | grep -E '^[a-zA-Z][a-zA-Z0-9_./-]*/[a-zA-Z0-9_./-]+: [A-Z]' \
+            | grep -ivE '^(Signed-off-by|Reviewed-by|Acked-by|Tested-by|CC|Reported-by|Co-authored-by|Link|Fixes|Note|Suggested-by|Bug):' \
+            | wc -l | tr -d ' ')
+        if [ "$subj_like_count" -ge 2 ]; then
+            error "body contains $subj_like_count subject-like lines (squash-mess?)"
+        fi
+    fi
+
+    if [ $err -eq 0 ] && [ $warn_count -eq 0 ]; then
+        printf "commit message OK\n"
+    fi
+
+    return $err
+}
+
+# --- Main ---
+
+if [ $# -eq 0 ]; then
+    # No argument: read a single message from stdin
+    tmpfile=$(mktemp)
+    trap 'rm -f "$tmpfile"' EXIT
+    cat > "$tmpfile"
+    check_message "$tmpfile"
+    exit $?
+elif [ $# -eq 1 ] && [ -f "$1" ]; then
+    # Single argument is an existing file: treat as commit message file
+    # (used by pre-commit commit-msg hook passing .git/COMMIT_EDITMSG)
+    check_message "$1"
+    exit $?
+else
+    # Argument(s) provided: treat as git revision range
+    revs=$(git log --format=%H "$@" 2>/dev/null) || {
+        printf "${RED}ERROR:${RST} invalid revision range: %s\n" "$*" >&2
+        exit 1
+    }
+    if [ -z "$revs" ]; then
+        printf "no commits in range: %s\n" "$*"
+        exit 0
+    fi
+
+    tmpfile=$(mktemp)
+    trap 'rm -f "$tmpfile"' EXIT
+    total=0
+    failures=0
+
+    for sha in $revs; do
+        total=$((total + 1))
+        printf '\n--- %s: %s ---\n' "$sha" "$(git log -1 --format=%s "$sha")"
+        git log -1 --format="%B" "$sha" > "$tmpfile"
+        if ! check_message "$tmpfile"; then
+            failures=$((failures + 1))
+        fi
+    done
+
+    printf '\n--- Result: %d/%d passed ---\n' "$((total - failures))" "$total"
+    [ "$failures" -eq 0 ]
+    exit $?
+fi
-- 
2.52.0


From ad6401213d8edf43f3943f47409505e027b29b85 Mon Sep 17 00:00:00 2001
From: Jun Zhao <barryjzhao@tencent.com>
Date: Mon, 23 Feb 2026 10:01:52 +0800
Subject: [PATCH 2/3] .forgejo/workflows: add commit message validation CI
 workflow

Add a Forgejo CI workflow that runs check_commit_msg.sh on every
pull request to validate all commit messages in the PR range.

Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
---
 .forgejo/workflows/commitmsg.yml | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 .forgejo/workflows/commitmsg.yml

diff --git a/.forgejo/workflows/commitmsg.yml b/.forgejo/workflows/commitmsg.yml
new file mode 100644
index 0000000000..e6e874ffd2
--- /dev/null
+++ b/.forgejo/workflows/commitmsg.yml
@@ -0,0 +1,19 @@
+name: Commit Messages
+
+on:
+  pull_request:
+
+concurrency:
+  cancel-in-progress: ${{ forge.event_name == 'pull_request' }}
+
+jobs:
+  check:
+    name: Validate
+    runs-on: utilities
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v6
+        with:
+          fetch-depth: 0
+      - name: Check commit messages
+        run: sh tools/check_commit_msg.sh origin/master..HEAD
-- 
2.52.0


From 06567c8d906d366f15e05f27e792cb55ecb7053d Mon Sep 17 00:00:00 2001
From: Jun Zhao <barryjzhao@tencent.com>
Date: Mon, 23 Feb 2026 10:03:39 +0800
Subject: [PATCH 3/3] .forgejo/pre-commit: add local commit-msg hook

Register check_commit_msg.sh as a commit-msg stage hook in the
pre-commit configuration so developers get immediate feedback
on commit message format during `git commit`.

Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
---
 .forgejo/pre-commit/config.yaml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/.forgejo/pre-commit/config.yaml b/.forgejo/pre-commit/config.yaml
index f1ab7765ef..c8de05fadf 100644
--- a/.forgejo/pre-commit/config.yaml
+++ b/.forgejo/pre-commit/config.yaml
@@ -20,6 +20,12 @@ repos:
     - id: trailing-whitespace
 - repo: local
   hooks:
+    - id: check-commit-message
+      name: validate commit message format
+      language: script
+      entry: ./tools/check_commit_msg.sh
+      stages: [commit-msg]
+      always_run: true
     - id: aarch64-asm-indent
       name: fix aarch64 assembly indentation
       files: ^.*/aarch64/.*\.S$
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-02-23 15:23 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-02-23  2:47 [FFmpeg-devel] [PR] commitcheck (PR #22260) Jun Zhao via ffmpeg-devel

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git