feat(machine): setup verification and install flow integration#107
feat(machine): setup verification and install flow integration#107nvandessel merged 3 commits intomainfrom
Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughAdds SSH/GPG verification and related CLI, doctor checks, prompt default resolution, and SSH setup integration: new verification module, a "machine keys verify" command, doctor SSH checks, prompt defaults resolver, and setupKeys in installer. (49 words) Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant CLI as CLI
participant Verify as Verifier
participant GitConfig as GitConfig
participant SSH as SSH/GitHub
participant GPG as GPG
participant Output as Output
User->>CLI: machine keys verify
CLI->>Verify: RunAllVerifications(ctx, gpgKeyID)
Verify->>GitConfig: VerifyGitConfig()
GitConfig-->>Verify: user.name, user.email, signingkey
Verify->>SSH: VerifySSHGitHub(ctx)
SSH-->>Verify: VerifyResult (pass/fail/skip)
alt gpg key provided
Verify->>GPG: VerifyGPGSign(keyID)
GPG-->>Verify: VerifyResult (pass/fail)
end
Verify-->>CLI: []VerifyResult
CLI->>Output: Render results with icons
Output-->>User: Display statuses
CLI->>User: Exit code (0 if all pass, 1 if any fail)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryAdds comprehensive verification system for SSH, GPG, and git configuration with proper timeout handling and integrates key setup into the installation flow. Major Changes:
Code Quality:
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| internal/machine/prompts.go | added smart defaults for git config (name/email/GPG keys), transforms signing_key to select menu when GPG keys detected |
| internal/machine/verify.go | new verification system for SSH/GPG/git config with proper timeout/cancel distinction and cross-platform os.DevNull usage |
| internal/setup/setup.go | integrated key setup step before machine config, properly skipped in non-interactive mode with informative output |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
Start[g4d install] --> Detect[Detect Platform]
Detect --> Deps[Install Dependencies]
Deps --> Stow[Stow Configs]
Stow --> External[Clone External Deps]
External --> KeySetup{Skip Keys?}
KeySetup -->|No + Interactive| DetectKeys[Detect SSH Keys]
KeySetup -->|Yes / Auto Mode| MachineConfig[Machine Configuration]
DetectKeys --> CheckGH{gh CLI<br/>authenticated?}
CheckGH -->|Yes| ShowKeys[Display Found Keys]
CheckGH -->|No| ShowKeys
ShowKeys --> MachineConfig
MachineConfig --> SmartDefaults{Need Config?}
SmartDefaults -->|Yes| AutoDetect[Auto-detect:<br/>- git user.name<br/>- git user.email<br/>- GPG keys]
SmartDefaults -->|No| Done[Installation Complete]
AutoDetect --> Prompt[Prompt with Defaults]
Prompt --> Render[Render & Write Config]
Render --> Done
Verify[g4d machine keys verify] --> RunChecks{Run All Checks}
RunChecks --> GitName[Verify git user.name]
RunChecks --> GitEmail[Verify git user.email]
RunChecks --> GitSignKey[Verify git signingkey]
RunChecks --> SSHCheck[Verify SSH GitHub]
RunChecks --> GPGCheck{GPG key<br/>configured?}
SSHCheck --> SSHCmd[ssh -T git@github.com<br/>with 10s timeout]
SSHCmd --> SSHResult{Result?}
SSHResult -->|Successfully authenticated| PassSSH[✓ PASS]
SSHResult -->|Timeout| SkipSSH[⊘ SKIP: timed out]
SSHResult -->|Cancelled| SkipCancel[⊘ SKIP: cancelled]
SSHResult -->|Error| FailSSH[✗ FAIL]
GPGCheck -->|Yes| GPGSign[gpg --sign test]
GPGCheck -->|No| SkipGPG[⊘ SKIP]
GPGSign --> GPGResult{Result?}
GPGResult -->|Success| PassGPG[✓ PASS]
GPGResult -->|Error| FailGPG[✗ FAIL]
GitName --> ResultName{Configured?}
ResultName -->|Yes| PassName[✓ PASS]
ResultName -->|No| FailName[✗ FAIL]
Doctor[g4d doctor] --> CheckSSHKeys[Check SSH Keys]
CheckSSHKeys --> CheckGitHubSSH[Check GitHub SSH]
CheckGitHubSSH --> OtherChecks[Other Health Checks]
Last reviewed commit: ac0ca17
🌴 Frond Stackfeat/setup-verification has been merged. 🎉 Managed by frond |
a5cc141 to
8914fcb
Compare
305f613 to
c84b4f5
Compare
|
@greptileai review |
internal/machine/prompts.go
Outdated
| } else if val == "Enter manually..." { | ||
| val = ManualEntryValue |
There was a problem hiding this comment.
ManualEntryValue is set but never consumed. If a user selects "Enter manually...", the value __manual__ will be written directly to the config file instead of prompting for actual input.
| } else if val == "Enter manually..." { | |
| val = ManualEntryValue | |
| } else if val == "Enter manually..." { | |
| // TODO: Implement manual entry prompt flow | |
| // For now, fall back to empty string | |
| val = "" |
internal/setup/setup.go
Outdated
| } | ||
| sshDir := filepath.Join(home, ".ssh") | ||
|
|
||
| keys, _ := machine.DetectAllSSHKeys(sshDir) |
There was a problem hiding this comment.
Error from DetectAllSSHKeys is silently discarded, which could hide legitimate errors like permission issues or filesystem problems.
| keys, _ := machine.DetectAllSSHKeys(sshDir) | |
| keys, err := machine.DetectAllSSHKeys(sshDir) | |
| if err != nil { | |
| progress(opts, fmt.Sprintf("⚠ Error detecting SSH keys: %v", err)) | |
| keys = []machine.SSHKey{} // Continue with empty list | |
| } |
|
@greptileai review |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #107 +/- ##
==========================================
+ Coverage 48.74% 48.91% +0.16%
==========================================
Files 109 110 +1
Lines 12261 12466 +205
==========================================
+ Hits 5977 6098 +121
- Misses 6284 6368 +84
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
internal/machine/prompts_test.go (1)
235-247: Consider renaming test for clarity.The test name
TestResolveDefaults_NoCopyis slightly misleading since it's testing that the function does copy and doesn't mutate the original. A clearer name would beTestResolveDefaults_DoesNotMutateOriginal.💡 Suggested rename
-func TestResolveDefaults_NoCopy(t *testing.T) { - // Verify original prompts are not mutated +func TestResolveDefaults_DoesNotMutateOriginal(t *testing.T) { + // Verify resolveDefaults returns a copy and does not mutate input🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/machine/prompts_test.go` around lines 235 - 247, Rename the test function TestResolveDefaults_NoCopy to TestResolveDefaults_DoesNotMutateOriginal to better reflect its intent; update the function declaration and any references to it in the file or test suite, keeping the body intact (the test calls resolveDefaults with a config.MachinePrompt and asserts original.Prompts[0].Default remains empty) so the symbol names to change are the test function identifier TestResolveDefaults_NoCopy and its usage, while leaving resolveDefaults and config.MachinePrompt untouched.internal/setup/setup.go (1)
40-41: Unused result fieldsKeysGeneratedandKeysRegistered.These fields are declared but never populated by
setupKeys. If key generation/registration isn't implemented yet, consider either:
- Removing these fields until the functionality is added
- Adding a TODO comment explaining the planned usage
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/setup/setup.go` around lines 40 - 41, The struct fields KeysGenerated and KeysRegistered are declared but never populated by setupKeys; either remove these unused fields from the struct or mark them with a TODO and wire them up in setupKeys later. Locate the declarations of KeysGenerated and KeysRegistered in the same struct and either delete those fields, or add a clear TODO comment referencing setupKeys and describe the intended contents (paths of generated SSH keys and descriptions of registered keys) so future work knows to populate them when implementing key generation/registration. Ensure any removal updates any references to those fields elsewhere, and if keeping them, add a brief comment and leave a small TODO ticket ID or note for implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/machine/prompts.go`:
- Around line 14-16: The ManualEntryValue sentinel ("__manual__") is being
passed through into the template renderer and rendered literally; update the
flow to detect and handle this sentinel before calling
RenderMachineConfig/tmpl.Execute (or inside RenderMachineConfig) by validating
the values map for ManualEntryValue entries, and when found either prompt the
user for actual input (re-prompt the selection that set ManualEntryValue) or
return a validation error so the caller can request manual input; reference the
ManualEntryValue constant, the code path that sets it (the "Enter manually..."
selection) and the RenderMachineConfig/tmpl.Execute call when adding the check
and handling logic.
In `@internal/machine/verify.go`:
- Line 77: Replace the hard-coded "/dev/null" arguments in the exec.Command
invocation that constructs cmd in verify.go with the platform-agnostic
os.DevNull; update the exec.Command call that currently uses ("gpg", "--batch",
"--no-tty", "--yes", "--sign", "--default-key", keyID, "--output", "/dev/null",
"/dev/null") to use os.DevNull for both occurrences, and add the necessary
import for the os package so the code compiles cross-platform.
---
Nitpick comments:
In `@internal/machine/prompts_test.go`:
- Around line 235-247: Rename the test function TestResolveDefaults_NoCopy to
TestResolveDefaults_DoesNotMutateOriginal to better reflect its intent; update
the function declaration and any references to it in the file or test suite,
keeping the body intact (the test calls resolveDefaults with a
config.MachinePrompt and asserts original.Prompts[0].Default remains empty) so
the symbol names to change are the test function identifier
TestResolveDefaults_NoCopy and its usage, while leaving resolveDefaults and
config.MachinePrompt untouched.
In `@internal/setup/setup.go`:
- Around line 40-41: The struct fields KeysGenerated and KeysRegistered are
declared but never populated by setupKeys; either remove these unused fields
from the struct or mark them with a TODO and wire them up in setupKeys later.
Locate the declarations of KeysGenerated and KeysRegistered in the same struct
and either delete those fields, or add a clear TODO comment referencing
setupKeys and describe the intended contents (paths of generated SSH keys and
descriptions of registered keys) so future work knows to populate them when
implementing key generation/registration. Ensure any removal updates any
references to those fields elsewhere, and if keeping them, add a brief comment
and leave a small TODO ticket ID or note for implementation.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
cmd/g4d/machine.gointernal/doctor/check.gointernal/machine/prompts.gointernal/machine/prompts_test.gointernal/machine/verify.gointernal/machine/verify_test.gointernal/setup/setup.go
| if cmdCtx.Err() != nil { | ||
| result.Status = VerifySkip | ||
| result.Message = "SSH verification timed out" | ||
| return result |
There was a problem hiding this comment.
message always says "timed out" but cmdCtx.Err() can be context.Canceled (parent cancelled) or context.DeadlineExceeded (timeout). distinguish between them
| if cmdCtx.Err() != nil { | |
| result.Status = VerifySkip | |
| result.Message = "SSH verification timed out" | |
| return result | |
| if cmdCtx.Err() != nil { | |
| result.Status = VerifySkip | |
| if cmdCtx.Err() == context.DeadlineExceeded { | |
| result.Message = "SSH verification timed out" | |
| } else { | |
| result.Message = "SSH verification cancelled" | |
| } | |
| return result | |
| } |
|
@greptileai review |
Add verification system for machine setup health: - VerifySSHGitHub() tests SSH auth to GitHub with timeout - VerifyGPGSign() validates GPG signing capability - VerifyGitConfig() checks user.name, email, signingkey - RunAllVerifications() aggregates all checks Integrate with install and doctor flows: - New key setup step in install flow (before machine config) - SSH keys and GitHub SSH doctor checks - CLI: g4d machine keys verify Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… handling - Add VerifyPass/VerifyFail/VerifySkip constants for type-safe status checks - Replace all status string literals with constants across verify, doctor, cmd - Add cobra.NoArgs to machineKeysVerifyCmd - Fix context variable shadowing in VerifySSHGitHub (ctx -> cmdCtx) - Surface DetectAllSSHKeys error in setupKeys instead of discarding Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… cancel
- Replace hardcoded /dev/null with os.DevNull in GPG sign verification
- Distinguish context.DeadlineExceeded ("timed out") from context.Canceled
("cancelled") in SSH verification
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
internal/machine/verify.go (1)
37-37: ReconsiderStrictHostKeyChecking=accept-newfor TOFU risk, butStrictHostKeyChecking=yesis impractical here.
accept-newdoes perform trust-on-first-use (TOFU) and appends the host key to the user's~/.ssh/known_hostson first connection. This presents a brief window for MITM attacks and permanently modifies the user's SSH trust state.However, suggesting
StrictHostKeyChecking=yesisn't practical for a verification check—most users won't have GitHub's key pre-established inknown_hosts. To address the security concern without blocking verification:
- Fetch GitHub's public key programmatically, validate it offline, and use
UserKnownHostsFile=/dev/null(requires known key in advance), or- Document the TOFU risk and consider whether this verification step justifies the trust mutation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/machine/verify.go` at line 37, The current exec.CommandContext invocation uses "StrictHostKeyChecking=accept-new" (cmd created in verify.go) which causes TOFU and mutates the user's known_hosts; instead, change the verification to fetch GitHub's SSH host key (or its fingerprint) ahead of time and validate it offline, write that trusted key to a temporary known_hosts file, and call exec.CommandContext("ssh", "-T", "-o", "UserKnownHostsFile=<tempfile>", "-o", "StrictHostKeyChecking=yes", "-o", "BatchMode=yes", "git@github.com") so the check will only accept the pre-validated key; if you cannot fetch/validate the key programmatically, replace the current behavior with an explicit documented warning about the TOFU risk and surface a user-facing opt-in before mutating known_hosts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/machine/verify.go`:
- Around line 102-135: The three calls to GetGitUserName, GetGitUserEmail, and
GetGitSigningKey currently discard errors (e.g., name, _ := GetGitUserName())
and always treat empty values as "not configured"; change each to check the
returned error immediately, and when err != nil append a VerifyResult (same Name
values: "git-user-name", "git-user-email", "git-user-signingkey" or existing
names) with Status=VerifyFail and Message set to the wrapped error (use
fmt.Errorf("reading git config %s: %w", "<field>", err)) so the real failure is
reported; only when err==nil continue to check empty string and append
VerifyPass with the value or VerifyFail "not configured" as appropriate.
- Around line 61-64: When a command returns err but stderrStr may be empty
(e.g., binary not found), wrap the original error with context using fmt.Errorf
and %w instead of only logging stderrStr; update the error assignments around
the SSH verification blocks that set result.Status = VerifyFail and
result.Message = fmt.Sprintf("SSH authentication failed: %s",
strings.TrimSpace(stderrStr)) (and the similar block that sets VerifyFail later)
to set result.Message using fmt.Errorf("SSH authentication failed: %s: %w",
strings.TrimSpace(stderrStrOrPlaceholder), err). Ensure you include the original
err via %w so callers can unwrap it, and use a placeholder or err.String when
stderrStr is empty to preserve error detail.
---
Nitpick comments:
In `@internal/machine/verify.go`:
- Line 37: The current exec.CommandContext invocation uses
"StrictHostKeyChecking=accept-new" (cmd created in verify.go) which causes TOFU
and mutates the user's known_hosts; instead, change the verification to fetch
GitHub's SSH host key (or its fingerprint) ahead of time and validate it
offline, write that trusted key to a temporary known_hosts file, and call
exec.CommandContext("ssh", "-T", "-o", "UserKnownHostsFile=<tempfile>", "-o",
"StrictHostKeyChecking=yes", "-o", "BatchMode=yes", "git@github.com") so the
check will only accept the pre-validated key; if you cannot fetch/validate the
key programmatically, replace the current behavior with an explicit documented
warning about the TOFU risk and surface a user-facing opt-in before mutating
known_hosts.
| if err != nil { | ||
| result.Status = VerifyFail | ||
| result.Message = fmt.Sprintf("SSH authentication failed: %s", strings.TrimSpace(stderrStr)) | ||
| return result |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n internal/machine/verify.go | head -100Repository: nvandessel/go4dot
Length of output: 3483
🏁 Script executed:
# Check GO_GUIDELINES.md to understand the full context of the error wrapping guideline
fd -type f -name "GO_GUIDELINES.md" -o -name "go_guidelines.md" -o -name "*guidelines*.md" | head -20Repository: nvandessel/go4dot
Length of output: 231
🏁 Script executed:
fd -name "*[Gg][Oo]*[Gg][Uu][Ii][Dd]*" | head -20Repository: nvandessel/go4dot
Length of output: 291
🏁 Script executed:
find . -iname "*guideline*" -type fRepository: nvandessel/go4dot
Length of output: 79
🏁 Script executed:
cat -n GO_GUIDELINES.mdRepository: nvandessel/go4dot
Length of output: 3910
Use fmt.Errorf to wrap error context when stderr is empty.
On lines 63 and 88, error messages only use stderr. If process execution fails (e.g., binary not found), stderr can be empty and the error detail is lost. Follow the guideline to wrap errors with fmt.Errorf and %w for context.
🔧 Suggested fix
if err != nil {
result.Status = VerifyFail
- result.Message = fmt.Sprintf("SSH authentication failed: %s", strings.TrimSpace(stderrStr))
+ msg := strings.TrimSpace(stderrStr)
+ if msg == "" {
+ msg = fmt.Errorf("command execution failed: %w", err).Error()
+ }
+ result.Message = fmt.Sprintf("SSH authentication failed: %s", msg)
return result
}
@@
if err := cmd.Run(); err != nil {
result.Status = VerifyFail
- result.Message = fmt.Sprintf("GPG signing failed: %s", strings.TrimSpace(stderr.String()))
+ msg := strings.TrimSpace(stderr.String())
+ if msg == "" {
+ msg = fmt.Errorf("command execution failed: %w", err).Error()
+ }
+ result.Message = fmt.Sprintf("GPG signing failed: %s", msg)
return result
}Also applies to: 86-89
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@internal/machine/verify.go` around lines 61 - 64, When a command returns err
but stderrStr may be empty (e.g., binary not found), wrap the original error
with context using fmt.Errorf and %w instead of only logging stderrStr; update
the error assignments around the SSH verification blocks that set result.Status
= VerifyFail and result.Message = fmt.Sprintf("SSH authentication failed: %s",
strings.TrimSpace(stderrStr)) (and the similar block that sets VerifyFail later)
to set result.Message using fmt.Errorf("SSH authentication failed: %s: %w",
strings.TrimSpace(stderrStrOrPlaceholder), err). Ensure you include the original
err via %w so callers can unwrap it, and use a placeholder or err.String when
stderrStr is empty to preserve error detail.
| name, _ := GetGitUserName() | ||
| if name != "" { | ||
| results = append(results, VerifyResult{ | ||
| Name: "git-user-name", | ||
| Status: VerifyPass, | ||
| Message: fmt.Sprintf("user.name = %s", name), | ||
| }) | ||
| } else { | ||
| results = append(results, VerifyResult{ | ||
| Name: "git-user-name", | ||
| Status: VerifyFail, | ||
| Message: "git user.name not configured", | ||
| }) | ||
| } | ||
|
|
||
| // Check user.email | ||
| email, _ := GetGitUserEmail() | ||
| if email != "" { | ||
| results = append(results, VerifyResult{ | ||
| Name: "git-user-email", | ||
| Status: VerifyPass, | ||
| Message: fmt.Sprintf("user.email = %s", email), | ||
| }) | ||
| } else { | ||
| results = append(results, VerifyResult{ | ||
| Name: "git-user-email", | ||
| Status: VerifyFail, | ||
| Message: "git user.email not configured", | ||
| }) | ||
| } | ||
|
|
||
| // Check user.signingkey | ||
| signingKey, _ := GetGitSigningKey() | ||
| if signingKey != "" { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify function signatures and error handling patterns
echo "=== Checking GetGit function signatures ==="
rg -n -A1 'func GetGit(UserName|UserEmail|SigningKey)\(\)' internal/machine/git.go
echo -e "\n=== Checking call sites in verify.go ==="
rg -n 'GetGitUser(Name|Email)|GetGitSigningKey' internal/machine/verify.go
echo -e "\n=== Checking blank identifier patterns in verify.go ==="
rg -n ',\s*_\s*:=' internal/machine/verify.goRepository: nvandessel/go4dot
Length of output: 683
🏁 Script executed:
#!/bin/bash
# Get full context of Lines 102-135 from verify.go to confirm current state
echo "=== Lines 102-135 of verify.go ==="
sed -n '102,135p' internal/machine/verify.go | cat -nRepository: nvandessel/go4dot
Length of output: 1154
🏁 Script executed:
#!/bin/bash
# Check if GO_GUIDELINES.md exists and what it says about error handling
echo "=== Checking for GO_GUIDELINES.md ==="
find . -name "GO_GUIDELINES.md" -o -name "go_guidelines.md" -o -name "*guidelines*.md" | head -20
echo -e "\n=== Checking root directory for guideline files ==="
ls -la | grep -i guideRepository: nvandessel/go4dot
Length of output: 238
🏁 Script executed:
#!/bin/bash
# Read GO_GUIDELINES.md to verify the stated guideline about error wrapping
echo "=== Content of GO_GUIDELINES.md ==="
cat GO_GUIDELINES.mdRepository: nvandessel/go4dot
Length of output: 3470
🏁 Script executed:
#!/bin/bash
# Check the full context around Lines 102-135 to understand the function
echo "=== Full VerifyGitConfig function ==="
sed -n '95,150p' internal/machine/verify.go | cat -nRepository: nvandessel/go4dot
Length of output: 1811
Check and report errors from git config accessors instead of silently discarding them.
On lines 102, 118, and 134, errors are ignored with the blank identifier (name, _ := GetGitUserName()). When these calls fail—whether due to missing git, permission issues, or other errors—the code incorrectly reports "git user.name not configured" instead of the actual failure. This masks diagnostic information needed for troubleshooting.
🔧 Suggested fix
func VerifyGitConfig() []VerifyResult {
var results []VerifyResult
// Check user.name
- name, _ := GetGitUserName()
- if name != "" {
+ name, err := GetGitUserName()
+ if err != nil {
+ results = append(results, VerifyResult{
+ Name: "git-user-name",
+ Status: VerifyFail,
+ Message: fmt.Sprintf("failed to read git user.name: %v", err),
+ })
+ } else if name != "" {
results = append(results, VerifyResult{
Name: "git-user-name",
Status: VerifyPass,
Message: fmt.Sprintf("user.name = %s", name),
})
} else {
@@
// Check user.email
- email, _ := GetGitUserEmail()
- if email != "" {
+ email, err := GetGitUserEmail()
+ if err != nil {
+ results = append(results, VerifyResult{
+ Name: "git-user-email",
+ Status: VerifyFail,
+ Message: fmt.Sprintf("failed to read git user.email: %v", err),
+ })
+ } else if email != "" {
results = append(results, VerifyResult{
Name: "git-user-email",
Status: VerifyPass,
Message: fmt.Sprintf("user.email = %s", email),
})
} else {
@@
// Check user.signingkey
- signingKey, _ := GetGitSigningKey()
- if signingKey != "" {
+ signingKey, err := GetGitSigningKey()
+ if err != nil {
+ results = append(results, VerifyResult{
+ Name: "git-signing-key",
+ Status: VerifyFail,
+ Message: fmt.Sprintf("failed to read git user.signingkey: %v", err),
+ })
+ } else if signingKey != "" {
results = append(results, VerifyResult{
Name: "git-signing-key",
Status: VerifyPass,
Message: fmt.Sprintf("user.signingkey = %s", signingKey),
})
} else {This violates GO_GUIDELINES.md: "Check errors immediately" and "Use fmt.Errorf with %w to wrap errors for context."
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| name, _ := GetGitUserName() | |
| if name != "" { | |
| results = append(results, VerifyResult{ | |
| Name: "git-user-name", | |
| Status: VerifyPass, | |
| Message: fmt.Sprintf("user.name = %s", name), | |
| }) | |
| } else { | |
| results = append(results, VerifyResult{ | |
| Name: "git-user-name", | |
| Status: VerifyFail, | |
| Message: "git user.name not configured", | |
| }) | |
| } | |
| // Check user.email | |
| email, _ := GetGitUserEmail() | |
| if email != "" { | |
| results = append(results, VerifyResult{ | |
| Name: "git-user-email", | |
| Status: VerifyPass, | |
| Message: fmt.Sprintf("user.email = %s", email), | |
| }) | |
| } else { | |
| results = append(results, VerifyResult{ | |
| Name: "git-user-email", | |
| Status: VerifyFail, | |
| Message: "git user.email not configured", | |
| }) | |
| } | |
| // Check user.signingkey | |
| signingKey, _ := GetGitSigningKey() | |
| if signingKey != "" { | |
| name, err := GetGitUserName() | |
| if err != nil { | |
| results = append(results, VerifyResult{ | |
| Name: "git-user-name", | |
| Status: VerifyFail, | |
| Message: fmt.Sprintf("failed to read git user.name: %v", err), | |
| }) | |
| } else if name != "" { | |
| results = append(results, VerifyResult{ | |
| Name: "git-user-name", | |
| Status: VerifyPass, | |
| Message: fmt.Sprintf("user.name = %s", name), | |
| }) | |
| } else { | |
| results = append(results, VerifyResult{ | |
| Name: "git-user-name", | |
| Status: VerifyFail, | |
| Message: "git user.name not configured", | |
| }) | |
| } | |
| // Check user.email | |
| email, err := GetGitUserEmail() | |
| if err != nil { | |
| results = append(results, VerifyResult{ | |
| Name: "git-user-email", | |
| Status: VerifyFail, | |
| Message: fmt.Sprintf("failed to read git user.email: %v", err), | |
| }) | |
| } else if email != "" { | |
| results = append(results, VerifyResult{ | |
| Name: "git-user-email", | |
| Status: VerifyPass, | |
| Message: fmt.Sprintf("user.email = %s", email), | |
| }) | |
| } else { | |
| results = append(results, VerifyResult{ | |
| Name: "git-user-email", | |
| Status: VerifyFail, | |
| Message: "git user.email not configured", | |
| }) | |
| } | |
| // Check user.signingkey | |
| signingKey, err := GetGitSigningKey() | |
| if err != nil { | |
| results = append(results, VerifyResult{ | |
| Name: "git-signing-key", | |
| Status: VerifyFail, | |
| Message: fmt.Sprintf("failed to read git user.signingkey: %v", err), | |
| }) | |
| } else if signingKey != "" { | |
| results = append(results, VerifyResult{ | |
| Name: "git-signing-key", | |
| Status: VerifyPass, | |
| Message: fmt.Sprintf("user.signingkey = %s", signingKey), | |
| }) | |
| } else { | |
| results = append(results, VerifyResult{ | |
| Name: "git-signing-key", | |
| Status: VerifyFail, | |
| Message: "git user.signingkey not configured", | |
| }) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@internal/machine/verify.go` around lines 102 - 135, The three calls to
GetGitUserName, GetGitUserEmail, and GetGitSigningKey currently discard errors
(e.g., name, _ := GetGitUserName()) and always treat empty values as "not
configured"; change each to check the returned error immediately, and when err
!= nil append a VerifyResult (same Name values: "git-user-name",
"git-user-email", "git-user-signingkey" or existing names) with
Status=VerifyFail and Message set to the wrapped error (use fmt.Errorf("reading
git config %s: %w", "<field>", err)) so the real failure is reported; only when
err==nil continue to check empty string and append VerifyPass with the value or
VerifyFail "not configured" as appropriate.
095d1e0 to
ac0ca17
Compare
Summary
VerifySSHGitHub()(with timeout),VerifyGPGSign(),VerifyGitConfig(),RunAllVerifications()g4d machine keys verifyDepends on: PR 2 (feat/gh-key-registration) and PR 3 (feat/smart-prompt-defaults)
Test plan
make build && make lint && make testpassesg4d machine keys verifyshows check results with pass/fail/skip iconsg4d doctorincludes SSH keys and GitHub SSH checks🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests