fix: init setup + commit signing e2e test#46
Merged
stormer78 merged 5 commits intoOpenVTC:release/v0.1.6from Apr 18, 2026
Merged
fix: init setup + commit signing e2e test#46stormer78 merged 5 commits intoOpenVTC:release/v0.1.6from
stormer78 merged 5 commits intoOpenVTC:release/v0.1.6from
Conversation
Signed-off-by: Francis Pineda <francis.p@affinidi.com>
94b3bef to
aee69be
Compare
Signed-off-by: Francis Pineda <francis.p@affinidi.com>
49c3d82 to
9c947e4
Compare
Signed-off-by: Francis Pineda <francis.p@affinidi.com>
a111427 to
f7b4582
Compare
Signed-off-by: Francis Pineda <francis.p@affinidi.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes
Fixes seven failures across the signing and verification paths, with unit tests and follow-on improvements.
Fix 1: Signing buffer path treated as unrecognised subcommand
Symptom
Root cause
Git appends the buffer file path as a trailing positional argument when it calls the signing program. The CLI had no positional field defined, so clap treated the path as an unknown subcommand.
Fix
Added a hidden positional
sign_filefield toCliand threaded it through tohandle_sign. To prevent a side effect of this change where any unrecognised bare word (e.g. a typo) would be silently absorbed intosign_fileand fall through to the help branch with a guard was added: ifsign_fileis set but-Yis absent, the tool errors with an unrecognised subcommand message. The git signing path always includes-Y signand is never affected by the guard.Files changed:
src/main.rs,src/sign.rsFix 2: Global
user.signingKeyoverrides the config path passed via-fSymptom
Root cause
When
user.signingKeyis set in the global git config, git passes that value as the-fargument instead ofgpg.ssh.defaultKeyFile.did-git-signreceived the.pubpath and failed to parse it as its JSON config file.Fix
did-git-sign initnow setsuser.signingKeyat local repo scope to thedid-git-signconfig path, overriding any global value. Becauseuser.signingKeyis conventionally a.pubpath, setting it to a JSON file may surprise users inspectinggit config --list. If a globaluser.signingKeyexisted before init, a notice is now printed explaining the local override and confirming the global configuration is unchanged.Files changed:
src/init.rs,src/main.rsFix 3: Signature written to stdout; git expects a
.sigfile on diskSymptom
Root cause
The signature was printed to stdout. Git reads it back from
<buffer_file>.sigon disk after the signing program exits. The.sigfile was never created.Fix
handle_signnow writes the signature to<sign_file>.sig, matchingssh-keygenbehaviour.Files changed:
src/sign.rsFix 4: Commits show as "Unverified" on GitLab / GitHub
Symptom
Signing succeeds locally but the commit badge on GitLab or GitHub shows Unverified.
Root cause
The signing public key was not registered against the committer account, or the committer email did not match the account email.
Fix
did-git-sign initnow prints explicit key registration steps after setup completes.did-git-sign healthprints the SSH public key and the allowed-signers entry at any time.Files changed:
src/main.rsFix 5:
git config user.emailshows the DID key ID instead of the user's emailSymptom
Or two
user.emailentries appear ingit config --list, with the DID key ID as the local override.Root cause
setup_gitunconditionally wrotegit config user.email <did_key_id>, clobbering the user's configured email at local repo scope.Fix
Removed the unconditional write.
did-git-sign initconfigures signing only and managing git identity fields is outside its scope.Git's SSH verification is a two-step process: it first calls
find-principals, which scans theallowed_signersfile and returns the principal whose public key fingerprint matches the signature, which is the DID key ID. It then callsverifywith that fingerprint-derived principal as-I.user.emailis not part of either step. The two things that depend on email and on theallowed_signersprincipal are on separate paths:user.emailused?git log --show-signaturefind-principals→ DIDThis was confirmed against a live repo using
git log --show-signaturereturned a good signature withuser.emailset to a real address andallowed_signerscontaining only the DID principal. Given this, the--emailflag that was also part of this fix was removed: sinceuser.emailhas no role in local verification, the flag serves no purpose. Git identity remains the user's own configuration. Any user who previously passed--emailcan set their email directly withgit config --local user.email <address>.Files changed:
src/init.rs,src/main.rsFix 6 & 7:
git log --show-signaturefails with "unexpected argument '-s'" and "'-O'"Symptom
Both errors appear twice per
git log --show-signatureinvocation because git calls the signing program separately for each verification step.Root cause
When verifying a commit, git calls the signing program with
-s <sig_file>,-I <principal>, and-O hashalg=sha512. None of these flags were declared inCli, so clap rejected them. The"verify"operation also fell through tobail!since only"sign"was handled.Fix
Added
-s,-I, and-Oas hidden CLI arguments. All-Yoperations other thansignare delegated to the systemssh-keygenbinary with all received flags forwarded verbatim. Verification is stateless and it only needs the public key fromallowed_signers, whichssh-keygenhandles natively.Three improvements were made to the delegation path at the same time. The
ssh-keygenbinary is now resolved via aDID_GIT_SIGN_SSH_KEYGENenvironment variable before falling back to the bare name which matters in GUI git clients and minimal CI containers where git's inherited$PATHmay not include the OpenSSH tools. Thedelegate_to_ssh_keygenhelper was refactored to return the exit code rather than callingprocess::exitinternally, making the tokio runtime safety boundary visible at the call site. The explicit enumeration of operation names was replaced with asign/ catch-all split so that any new-Yoperation git introduces in the future is forwarded automatically rather than breakingdid-git-sign.Files changed:
src/main.rsTests
Three unit tests are added alongside the fixes. All 37 tests in the crate pass.
sign::tests::sig_path_appends_dot_sig_not_replaces_extension: Regression guard for Fix 3. Encodes the contract that.sigis appended to the full filename rather than replacing any existing extension. Protects against a future refactor usingPath::with_extension("sig"), which would silently breakbuffer.diff→buffer.diff.sigintobuffer.sig.tests::delegate_forwards_all_flags_to_ssh_keygen: Regression guard for Fixes 6 & 7. Parses a realCliviaCli::try_parse_from, pointsDID_GIT_SIGN_SSH_KEYGENat a small mock shell script (tests/fixtures/mock_ssh_keygen.sh) that captures its argv to a temp file, and asserts every forwarded flag is present. Validates both argument assembly and the env-var override path from Fix 7.init::tests::setup_git_never_writes_user_email: Regression guard for Fix 5. Runssetup_gitagainst a realgit inittemp repository and asserts thatgit config --local user.emailexits non-zero afterwards. Preventsuser.emailfrom being silently re-introduced in a future change.The
EnvVarGuardandCwdGuardhelpers used by these tests are RAII structs: they restore the original env var or working directory on drop, keeping cleanup panic-safe. Both tests that mutate process-global state (set_var,set_current_dir) are annotated#[serial_test::serial]to prevent races with concurrently running tests.serial_testis added as a dev-only dependency and is not linked into the release binary.PS: The commits are signed by DID-GIT-SIGN 🙂