From 2bce95c635c0308e19ec9acb077485eafabe1460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Legarraga?= Date: Fri, 21 Aug 2026 15:04:49 -0400 Subject: [PATCH 1/4] ci: block merges that skip CHANGELOG.md or contributor credit Closes #113. A credit-guard job runs scripts/check_credit.sh on every pull request: if the diff touches philanthropy/, the PR must also touch CHANGELOG.md and its author must already appear in CONTRIBUTORS.md. Runs only on pull_request events, so direct pushes and pre-guard history are never re-litigated. Verified against four scenarios locally: source change without changelog (fails), uncredited author (fails), credited author with changelog (passes), tests-only diff (skips). --- .github/workflows/ci.yml | 13 +++++++++++++ scripts/check_credit.sh | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100755 scripts/check_credit.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd0123a..7269773 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,19 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: + credit-guard: + name: Changelog and contributor credit (issue #113) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - name: Check CHANGELOG.md and CONTRIBUTORS.md updates + env: + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: bash scripts/check_credit.sh "$BASE_SHA" "$HEAD_SHA" "$PR_AUTHOR" lint: name: Lint and type check runs-on: ubuntu-latest diff --git a/scripts/check_credit.sh b/scripts/check_credit.sh new file mode 100755 index 0000000..e86d853 --- /dev/null +++ b/scripts/check_credit.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Credit guard (issue #113): a PR that changes code under philanthropy/ must +# also touch CHANGELOG.md, and its author must already be credited in +# CONTRIBUTORS.md. Runs on pull_request events only; direct pushes to main +# and history that predates the guard are not checked. +set -euo pipefail + +base=$1 +head=$2 +author=${3:-} + +changed=$(git diff --name-only "$base" "$head") + +if ! echo "$changed" | grep -q '^philanthropy/'; then + echo "credit guard: no source changes under philanthropy/ - nothing to check" + exit 0 +fi + +if ! echo "$changed" | grep -qx 'CHANGELOG.md'; then + echo "::error::This PR touches files under philanthropy/ but not CHANGELOG.md. Add a line under [Unreleased] describing the change." + exit 1 +fi + +if [ -z "$author" ]; then + echo "::error::PR author could not be determined; cannot run the CONTRIBUTORS.md check." + exit 1 +fi + +if ! grep -qE "@${author}([^A-Za-z0-9_-]|$)" CONTRIBUTORS.md; then + echo "::error::This PR touches files under philanthropy/ but its author (@$author) is not listed in CONTRIBUTORS.md. Add yourself in the same pull request (see 'Getting listed')." + exit 1 +fi + +echo "credit guard OK: changelog touched and @$author is credited" From c4c159a50bf8b5a77b3e5d174f795f0b13899238 Mon Sep 17 00:00:00 2001 From: slegarraga Date: Sat, 22 Aug 2026 12:37:29 -0400 Subject: [PATCH 2/4] docs(changelog): add Unreleased entry for the credit-guard job Per AGENTS.md, every PR needs an entry under [Unreleased]. Describes the new scripts/check_credit.sh wiring and its two gates (changelog touched, author credited) for PRs that touch philanthropy/. --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4320b0f..00f48d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) ## [Unreleased] +### Added +- `credit-guard` CI job: pull requests touching `philanthropy/` must also + update this changelog, and the author must be credited in + CONTRIBUTORS.md. Implemented as `scripts/check_credit.sh`, wired into + `ci.yml` on `pull_request` events only; failures surface as inline + `::error::` annotations on the Files tab. Closes #113. + ## [1.0.0] - TBD The API freeze. No code changes: 1.0.0 is a promise, not a feature. From cd5d57ea42673031d1dcd5468fd70863a5d12d97 Mon Sep 17 00:00:00 2001 From: slegarraga Date: Sat, 22 Aug 2026 13:23:07 -0400 Subject: [PATCH 3/4] chore: retrigger CI on final head Signed-off-by: slegarraga From 0af3a45b1440520b4c0ae99ddce02033a59c79c3 Mon Sep 17 00:00:00 2001 From: slegarraga Date: Sat, 22 Aug 2026 20:38:18 -0400 Subject: [PATCH 4/4] fix(ci): skip credit-guard on push events, match author logins literally - credit-guard now has 'if: github.event_name == pull_request' so post-merge pushes to main do not invoke check_credit.sh with empty SHAs (review note from shivamlalakiya; makes the CHANGELOG claim literally true in YAML). - check_credit.sh matches the @login token literally via awk index instead of interpolating it into an ERE, so bot logins like dependabot[bot] are not parsed as a character class. Verified with a 7-case harness covering human/bot/prefix/skip/changelog/empty-author paths. --- .github/workflows/ci.yml | 1 + scripts/check_credit.sh | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72f2860..c939166 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,7 @@ concurrency: jobs: credit-guard: name: Changelog and contributor credit (issue #113) + if: github.event_name == 'pull_request' runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 diff --git a/scripts/check_credit.sh b/scripts/check_credit.sh index e86d853..2a72d33 100755 --- a/scripts/check_credit.sh +++ b/scripts/check_credit.sh @@ -26,7 +26,16 @@ if [ -z "$author" ]; then exit 1 fi -if ! grep -qE "@${author}([^A-Za-z0-9_-]|$)" CONTRIBUTORS.md; then +# Match the literal token "@" not followed by another login character. +# The author is interpolated literally (awk index, no regex), so bot logins +# such as dependabot[bot] are not misread as a character class. +if ! awk -v token="@$author" ' + index($0, token) { + rest = substr($0, index($0, token) + length(token)) + if (rest == "" || rest !~ /^[A-Za-z0-9_-]/) { found = 1; exit } + } + END { exit found ? 0 : 1 } +' CONTRIBUTORS.md; then echo "::error::This PR touches files under philanthropy/ but its author (@$author) is not listed in CONTRIBUTORS.md. Add yourself in the same pull request (see 'Getting listed')." exit 1 fi