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
40 changes: 40 additions & 0 deletions skills/github-project/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,46 @@ When setting up CI for Go projects, ensure these GitHub configurations:
| Auto-merge workflow | Merge minor/patch updates automatically | `assets/auto-merge*.yml` templates |
| Required checks | CI workflow names in branch protection | Match exact workflow job names |

## Merge Strategy & Signed Commits

When configuring repositories that require signed commits with clean history, consult `references/merge-strategy.md` for the recommended settings.

### Quick Reference

For signed commits workflow (rebase locally + merge commit):

| Repository Setting | Value | Why |
|--------------------|-------|-----|
| `allow_merge_commit` | **true** | Preserves signatures on feature branch commits |
| `allow_rebase_merge` | true | GitHub requires at least one of squash/rebase |
| `allow_squash_merge` | false | Destroys individual commit signatures |

| Branch Protection | Value | Why |
|-------------------|-------|-----|
| `required_signatures` | true | Enforces GPG/SSH signed commits |
| `required_linear_history` | **false** | Must be false - conflicts with merge commits |

### Workflow

```bash
# 1. Developer rebases PR branch locally (signs commits)
git fetch origin && git rebase origin/main
git push --force-with-lease

# 2. Merge via merge commit (preserves signatures)
gh pr merge <number> --merge
```

### Auto-Merge Compatibility

| Merge Strategy | Works with `required_signatures`? |
|----------------|-----------------------------------|
| Merge commit | ✅ Yes - GitHub signs the merge commit |
| Rebase merge | ❌ No - GitHub cannot sign rewritten commits |
| Squash merge | ❌ No - GitHub cannot sign squashed commit |

**Important:** When enabling auto-merge, select "Create a merge commit" strategy.

## Related Skills

When implementing Go code patterns and CI/CD workflows, use the `go-development` skill.
Expand Down
169 changes: 169 additions & 0 deletions skills/github-project/references/merge-strategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Merge Strategy for Signed Commits

This guide explains how to configure GitHub repositories that require both signed commits and clean git history.

## The Problem

GitHub's branch protection offers two relevant settings that conflict:

| Setting | Effect |
|---------|--------|
| `required_signatures` | All commits on protected branch must be signed |
| `required_linear_history` | Only squash or rebase merges allowed (no merge commits) |

**The conflict:** GitHub cannot sign commits during squash or rebase merge operations. When `required_linear_history` is enabled, GitHub rewrites commits server-side, but cannot sign them with your GPG/SSH key.

## The Solution

Use **local rebase + merge commit**:

1. Developers rebase their PR branch locally (signing commits with their key)
2. Force-push the rebased branch
3. Merge via merge commit (GitHub signs the merge commit with its key)

This gives you:
- ✅ Clean, linear history on feature branches
- ✅ Clear merge points on main branch
- ✅ All commits verified (developers sign feature commits, GitHub signs merge commits)

## Repository Settings

Configure via API:

```bash
gh api repos/{owner}/{repo} -X PATCH \
-f allow_merge_commit=true \
-f allow_rebase_merge=true \
-f allow_squash_merge=false
```

| Setting | Value | Reason |
|---------|-------|--------|
| `allow_merge_commit` | `true` | Required for signed commits workflow |
| `allow_rebase_merge` | `true` | GitHub requires at least one of squash/rebase |
| `allow_squash_merge` | `false` | Destroys individual commit history and signatures |

**Note:** GitHub requires at least one of `allow_squash_merge` or `allow_rebase_merge` to be true. Keep `allow_rebase_merge` enabled but don't use it for PRs requiring signatures.

## Branch Protection Settings

Configure via API:

```bash
gh api repos/{owner}/{repo}/branches/main/protection -X PUT \
--input - << 'EOF'
{
"required_status_checks": {
"strict": true,
"contexts": ["ci"]
},
"enforce_admins": true,
"required_pull_request_reviews": {
"required_approving_review_count": 1
},
"restrictions": null,
"required_linear_history": false,
"required_signatures": true
}
EOF
```

| Setting | Value | Reason |
|---------|-------|--------|
| `required_signatures` | `true` | Enforces signed commits |
| `required_linear_history` | `false` | **Must be false** - blocks merge commits |

## Developer Workflow

### Before Opening PR

```bash
# Ensure commits are signed
git config commit.gpgsign true
```

### Before Merging

```bash
# 1. Fetch latest main
git fetch origin

# 2. Rebase on main (re-signs commits)
git rebase origin/main

# 3. Force-push rebased branch
git push --force-with-lease
```

### Merging

```bash
# Use merge commit strategy
gh pr merge <number> --merge
```

## Auto-Merge Configuration

Auto-merge works with signed commits **only when using merge commit strategy**.

| Strategy | Compatible | Reason |
|----------|------------|--------|
| Merge commit | ✅ | GitHub signs merge commit with its key |
| Rebase | ❌ | GitHub cannot sign rewritten commits |
| Squash | ❌ | GitHub cannot sign squashed commit |

When configuring auto-merge workflows, ensure they use `--merge`:

```yaml
- name: Enable auto-merge
run: gh pr merge --auto --merge "$PR_NUMBER"
```

## How GitHub Signing Works

When you merge via the GitHub UI or API with merge commit:

1. **Feature branch commits**: Retain original GPG/SSH signatures from developers
2. **Merge commit**: Signed by GitHub's web-flow key (`noreply@github.com`)

Both are marked as "Verified" in the GitHub UI:
- Developer commits show the developer's GPG key
- Merge commits show "Verified" with GitHub as the signer

## Troubleshooting

### "Merge commits are not allowed on this repository"

**Cause:** `allow_merge_commit` is false in repository settings.

**Fix:**
```bash
gh api repos/{owner}/{repo} -X PATCH -f allow_merge_commit=true
```

### "Base branch requires signed commits. Rebase merges cannot be automatically signed"

**Cause:** `required_linear_history` is true, forcing rebase merge which GitHub cannot sign.

**Fix:**
```bash
gh api repos/{owner}/{repo}/branches/main/protection -X PUT \
--input - << 'EOF'
{
...existing settings...,
"required_linear_history": false
}
EOF
```

### Auto-merge fails with signature error

**Cause:** Auto-merge configured with rebase or squash strategy.

**Fix:** Update auto-merge workflow to use `--merge` flag instead of `--rebase` or `--squash`.

## References

- [GitHub Branch Protection](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches)
- [Signing Commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)
- [About Merge Methods](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/about-merge-methods-on-github)