Aman-Mittal opened a new issue, #428:
URL: https://github.com/apache/fineract-backoffice-ui/issues/428

   `main` requires signed commits, and **Verify Commit Signatures** is what 
enforces it. It currently answers a different question from the one GitHub 
answers, so a PR can be green on the check and show **Unverified** on every 
commit. A maintainer then has to notice by eye and ask the contributor to fix 
it by hand, which is where this keeps landing.
   
   ## Evidence
   
   #392 — the check is `success` on head `af3ef580`, and all three commits:
   
   | commit | check | `verification.verified` | `reason` | signature present |
   |---|---|---|---|---|
   | `9d7f1793` | ✅ success | `false` | `no_user` | yes |
   | `74f75234` | ✅ success | `false` | `no_user` | yes |
   | `af3ef580` | ✅ success | `false` | `no_user` | yes |
   
   Not a one-off:
   
   | PR | commits | unverified | reason |
   |---|---|---|---|
   | #392 | 3 | 3 | `no_user` |
   | #331 | 2 | 2 | `no_user` |
   | #260 | 1 | 1 | `unsigned` |
   
   ```bash
   gh api repos/apache/fineract-backoffice-ui/pulls/392/commits \
     --jq '.[] | {sha: .sha[0:8], verified: .commit.verification.verified, 
reason: .commit.verification.reason}'
   ```
   
   ## Three separate problems
   
   **1. The check asks the wrong question.**
   
   `scripts/verify-signed-commits.sh` treats every `%G?` except `N` as a pass, 
and the comment says so plainly — a runner holds no contributor public keys, so 
`E`/`U`/`B` are deliberately accepted. That reduces the check to *"is a 
signature blob attached?"*. GitHub instead asks *"is this signature made by a 
key registered to a GitHub account whose verified email matches the 
committer?"* A commit passes the first and fails the second all day. `B` (bad 
signature), `R` (revoked key) and `X` (expired signature) are also currently 
passes.
   
   **2. The gate is supplied by the branch it gates.**
   
   The workflow triggers on `pull_request` and runs 
`./scripts/verify-signed-commits.sh` from the checked-out merge ref — the PR's 
copy. A PR can therefore weaken the check that is meant to gate it, and #392's 
third commit does exactly that: it adds a `gpgsig` header fallback so that any 
signature payload counts as signed, and points git at an empty 
`allowed_signers` file.
   
   That commit was itself written to get past the check rather than to satisfy 
it (`Co-authored-by: Cursor <[email protected]>`). Whatever the intent, a 
gate a contributor can edit from inside the PR is not a gate. **This is the 
part worth fixing first**, and it should be resolved independently of whether 
#392 lands.
   
   **3. The contributor-side cause in #392 and #331 is not signing at all.**
   
   ```
   author email   : timichris45
   committer email: timichris45
   gh author acct : None
   signature      : -----BEGIN SSH SIGNATURE-----
   ```
   
   `user.email` is a bare username, not an address. GitHub cannot map it to an 
account, which is what `no_user` means — so the commits are not attributed to 
the PR author either, and no key lookup ever happens. The SSH signing is set up 
correctly; the identity is not. No change to signing configuration fixes this, 
which is presumably why repeated asking has not converged.
   
   Worth noting: `main`'s current script would *fail* these commits, because 
git reports SSH signatures as `%G?=N` without `gpg.ssh.allowedSignersFile`. So 
on `main` the check is accidentally right for SSH-signed commits and wrong for 
GPG-signed ones — failing for a reason unrelated to whether GitHub can verify 
them.
   
   ## Suggested fix
   
   Ask GitHub, since GitHub is the authority and its answer cannot be forged 
from inside the PR:
   
   ```
   GET /repos/{owner}/{repo}/pulls/{number}/commits
     → .[].commit.verification.verified   // must be true
     → .[].commit.verification.reason     // for the failure message
   ```
   
   Fail on any commit with `verified: false`, and put `reason` in the 
annotation — the reasons are distinct and each implies a different remedy:
   
   - `unsigned` — no signature; set up signing
   - `no_user` — committer email is not on any GitHub account (**#392, #331**)
   - `unverified_email` — email not *verified* on the account
   - `unknown_key` / `unknown_signature_type` — key not uploaded to GitHub
   - `expired_key`, `bad_signature`, `malformed_signature`
   
   Move the job to `pull_request_target` so it runs the **base branch's** 
workflow definition and carries a write token. It needs no repository content 
at all — only the API — so it must **not** check out or execute PR code, which 
also removes the usual `pull_request_target` hazard. `permissions: 
pull-requests: write, contents: read`.
   
   Under the current `pull_request` trigger, `GITHUB_TOKEN` is read-only for 
fork PRs, so commenting is not possible without this change.
   
   Keeping `scripts/verify-signed-commits.sh` is still worthwhile as a local 
pre-push aid — it just should not be the CI gate.
   
   ## The manual step to remove
   
   Have the job post one comment when it fails, naming the offending commits 
and the specific `reason`, with the matching remedy and a link to 
[CONTRIBUTING.md#commit-signing](../blob/main/CONTRIBUTING.md#commit-signing). 
Update rather than append on re-runs, so a PR does not collect a wall of 
identical comments.
   
   For `no_user` in particular the message should say the committer email is 
not an email address on their GitHub account and point at `git config 
user.email` plus:
   
   ```bash
   git rebase --exec 'git commit --amend --no-edit --reset-author -S' 
origin/main
   ```
   
   > I read *"and also commit if commits are not signed"* as **comment**. If 
you did mean having CI amend and sign commits: it cannot for fork PRs (no write 
access to the contributor's branch), and re-signing someone else's commit with 
a CI key would attest to authorship the runner cannot vouch for. Say the word 
and I will reopen that part.
   
   ## Also worth doing
   
   - Enable branch protection **Require signed commits** on `main`. Branch 
protection is enforced by GitHub itself, so it cannot be edited by a PR and 
does not depend on any workflow — the check then becomes early feedback rather 
than the only line of defence.
   - Revert the CI change in #392 (`af3ef580`) regardless of that PR's fate; 
the SSH-signature gap it describes is real, but the fix belongs on `main` on 
its own merits, not inside a PR it makes pass.
   - The PR template asks for signed commits and this still recurs, so the 
message evidently is not landing where people read it. A failing check that 
names the exact problem will do more than more template text.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to