From 73cdc53ada0f22bf0b2ecebf5b9568324b9d4ee4 Mon Sep 17 00:00:00 2001 From: Danny Willems Date: Sun, 8 Feb 2026 13:54:53 -0300 Subject: [PATCH] ci: enforce changelog hygiene in pull requests Add changelog CI workflow with three jobs: changelog format validation via tarides/changelog-check-action, dedicated commit enforcement via custom shell script, and shellcheck linting. Add lint-shell Makefile target. --- .github/scripts/check-changelog-commit.sh | 75 +++++++++++++++++++++++ .github/workflows/changelog.yaml | 39 ++++++++++++ Makefile | 4 ++ 3 files changed, 118 insertions(+) create mode 100755 .github/scripts/check-changelog-commit.sh create mode 100644 .github/workflows/changelog.yaml diff --git a/.github/scripts/check-changelog-commit.sh b/.github/scripts/check-changelog-commit.sh new file mode 100755 index 0000000..396d90a --- /dev/null +++ b/.github/scripts/check-changelog-commit.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# Verify CHANGELOG.md hygiene in pull requests: +# 1. CHANGELOG.md changes must be in their own dedicated commit +# 2. Commit hashes referenced in new changelog entries must exist +# in the branch +# 3. Commit link URLs must match their reference keys +# +# Usage: check-changelog-commit.sh +set -euo pipefail + +base="${1:?Usage: check-changelog-commit.sh }" +errors=0 + +# --- Check 1: CHANGELOG.md must be in dedicated commits --- + +for commit in $(git log --format=%H "${base}..HEAD"); do + files=$(git diff-tree --no-commit-id --name-only -r "$commit") + if echo "$files" | grep -q "^CHANGELOG.md$"; then + file_count=$(echo "$files" | wc -l | tr -d ' ') + if [ "$file_count" -gt 1 ]; then + echo "::error::Commit $commit modifies CHANGELOG.md alongside other files." + echo "CHANGELOG.md changes must be in their own dedicated commit." + errors=$((errors + 1)) + fi + fi +done + +# --- Check 2: Referenced commit hashes must exist in the branch --- + +# Get new changelog lines added in this PR +changelog_diff=$(git diff "${base}..HEAD" -- CHANGELOG.md \ + | grep "^+" | grep -v "^+++" || true) + +# Extract short commit hashes from inline references like ([abc1234], ...) +inline_hashes=$(echo "$changelog_diff" \ + | grep -oE '\(\[([0-9a-f]{7,})\]' \ + | grep -oE '[0-9a-f]{7,}' | sort -u || true) + +# Get all commits reachable from HEAD (not just this branch) +for hash in $inline_hashes; do + if ! git cat-file -t "$hash" >/dev/null 2>&1; then + echo "::error::Commit $hash referenced in CHANGELOG.md does not exist." + errors=$((errors + 1)) + fi +done + +# --- Check 3: Link definition URLs must match their keys --- +# Matches lines like: [abc1234]: https://.../commit/xyz7890 +# and verifies abc1234 == xyz7890 + +link_lines=$(echo "$changelog_diff" \ + | grep -E '^\+\[[0-9a-f]{7,}\]: https://.*commit/' || true) + +while IFS= read -r line; do + [ -z "$line" ] && continue + key=$(echo "$line" \ + | grep -oE '\[([0-9a-f]{7,})\]' | head -1 \ + | tr -d '[]') + url_hash=$(echo "$line" \ + | grep -oE 'commit/[0-9a-f]{7,}' \ + | sed 's|commit/||') + if [ -n "$key" ] && [ -n "$url_hash" ] && [ "$key" != "$url_hash" ]; then + echo "::error::Link [$key] points to commit/$url_hash but should point to commit/$key" + errors=$((errors + 1)) + fi +done <<< "$link_lines" + +# --- Summary --- + +if [ "$errors" -gt 0 ]; then + echo "Found $errors changelog error(s)." + exit 1 +fi + +echo "All changelog checks passed." diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml new file mode 100644 index 0000000..3e999f6 --- /dev/null +++ b/.github/workflows/changelog.yaml @@ -0,0 +1,39 @@ +name: CHANGELOG related checks per pull requests +on: + pull_request: + types: [assigned, opened, synchronize, reopened, labeled, unlabeled] + branches: + - master + merge_group: + types: [checks_requested] + +jobs: + check-changelog: + name: Check changelog action + runs-on: ubuntu-latest + steps: + - uses: tarides/changelog-check-action@v3 + with: + changelog: CHANGELOG.md + + check-changelog-commit: + name: Check changelog is in dedicated commit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Verify CHANGELOG.md changes are in own commit + run: > + .github/scripts/check-changelog-commit.sh + "${{ github.event.pull_request.base.sha }}" + + shellcheck: + name: Shellcheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Install shellcheck + run: sudo apt-get install -y shellcheck + - name: Run shellcheck + run: make lint-shell diff --git a/Makefile b/Makefile index be6f4c2..b8f6436 100644 --- a/Makefile +++ b/Makefile @@ -97,6 +97,10 @@ lint: ## Lint all code with clippy @echo "Linting..." $(CARGO) clippy --all-targets -- -D warnings +.PHONY: lint-shell +lint-shell: ## Lint shell scripts using shellcheck + shellcheck .github/scripts/*.sh + # ============================================================================= # Testing # =============================================================================