Files
FlClash/tool/check_commit_msg.sh
T
chen08209 96fcb9ab5e chore(agents,tool): rebuild agent config, lint gates, CI, and release tooling
Rework .agents/, Claude/Codex config, CI workflows, lint gates and
lint-rule tests, and the release/changelog tooling. The 5% comment
density gate stays diff-scoped inside git hooks, where GIT_DIR outranks
the -C the gate uses to locate the repository. The tray icon generator
writes multi-size ICO files from the SVG sources into the per-platform
tray asset directories. The custom code-review subagents are gone: the
built-in /code-review never dispatched them, and the subagent model now
lives in a user-level setting outside the repository.

The Makefile, build_config.yaml and CI hand the Core and Helper builds to
the setup build hook, so the buildkit launchers go, and setup.dart
packages AppImage and rpm on arm64 with flutter_distributor pinned to the
fork tag that normalizes packaging permissions.

Changelog: skip
2026-09-10 12:42:52 +08:00

129 lines
3.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
message_file="${1:?commit message file is required}"
# The subject and the body have to be cut from the same text, or they overlap:
# `git commit --cleanup=verbatim` and hook-written templates both leave blank
# lines above the subject, and a body taken as "everything from line two" then
# contains the subject itself — enough for the trailer checks below to read the
# subject as a footer.
cleaned="$(grep -v '^#' "$message_file" | sed '/[^[:space:]]/,$!d' || true)"
subject="$(head -n 1 <<<"$cleaned")"
if [[ -z "$subject" ]]; then
echo 'Commit message is empty.' >&2
exit 1
fi
if [[ "$subject" =~ ^(Merge|Revert)[[:space:]] ]]; then
exit 0
fi
if [[ "$subject" =~ ^(fixup|squash)! ]]; then
exit 0
fi
types='feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert'
if [[ ! "$subject" =~ ^($types)(\([a-z0-9,./_-]+\))?!?:[[:space:]].+ ]]; then
cat >&2 <<EOF
Commit subject does not follow Conventional Commits:
$subject
Expected: <type>[(scope)][!]: <description>
Types: ${types//|/, }
Examples:
perf(views): stop redoing per-frame work in build
refactor(tray): replace the tray_manager fork with a first-party plugin
fix(core,android)!: keep failures visible and lifecycle results honest
EOF
exit 1
fi
if [[ ${#subject} -gt 100 ]]; then
echo "Commit subject is ${#subject} characters; keep it within 100." >&2
exit 1
fi
description="${subject#*: }"
first_word="${description%% *}"
first_word="${first_word%%[^[:alnum:]]*}"
if [[ "$first_word" =~ ^[A-Z][a-z]+$ ]]; then
echo "Commit description should start in lower case: $description" >&2
echo 'Identifiers and acronyms keep their own casing, for example AppBar or DNS.' >&2
exit 1
fi
if [[ "$description" =~ \.$ ]]; then
echo "Commit description should not end with a period: $description" >&2
exit 1
fi
# Changelog trailers. `tool/changelog.dart` reads these to build the user facing
# changelog; the subject is only a fallback.
body="$(tail -n +2 <<<"$cleaned")"
groups='breaking|feat|fix|perf|revert'
while IFS= read -r line; do
if [[ "$line" =~ ^Changelog-([A-Za-z0-9-]+): ]]; then
key="${BASH_REMATCH[1]}"
if [[ "$key" != 'Type' ]]; then
echo "Unknown changelog trailer: Changelog-$key" >&2
echo "The changelog is English only; Changelog-Type is the only" >&2
echo "suffixed trailer. Do not add translations to commit messages." >&2
exit 1
fi
value="${line#*: }"
if [[ ! "$value" =~ ^($groups)$ ]]; then
echo "Unknown Changelog-Type: $value" >&2
echo "Expected one of: ${groups//|/, }" >&2
exit 1
fi
fi
if [[ "$line" =~ ^Breaking-([A-Za-z0-9-]+): ]]; then
echo "Unknown breaking trailer: Breaking-${BASH_REMATCH[1]}" >&2
echo "The changelog is English only; use BREAKING CHANGE: alone." >&2
exit 1
fi
done <<<"$body"
agents='anthropic|claude|codex|copilot|cursor|devin|gemini|openai|\[bot\]'
if grep -qiE "^Co-authored-by:.*($agents)" <<<"$body"; then
cat >&2 <<'EOF'
Do not credit a coding agent in a Co-authored-by trailer.
The history records who owns the change, not which tool typed it. Human
co-authors are still fine.
EOF
exit 1
fi
if [[ "$subject" =~ ^($types)(\([a-z0-9,./_-]+\))?!: ]] &&
! grep -qE '^BREAKING[ -]CHANGE:' <<<"$body"; then
cat >&2 <<'EOF'
A breaking commit needs a BREAKING CHANGE footer describing what breaks:
feat(backup)!: new archive layout
BREAKING CHANGE: Archives from 0.8.95 and earlier need re-import
EOF
exit 1
fi
type="${subject%%[(:!]*}"
if [[ "$type" =~ ^(feat|fix|perf)$ ]] && ! grep -qE '^Changelog:' <<<"$body"; then
cat >&2 <<EOF
Note: no "Changelog:" trailer, so the changelog will reuse this subject.
Changelog: $description
EOF
fi