Commit 311bd03689

311bd03689a6311dd7a187b26f1ecd92c31f25f4

parent: 9f7b9fe809

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-07 01:08 UTC

Fix final-review findings on repo deps settings

Pull-to-refresh now reloads the deps report alongside settings, the
enable/disable reload test asserts the reload actually landed instead
of only checking argv, the toggle no longer draws when the status read
has failed, the explanation moved to the section footer, the
last-check timestamp is labeled, the unused section parameter is
dropped, and a converse success-then-failure test pins that a later
failure clears stale deps state.

Layout: unified · split

gitbay/Views/Repos/RepoSettingsView.swift +14 −12
@@ -29,7 +29,7 @@ struct RepoSettingsView: View {
29 branchesSection(loaded) 29 branchesSection(loaded)
30 mergeRulesSection(loaded) 30 mergeRulesSection(loaded)
31 daemonSection(loaded) 31 daemonSection(loaded)
32 depsSection(loaded) 32 depsSection()
33 } 33 }
34 } 34 }
35 .overlay { LoadStateOverlay(state: model.state) } 35 .overlay { LoadStateOverlay(state: model.state) }
@@ -44,7 +44,10 @@ struct RepoSettingsView: View {
44 } 44 }
45 } 45 }
46 .task { await model.loadDeps() } 46 .task { await model.loadDeps() }
47 .refreshable { await model.load() } 47 .refreshable {
48 await model.load()
49 await model.loadDeps()
50 }
48 } 51 }
49 52
50 // MARK: - Sections 53 // MARK: - Sections
@@ -208,20 +211,17 @@ struct RepoSettingsView: View {
208 } 211 }
209 } 212 }
210 213
211 private func depsSection(_ loaded: RepoSettingsViewModel.Loaded) -> some View { 214 private func depsSection() -> some View {
212 Section { 215 Section {
213 SettingToggle(title: "Dependency checks", serverValue: model.deps?.enabled ?? false) {
214 await model.setDepsEnabled($0)
215 }
216 .disabled(model.deps == nil || model.working)
217 Text("Checks tell a public registry what this repository depends on.")
218 .font(.gbSans(.caption))
219 .foregroundStyle(.secondary)
220
221 if let deps = model.deps { 216 if let deps = model.deps {
217 SettingToggle(title: "Dependency checks", serverValue: deps.enabled) {
218 await model.setDepsEnabled($0)
219 }
220 .disabled(model.working)
221
222 if deps.enabled { 222 if deps.enabled {
223 if let lastCheck = deps.lastCheck { 223 if let lastCheck = deps.lastCheck {
224 Text(lastCheck, format: .relative(presentation: .named)) 224 (Text("Last checked ") + Text(lastCheck, format: .relative(presentation: .named)))
225 .font(.gbSans(.caption)) 225 .font(.gbSans(.caption))
226 .foregroundStyle(.secondary) 226 .foregroundStyle(.secondary)
227 } else { 227 } else {
@@ -266,6 +266,8 @@ struct RepoSettingsView: View {
266 } 266 }
267 } header: { 267 } header: {
268 Text("Dependencies") 268 Text("Dependencies")
269 } footer: {
270 Text("Checks tell a public registry what this repository depends on.")
269 } 271 }
270 } 272 }
271 273
gitbayTests/DepsTests.swift +22
@@ -112,6 +112,9 @@ struct DepsActionTests {
112 await model.setDepsEnabled(true) 112 await model.setDepsEnabled(true)
113 var write = try #require(stub.seen.first { $0.method == "POST" }) 113 var write = try #require(stub.seen.first { $0.method == "POST" })
114 #expect(try argvOf(write) == ["repo", "deps", "enable", "krz/gitbay"]) 114 #expect(try argvOf(write) == ["repo", "deps", "enable", "krz/gitbay"])
115 // The write must reload deps specifically, not the whole settings
116 // screen — otherwise this enqueued status stub goes unconsumed.
117 #expect(model.deps?.enabled == true)
115 118
116 let (client2, stub2) = try makeClient() 119 let (client2, stub2) = try makeClient()
117 let model2 = RepoSettingsViewModel(client: client2, repoPath: "krz/gitbay") 120 let model2 = RepoSettingsViewModel(client: client2, repoPath: "krz/gitbay")
@@ -122,6 +125,7 @@ struct DepsActionTests {
122 await model2.setDepsEnabled(false) 125 await model2.setDepsEnabled(false)
123 write = try #require(stub2.seen.first { $0.method == "POST" }) 126 write = try #require(stub2.seen.first { $0.method == "POST" })
124 #expect(try argvOf(write) == ["repo", "deps", "disable", "krz/gitbay"]) 127 #expect(try argvOf(write) == ["repo", "deps", "disable", "krz/gitbay"])
128 #expect(model2.deps?.enabled == true)
125 } 129 }
126 130
127 /// "Could not ask" must not render as "checks are off". 131 /// "Could not ask" must not render as "checks are off".
@@ -170,4 +174,22 @@ struct DepsActionTests {
170 #expect(model.depsError == nil) 174 #expect(model.depsError == nil)
171 #expect(model.deps?.enabled == true) 175 #expect(model.deps?.enabled == true)
172 } 176 }
177
178 /// The converse of the above: a later failure must drop the stale
179 /// status, not leave it sitting alongside the new error.
180 @Test func depsClearsOnALaterFailureAfterASuccess() async throws {
181 let (client, stub) = try makeClient()
182 stub.enqueue(.init(status: 200, json: fullStatus))
183 let model = RepoSettingsViewModel(client: client, repoPath: "krz/gitbay")
184 await model.loadDeps()
185 #expect(model.deps?.enabled == true)
186
187 stub.enqueue(.init(status: 200, json: """
188 {"protocol_version":1,"error":"denied","exit_code":4}
189 """))
190 await model.loadDeps()
191
192 #expect(model.deps == nil)
193 #expect(model.depsError?.isEmpty == false)
194 }
173} 195}