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
54 changes: 54 additions & 0 deletions .github/workflows/commit-signatures.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# SPDX-License-Identifier: Apache-2.0
# Code authors: Vijay and Codex
name: commit-signatures

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review, edited]

permissions:
contents: read
pull-requests: read

jobs:
commit-signatures:
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Verify every PR commit
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const event = context.payload.pull_request;
const params = { ...context.repo, pull_number: event.number };
const { data: before } = await github.rest.pulls.get(params);
if (before.head.sha !== event.head.sha || before.base.ref !== event.base.ref) {
throw new Error('PR changed since this run was requested; run the check on the current PR.');
}

// The PR commits endpoint caps results at 250; never approve a partial list.
const commits = await github.paginate(github.rest.pulls.listCommits, {
...params, per_page: 100,
});
const { data: after } = await github.rest.pulls.get(params);
if (after.head.sha !== before.head.sha || after.base.sha !== before.base.sha ||
after.base.ref !== before.base.ref || after.commits !== before.commits) {
throw new Error('PR changed during verification; rerun the check.');
}
const shas = new Set(commits.map(commit => commit.sha));
if (!Number.isInteger(before.commits) || before.commits < 1 ||
commits.length !== before.commits || shas.size !== before.commits ||
!shas.has(before.head.sha)) {
throw new Error('Could not verify the complete PR commit list. PRs over 250 commits must be split.');
}

const unverified = commits.filter(commit => commit.commit?.verification?.verified !== true);
for (const commit of unverified) {
const reason = commit.commit?.verification?.reason ?? 'verification unavailable';
core.error(`Unverified commit ${commit.sha}: ${reason}`);
}
if (unverified.length > 0) {
core.setFailed(`${unverified.length} of ${commits.length} PR commits are not verified. Sign every commit and push the updated branch.`);
} else {
core.info(`All ${commits.length} PR commits have verified signatures.`);
}
58 changes: 58 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Contributing

Bug reports, capture recipes and focused improvements are welcome. Open an
[issue](https://github.com/llm-measurement/fleetdiff/issues/new/choose) for a behavior
change, and include a synthetic example and the result you expected. Report
vulnerabilities through [Security](SECURITY.md), not a public issue.

Keep changes focused, preserve existing privacy and accounting rules, and add
tests for changed behavior. Before opening a pull request, run:

```sh
go test -race ./...
go vet ./...
```

## Signed commits

Every commit in a pull request must have a signature GitHub verifies. The
`commit-signatures` check lists every unverified commit, including bot and merge
commits. A signed final commit does not cover earlier unsigned commits. A
`Signed-off-by` line (`git commit -s`) is not a cryptographic signature.

For SSH signing, use Git 2.34 or later. Use an existing signing key or create one
with a passphrase; do not overwrite an existing key:

```sh
ssh-keygen -t ed25519 -C "your-verified-email@example.com" -f "$HOME/.ssh/id_ed25519_signing"
eval "$(ssh-agent -s)"
ssh-add "$HOME/.ssh/id_ed25519_signing"
```

Add the contents of `~/.ssh/id_ed25519_signing.pub` to GitHub under **Settings >
SSH and GPG keys > New SSH key**, choosing **Signing key**. Upload only the public
`.pub` file; a key registered only for authentication is not enough.

From this repository, configure signing and an email verified on your GitHub
account (your GitHub-provided no-reply email also works):

```sh
git config --local user.email "your-verified-email@example.com"
git config --local gpg.format ssh
git config --local user.signingkey "$HOME/.ssh/id_ed25519_signing.pub"
git config --local commit.gpgsign true
git commit -S -m "Describe the change"
```

After pushing, confirm **Verified** on every PR commit and a passing
`commit-signatures` check. Existing unsigned commits need to be signed again by
their contributor; adding another signed commit does not fix them. Rewriting a
shared branch requires coordination and changes commit IDs. GPG signatures
verified by GitHub also satisfy the check.

Split PRs with more than 250 commits: GitHub's PR-commit API caps the list there,
and the check rejects incomplete lists rather than approving unexamined commits.

See GitHub's [SSH signing setup](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key#telling-git-about-your-ssh-key),
[adding an SSH signing key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account),
and [signing commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits).
Loading