From 83e6d678972c9a6e3cd6868d5aede3419d6a790f Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Fri, 28 Aug 2026 11:22:12 +0200 Subject: [PATCH] Accept a CLA signature line anywhere in a comment (#894) --- .github/workflows/cla.yml | 2 +- actions/__init__.py | 2 +- actions/cla.py | 5 ++++- cla/README.md | 2 +- tests/test_cla.py | 20 +++++++++++++++----- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 53d4581..3c1119c 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -20,7 +20,7 @@ permissions: jobs: CLA: - if: github.event_name == 'pull_request_target' || (github.event.issue.pull_request && (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I sign the CLA')) + if: github.event_name == 'pull_request_target' || (github.event.issue.pull_request && (contains(github.event.comment.body, 'recheck') || contains(github.event.comment.body, 'I have read the CLA Document and I sign the CLA'))) concurrency: group: cla-${{ github.event.pull_request.number || github.event.issue.number }} runs-on: ubuntu-latest diff --git a/actions/__init__.py b/actions/__init__.py index 6423c41..6c1e49f 100644 --- a/actions/__init__.py +++ b/actions/__init__.py @@ -28,4 +28,4 @@ # ├── test_summarize_pr.py # └── ... -__version__ = "0.3.18" +__version__ = "0.3.19" diff --git a/actions/cla.py b/actions/cla.py index fde075a..9bdb452 100644 --- a/actions/cla.py +++ b/actions/cla.py @@ -22,6 +22,8 @@ CLA_PATH = "signatures/version1/cla.json" CLA_BRANCH = "cla-signatures" CLA_DOCUMENT = "https://docs.ultralytics.com/help/CLA" SIGN_COMMENT = "I have read the CLA Document and I sign the CLA" +SIGN_TEXT = SIGN_COMMENT.casefold() +SIGN_STRIP = " \t*_`\"'.!,;:" # whitespace, markdown emphasis, quotes and end punctuation around a signature line COMMENT_MARKER = "" LEGACY_MARKER = "CLA Assistant Lite bot" BOT_LOGIN = "github-actions[bot]" @@ -224,7 +226,8 @@ def run(action: Action, ledger_action: Action) -> None: records = { comment["user"]["id"]: _record(comment, action, number) for comment in comments - if comment.get("body") == SIGN_COMMENT and comment.get("user", {}).get("id") in contributor_ids - signed_ids + if any(line.strip(SIGN_STRIP).casefold() == SIGN_TEXT for line in (comment.get("body") or "").splitlines()) + and comment.get("user", {}).get("id") in contributor_ids - signed_ids } if records: _persist(ledger_action, list(records.values()), action, number) diff --git a/cla/README.md b/cla/README.md index cfd5607..394e29b 100644 --- a/cla/README.md +++ b/cla/README.md @@ -17,7 +17,7 @@ permissions: jobs: CLA: - if: github.event_name == 'pull_request_target' || (github.event.issue.pull_request && (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I sign the CLA')) + if: github.event_name == 'pull_request_target' || (github.event.issue.pull_request && (contains(github.event.comment.body, 'recheck') || contains(github.event.comment.body, 'I have read the CLA Document and I sign the CLA'))) concurrency: group: cla-${{ github.event.pull_request.number || github.event.issue.number }} runs-on: ubuntu-latest diff --git a/tests/test_cla.py b/tests/test_cla.py index 8789722..98fb06b 100644 --- a/tests/test_cla.py +++ b/tests/test_cla.py @@ -172,12 +172,15 @@ def test_persist_surfaces_final_exhausted_error(monkeypatch, statuses, message): assert store.put.call_count == len(statuses) -def test_run_records_exact_sentence_and_updates_legacy_comment(): - """Persist an exact signature and reuse the legacy action's status comment.""" +@pytest.mark.parametrize( + "body", [cla.SIGN_COMMENT, f"{cla.SIGN_COMMENT}\r\n", f"{cla.SIGN_COMMENT} ", cla.SIGN_COMMENT.lower()] +) +def test_run_records_exact_sentence_and_updates_legacy_comment(body): + """Persist a signature despite trailing whitespace or casing.""" source, store = action(), action() signing = { "id": 30, - "body": cla.SIGN_COMMENT, + "body": body, "created_at": "date", "user": {"id": 2, "login": "new", "type": "User"}, } @@ -217,9 +220,16 @@ def test_run_stays_silent_when_all_contributors_already_signed(): source.patch.assert_not_called() -@pytest.mark.parametrize("body", [f"{cla.SIGN_COMMENT}!", cla.SIGN_COMMENT.lower(), f" {cla.SIGN_COMMENT}"]) +@pytest.mark.parametrize( + "body", + [ + f"could I write {cla.SIGN_COMMENT}?", + f"> {cla.SIGN_COMMENT}", + f'- **Sign the CLA**: sign by writing "{cla.SIGN_COMMENT}" in a new message.', + ], +) def test_run_rejects_similar_sentence_and_keeps_hard_failure(body): - """Reject a modified signing sentence and leave the CLA gate failed.""" + """Reject a quoted or embedded sentence and leave the CLA gate failed.""" source, store = action(), action() user = {"id": 2, "login": "new", "type": "User"} source.get.side_effect = [