Add SSH commit signing for HEAD commits - #2789
Conversation
|
Thanks for the contribution @kausters. I'm out for some time so I probably won't review it this week. I'm also looking to push out the revamped release I've had in the continuous channel for quite some time to stable. So ballpark 2+ weeks from now we'll get this in a build if all looks good. |
|
@greptile Can you review this PR? |
|
| Filename | Overview |
|---|---|
| GitUpKit/Core/GCCommitSigning.m | Core signing implementation. Key resolution, PATH retrieval, temp-file lifecycle, and the libgit2 buffer/signature/write flow are all correct. Error propagation and cleanup are handled throughout. |
| GitUpKit/Core/GCRepository+HEAD.m | Routes createCommitFromHEAD* and createCommitByAmendingHEAD* through the new signing helpers. The amend path is behaviorally equivalent to the old git_commit_create_from_callback path: same author, same committer (refreshed via git_signature_default), same parents, same message. |
| GitUpKit/Core/GCCommitSigning-Tests.m | Comprehensive focused signing tests covering unsigned fallback, missing key, inline key, default key command, file path, signer failure, and PATH refresh behavior. Shared helpers correctly moved to GCCommitSigningTestHelpers. |
| GitUpKit/Core/GCCommitSigningTestHelpers.m | Shared test helpers GCCommitSignature, GCCommitHasSSHSignature, GCConfigureSSHSigningWithKeyPath correctly extracted and used by both test files. |
| GitUpKit/Core/GCRepository+HEAD-Tests.m | Adds testSSHSignsUserFacingCommits covering normal, merge, and amend commits with a real ssh-keygen-generated key and git verify-commit end-to-end verification. |
| GitUpKit/Core/GCPrivate.h | Adds declarations for the two new C-level commit helpers and getPATHUsingShell: behind the existing TARGET_OS_IPHONE guard. Correct placement. |
| GitUpKit/GitUpKit.xcodeproj/project.pbxproj | GCCommitSigning.m added to all three library targets; test files added to the test target only. GCCommitSigningTestHelpers.h correctly not in Sources (header only). |
Sequence Diagram
sequenceDiagram
participant C as Caller (HEAD.m)
participant S as GCCommitSigning.m
participant L as libgit2
participant K as ssh-keygen / signer
C->>S: GCCreateCommitFromTreeWithOptionalSignature(repo, tree, parents, ...)
S->>L: git_repository_config → git_config_get_bool(commit.gpgsign)
alt "gpgsign=false or format≠ssh"
S->>L: git_commit_create(unsigned)
L-->>S: oid
else "gpgsign=true and format=ssh"
S->>L: git_commit_create_buffer → commitBuffer
S->>S: _CommitSigningPATH (shell → $PATH)
S->>S: _SSHSigningKeyPath (user.signingkey / defaultKeyCommand)
opt inline key
S->>S: write public key to tmp file
end
S->>K: /usr/bin/env signer -Y sign -n git -f keyPath
K-->>S: SSH signature (stdout)
S->>S: cleanup tmp key file
S->>L: git_commit_create_with_signature(commitBuffer, signature, gpgsig)
L-->>S: oid
end
S->>L: git_commit_lookup(oid) → GCCommit
S-->>C: GCCommit (signed or unsigned)
Reviews (3): Last reviewed commit: "Refresh signing PATH for each commit" | Re-trigger Greptile
|
Added another fix mentioned but not explicitly posted by Greptile: signing now (aa0f81d) resolves PATH once per signing operation and shares it between the default-key command and signer, instead of caching it for the process lifetime. I also added a test verifying that PATH is looked up only once within an operation and refreshed for the next commit. All focused signing tests pass. |
ee89a07 to
aa0f81d
Compare
Summary
Hi! This PR adds SSH commit signing support for GitUpKit user-facing HEAD commit flows. Repositories configured like the Git CLI with
commit.gpgsign=trueandgpg.format=sshnow create commits with agpgsigSSH signature for normal commits, merge commits, amend, and conflict-resolution commits.To limit changes scope, the change intentionally supports only SSH signing. Non-SSH signing formats, including OpenPGP and X.509, continue to produce unsigned GitUp commits rather than blocking existing workflows. History replay/reorder paths also remain unsigned; signing those GitUp-created rewrite commits can be considered separately from signing commits users make directly.
What changed
GCCommitSigning.mhelper that creates the unsigned commit buffer, signs it with SSH, and writes the signed object through libgit2.user.signingkey, inline SSH public keys, andgpg.ssh.defaultKeyCommand.gpg.ssh.programorssh-keygen, launched with GitUp login-shell PATH handling so GUI-launched GitUp can find shell-installed signers.Tests
verify-commitverification with an allowed signers file.Notes