Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/scripts/check-changelog-commit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/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
# 3. Commit link URLs must match their reference keys
#
# Usage: check-changelog-commit.sh <base-sha>
set -euo pipefail

base="${1:?Usage: check-changelog-commit.sh <base-sha>}"
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 ---

changelog_diff=$(git diff "${base}..HEAD" -- CHANGELOG.md \
| grep "^+" | grep -v "^+++" || true)

inline_hashes=$(echo "$changelog_diff" \
| grep -oE '\(\[([0-9a-f]{7,})\]' \
| grep -oE '[0-9a-f]{7,}' | sort -u || true)

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 URLs must match their keys ---

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."
40 changes: 40 additions & 0 deletions .github/workflows/changelog.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CHANGELOG related checks per pull requests
on:
pull_request:
types: [assigned, opened, synchronize, reopened, labeled, unlabeled]
branches:
- main
merge_group:
types: [checks_requested]

jobs:
check-changelog:
name: Check changelog action
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- 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
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ lint: ## Run ruff linter
lint-fix: ## Run ruff linter with auto-fix
poetry run ruff check --fix leakix/ tests/ example/ executable/

.PHONY: lint-shell
lint-shell: ## Lint shell scripts using shellcheck
shellcheck .github/scripts/*.sh

.PHONY: typecheck
typecheck: ## Run mypy type checker
poetry run mypy leakix/
Expand Down