Skip to content

Commit f7c32f2

Browse files
authored
refactor(git-panel): persist failure messages in app log store (#103)
1 parent e50dfae commit f7c32f2

File tree

2 files changed

+70
-11
lines changed

2 files changed

+70
-11
lines changed

Sources/Zero/Services/GitPanelService.swift

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class GitPanelService: ObservableObject {
4040
branches = try gitService.branches(in: containerName)
4141
errorMessage = nil
4242
} catch {
43-
errorMessage = error.localizedDescription
43+
reportFailure(action: "refresh", userMessage: error.localizedDescription, error: error)
4444
}
4545
}
4646

@@ -51,7 +51,7 @@ class GitPanelService: ObservableObject {
5151
try gitService.add(files: files, in: containerName)
5252
await refresh()
5353
} catch {
54-
errorMessage = error.localizedDescription
54+
reportFailure(action: "stage", userMessage: error.localizedDescription, error: error)
5555
}
5656
}
5757

@@ -62,7 +62,7 @@ class GitPanelService: ObservableObject {
6262
try gitService.addAll(in: containerName)
6363
await refresh()
6464
} catch {
65-
errorMessage = error.localizedDescription
65+
reportFailure(action: "stageAll", userMessage: error.localizedDescription, error: error)
6666
}
6767
}
6868

@@ -73,7 +73,7 @@ class GitPanelService: ObservableObject {
7373
try gitService.commit(message: message, in: containerName)
7474
await refresh()
7575
} catch {
76-
errorMessage = error.localizedDescription
76+
reportFailure(action: "commit", userMessage: error.localizedDescription, error: error)
7777
}
7878
}
7979

@@ -84,7 +84,7 @@ class GitPanelService: ObservableObject {
8484
try gitService.commitAll(message: message, in: containerName)
8585
await refresh()
8686
} catch {
87-
errorMessage = error.localizedDescription
87+
reportFailure(action: "commitAll", userMessage: error.localizedDescription, error: error)
8888
}
8989
}
9090

@@ -95,7 +95,7 @@ class GitPanelService: ObservableObject {
9595
try gitService.createAndCheckoutBranch(name: name, in: containerName)
9696
await refresh()
9797
} catch {
98-
errorMessage = error.localizedDescription
98+
reportFailure(action: "createBranch", userMessage: error.localizedDescription, error: error)
9999
}
100100
}
101101

@@ -106,7 +106,7 @@ class GitPanelService: ObservableObject {
106106
try gitService.checkout(branch: branch, in: containerName)
107107
await refresh()
108108
} catch {
109-
errorMessage = error.localizedDescription
109+
reportFailure(action: "checkout", userMessage: error.localizedDescription, error: error)
110110
}
111111
}
112112

@@ -117,7 +117,8 @@ class GitPanelService: ObservableObject {
117117
try gitService.push(in: containerName)
118118
await refresh()
119119
} catch {
120-
errorMessage = mapRemoteActionError(error, action: .push)
120+
let message = mapRemoteActionError(error, action: .push)
121+
reportFailure(action: "push", userMessage: message, error: error)
121122
}
122123
}
123124

@@ -130,11 +131,13 @@ class GitPanelService: ObservableObject {
130131
} catch {
131132
if isPullConflictError(error) {
132133
let conflictedFiles = (try? gitService.conflictedFiles(in: containerName)) ?? []
133-
errorMessage = pullConflictGuidance(conflictedFiles: conflictedFiles)
134+
let message = pullConflictGuidance(conflictedFiles: conflictedFiles)
135+
reportFailure(action: "pull", userMessage: message, error: error)
134136
return
135137
}
136138

137-
errorMessage = mapRemoteActionError(error, action: .pull)
139+
let message = mapRemoteActionError(error, action: .pull)
140+
reportFailure(action: "pull", userMessage: message, error: error)
138141
}
139142
}
140143

@@ -155,10 +158,15 @@ class GitPanelService: ObservableObject {
155158
} catch {
156159
selectedDiffTitle = "Diff · \(path)"
157160
selectedDiff = "Failed to load diff: \(error.localizedDescription)"
158-
errorMessage = error.localizedDescription
161+
reportFailure(action: "loadDiff", userMessage: error.localizedDescription, error: error)
159162
}
160163
}
161164

165+
private func reportFailure(action: String, userMessage: String, error: Error) {
166+
errorMessage = userMessage
167+
AppLogStore.shared.append("GitPanel \(action) failed: \(userMessage) [raw=\(error.localizedDescription)]")
168+
}
169+
162170
func showUntrackedDiffPlaceholder(for path: String) {
163171
selectedDiffTitle = "Untracked · \(path)"
164172
selectedDiff = "Untracked files have no git diff until staged."

Tests/ZeroTests/GitPanelServiceTests.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ private final class GitPanelMockContainerRunner: ContainerRunning {
2727

2828
@MainActor
2929
final class GitPanelServiceTests: XCTestCase {
30+
override func setUp() {
31+
super.setUp()
32+
AppLogStore.shared.clear()
33+
}
34+
35+
override func tearDown() {
36+
AppLogStore.shared.clear()
37+
super.tearDown()
38+
}
39+
3040
func testPushMapsNonFastForwardFailureToGuidance() async {
3141
// Given
3242
let runner = GitPanelMockContainerRunner()
@@ -129,6 +139,47 @@ final class GitPanelServiceTests: XCTestCase {
129139
XCTAssertEqual(service.errorMessage, "fatal: unexpected socket close")
130140
}
131141

142+
func testPushFailureAppendsErrorToAppLogStore() async {
143+
// Given
144+
let runner = GitPanelMockContainerRunner()
145+
runner.shellErrorsByCommandSubstring["git push"] = NSError(
146+
domain: "git",
147+
code: 1,
148+
userInfo: [NSLocalizedDescriptionKey: "fatal: unexpected socket close"]
149+
)
150+
let service = makeService(runner: runner)
151+
152+
// When
153+
await service.push()
154+
155+
// Then
156+
let logEntries = AppLogStore.shared.recentEntries()
157+
XCTAssertTrue(logEntries.contains { entry in
158+
entry.contains("GitPanel push failed") && entry.contains("fatal: unexpected socket close")
159+
})
160+
}
161+
162+
func testPullConflictAppendsGuidanceToAppLogStore() async {
163+
// Given
164+
let runner = GitPanelMockContainerRunner()
165+
runner.nextShellOutput = "Sources/Zero/Views/EditorView.swift"
166+
runner.shellErrorsByCommandSubstring["git pull"] = NSError(
167+
domain: "git",
168+
code: 1,
169+
userInfo: [NSLocalizedDescriptionKey: "Automatic merge failed; fix conflicts and then commit the result."]
170+
)
171+
let service = makeService(runner: runner)
172+
173+
// When
174+
await service.pull()
175+
176+
// Then
177+
let logEntries = AppLogStore.shared.recentEntries()
178+
XCTAssertTrue(logEntries.contains { entry in
179+
entry.contains("GitPanel pull failed") && entry.contains("Pull hit merge conflicts")
180+
})
181+
}
182+
132183
private func makeService(runner: GitPanelMockContainerRunner) -> GitPanelService {
133184
let service = GitPanelService()
134185
service.setup(gitService: GitService(runner: runner), containerName: "zero-dev-container")

0 commit comments

Comments
 (0)