Skip to content

feat: add SSH commit signing support - #20

Merged
Jamkris merged 3 commits into
mainfrom
feat/commit-signing
Apr 13, 2026
Merged

feat: add SSH commit signing support#20
Jamkris merged 3 commits into
mainfrom
feat/commit-signing

Conversation

@Jamkris

@Jamkris Jamkris commented Apr 13, 2026

Copy link
Copy Markdown
Owner

Reuse existing per-profile SSH keys for Git commit signing (Git >= 2.34). Adds interactive prompt during ghem add and ghem edit, generates gpgsign/gpg.format/signingkey in profile gitconfig, and shows signing status in ghem status and ghem list.

Summary by CodeRabbit

  • Documentation

    • Added "Commit Signing (SSH)" guide describing per-profile SSH commit signing, setup prompts, Git version requirement (Git ≥ 2.34), GitHub verification, and reuse of managed SSH keys.
  • New Features

    • Interactive enable/disable of SSH commit signing when creating or editing profiles.
    • Visual signing indicators in profile list and status.
    • Automatic SSH-signed commits for directories mapped to enabled profiles.
  • Localization

    • Added English and Korean messages for commit-signing prompts, badges, and status.

Reuse existing per-profile SSH keys for Git commit signing (Git >= 2.34).
Adds interactive prompt during `ghem add` and `ghem edit`, generates
gpgsign/gpg.format/signingkey in profile gitconfig, and shows signing
status in `ghem status` and `ghem list`.
@coderabbitai

coderabbitai Bot commented Apr 13, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d4deadb6-d319-4f69-ab1f-165cad5bfaf0

📥 Commits

Reviewing files that changed from the base of the PR and between 68ea652 and b89d097.

📒 Files selected for processing (2)
  • src/commands/edit.ts
  • src/core/gitconfig.ts

Walkthrough

Adds per-profile SSH commit signing: new Profile field, CLI prompts on add/edit guarded by Git >= 2.34 detection, gitconfig generation including SSH signing entries, list/status display of signing state, i18n additions, and a new git-version helper plus atomic write helper.

Changes

Cohort / File(s) Summary
Documentation
README.md, docs/ko-KR/README.md
Added "Commit Signing (SSH)" section describing add/edit prompts, required Git >= 2.34, resulting gitconfig entries, GitHub verification, and key reuse note.
CLI Commands
src/commands/add.ts, src/commands/edit.ts, src/commands/list.ts, src/commands/status.ts
Added commitSigning field and interactive prompts (guarded by support check); list shows signing badge; status reports signing enabled/disabled; warnings when unsupported.
Core config & IO
src/core/gitconfig.ts
Replaced write logic with writeFileAtomic; gitconfig generation builds lines array and conditionally emits user.signingkey, [commit] gpgsign = true, and [gpg] format = ssh when profile.commitSigning is true.
Git version detection
src/core/git.ts
New module that parses git --version, exports getGitVersion(), supportsSshCommitSigning(), and MIN_GIT_VERSION_FOR_SSH_SIGNING for guarding prompts.
Internationalization
src/i18n/types.ts, src/i18n/locales/en.ts, src/i18n/locales/ko.ts
Extended Messages type and added English/Korean strings for commit-signing prompts, confirmations, status labels, list badge, and edit prompt generator.
Types
src/types/config.ts
Added optional commitSigning?: boolean to the exported Profile interface.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

security, breaking

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add SSH commit signing support' directly and concisely describes the primary change—adding SSH-based commit signing functionality—which is reflected across command implementations, gitconfig generation, and UI updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/commit-signing

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/commands/edit.ts (1)

114-121: ⚠️ Potential issue | 🔴 Critical

Always persist commitSigning to prevent data loss when disabling signing.

Line 120 conditionally omits commitSigning when false. Since updateProfile replaces the entire profile object, false values are lost rather than persisted. A user disabling commit signing will have that change silently dropped.

Change:

         const updatedProfile = {
           name: profileName,
           gitUserName,
           gitUserEmail,
           sshKeyPath,
           directories,
-          ...(commitSigning && { commitSigning }),
+          commitSigning,
         };

Note: src/commands/add.ts has the same pattern at line 97 and needs the same fix.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/edit.ts` around lines 114 - 121, The object literal for
updatedProfile currently uses a conditional spread that omits commitSigning when
it's falsy, causing false to be lost when updateProfile replaces the entire
profile; change the construction to always include the commitSigning property
(e.g., set commitSigning: commitSigning) so false persists, and apply the same
fix in the analogous object in add.ts (the place where updatedProfile / new
profile is built before calling updateProfile).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/add.ts`:
- Around line 70-73: The prompt currently accepts commitSigning without
verifying Git version; add a Git version check (require >= 2.34) before
persisting the user's choice in both the add and edit flows. After you gather
the user's answer (commitSigning from confirm/t().commitSigningPrompt) call a
helper like ensureGitVersionOrThrow or inline a check using spawn/exec to run
`git --version`, parse the semver, and if the installed Git is < 2.34,
override/reject enabling commitSigning (set commitSigning = false) and surface a
clear message/error to the user; update both the code paths that set/persist
commitSigning in src/commands/add (where commitSigning is declared) and
src/commands/edit (where commitSigning is handled) so they use the same
validation function/name. Ensure the version check runs before any persistence
step so unsupported configurations are never saved.

In `@src/commands/list.ts`:
- Around line 47-49: The hardcoded signing badge text is rendered in
src/commands/list.ts where signing is set to profile.commitSigning ?
chalk.cyan(' [signing]') : ''; change this to use the localization/messages API
instead of a literal string: fetch the appropriate localized string key (e.g.,
messages.get('commands.list.signing') or msg('commands.list.signing')) and use
that value inside chalk.cyan when building the signing variable, then keep the
console.log(`${marker}${name}${email}${dirs}${signing}`) usage; ensure the
messages import/lookup used in other command files is reused so the badge is
localized for all locales.

In `@src/core/gitconfig.ts`:
- Line 56: The current direct call to writeFileSync(configPath,
lines.join('\n'), 'utf-8') can corrupt gitconfig-{profile} if interrupted;
update the profile gitconfig generation to perform an atomic write instead
(e.g., write to a temp file and atomically rename or use a library like
write-file-atomic) so the final replace of configPath is atomic; locate the
writeSite by the writeFileSync invocation and the variables configPath and lines
(or the surrounding function that assembles the gitconfig content) and replace
that call with an atomic-write helper that writes the joined lines and then
atomically moves them into place.

---

Outside diff comments:
In `@src/commands/edit.ts`:
- Around line 114-121: The object literal for updatedProfile currently uses a
conditional spread that omits commitSigning when it's falsy, causing false to be
lost when updateProfile replaces the entire profile; change the construction to
always include the commitSigning property (e.g., set commitSigning:
commitSigning) so false persists, and apply the same fix in the analogous object
in add.ts (the place where updatedProfile / new profile is built before calling
updateProfile).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 40891d89-9555-4ab0-8a27-a7abef60f6b9

📥 Commits

Reviewing files that changed from the base of the PR and between ff369f8 and 16c0d1c.

📒 Files selected for processing (11)
  • README.md
  • docs/ko-KR/README.md
  • src/commands/add.ts
  • src/commands/edit.ts
  • src/commands/list.ts
  • src/commands/status.ts
  • src/core/gitconfig.ts
  • src/i18n/locales/en.ts
  • src/i18n/locales/ko.ts
  • src/i18n/types.ts
  • src/types/config.ts

Comment thread src/commands/add.ts Outdated
Comment thread src/commands/list.ts Outdated
Comment thread src/core/gitconfig.ts Outdated
- Always persist commitSigning so disabling is not silently dropped
- Check Git >= 2.34 before prompting for SSH signing, warn and skip otherwise
- Localize list command signing badge via i18n
- Write profile gitconfig atomically to prevent corruption on interrupt

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/edit.ts`:
- Around line 82-95: The code wrongly flips commitSigning to false when
supportsSshCommitSigning() returns false, which rewrites a previously-signed
profile; instead, when supportsSshCommitSigning() is false and currentSigning is
true, log the warning via logger.warn(t().commitSigningUnsupported) but do not
change the data—leave commitSigning set to currentSigning so
generateProfileGitconfig() preserves signing config; only prompt and allow
changes when supportsSshCommitSigning() is true (functions/variables referenced:
supportsSshCommitSigning(), currentSigning, commitSigning,
generateProfileGitconfig()).

In `@src/core/gitconfig.ts`:
- Around line 21-24: The writeFileAtomic function currently uses a fixed temp
name `${path}.ghem-tmp` which allows concurrent processes to collide; update
writeFileAtomic to generate a unique temp path per invocation (e.g., include
process.pid plus a random/UUID or use a per-write tmp file in the same directory
via mkdtemp semantics) before writeFileSync and renameSync, ensure the temp file
is created in the same directory as path to avoid cross-device rename issues,
and make sure any temp-file cleanup on error is handled so renameSync in
writeFileAtomic cannot stomp or fail due to another process's temp file;
reference the writeFileAtomic function to implement these changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d7bf2cc6-4bde-41d3-9325-e39753f14961

📥 Commits

Reviewing files that changed from the base of the PR and between 16c0d1c and 68ea652.

📒 Files selected for processing (8)
  • src/commands/add.ts
  • src/commands/edit.ts
  • src/commands/list.ts
  • src/core/git.ts
  • src/core/gitconfig.ts
  • src/i18n/locales/en.ts
  • src/i18n/locales/ko.ts
  • src/i18n/types.ts

Comment thread src/commands/edit.ts
Comment thread src/core/gitconfig.ts Outdated
- edit: keep existing commitSigning value when Git < 2.34 (warn only)
  so previously-signed profiles are not silently rewritten
- gitconfig: use pid+random suffix for atomic temp file and clean up
  on failure to avoid collisions between concurrent processes
@Jamkris
Jamkris merged commit 07e63fd into main Apr 13, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant