Commit d7fac80c5f

d7fac80c5f39e46eb7614e23711bbed89355757f

parent: ef8d699532

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-29 02:49 UTC

Send the webhook secret on stdin

webhook add refuses `--secret <value>` since krz/gitbay#284. Send
`--secret -` and put the secret on stdin.

Closes #21

Layout: unified · split

gitbay/Repos/WebhookViewModels.swift +6 −5
@@ -28,17 +28,18 @@ final class WebhookListViewModel {
2828 }
2929 }
3030
31 /// `webhook add <repo> <url> [--secret <s>] [--events <e>]` — an empty
31 /// `webhook add <repo> <url> [--secret -] [--events <e>]` — an empty
3232 /// secret or event list is left out, so the server applies its default.
33 /// The secret travels on stdin; the server refuses it in argv.
3334 func add(url: String, secret: String, events: String) async -> Bool {
3435 let url = url.trimmingCharacters(in: .whitespaces)
3536 guard !url.isEmpty else { return false }
3637 var argv = ["webhook", "add", repoPath, url]
3738 let secret = secret.trimmingCharacters(in: .whitespaces)
38 if !secret.isEmpty { argv += ["--secret", secret] }
39 if !secret.isEmpty { argv += ["--secret", "-"] }
3940 let events = events.trimmingCharacters(in: .whitespaces)
4041 if !events.isEmpty { argv += ["--events", events] }
41 await perform(argv)
42 await perform(argv, stdin: secret.isEmpty ? nil : secret + "\n")
4243 return actionError == nil
4344 }
4445
@@ -46,12 +47,12 @@ final class WebhookListViewModel {
4647 await perform(["webhook", "remove", repoPath, String(id)])
4748 }
4849
49 private func perform(_ argv: [String]) async {
50 private func perform(_ argv: [String], stdin: String? = nil) async {
5051 working = true
5152 actionError = nil
5253 defer { working = false }
5354 do {
54 try await client.run(argv)
55 try await client.run(argv, stdin: stdin)
5556 await load()
5657 } catch let error as GitbayError {
5758 actionError = error.userFacingMessage
gitbayTests/WebhookTests.swift +8 −1
@@ -17,6 +17,11 @@ private func argvOf(_ seen: StubProtocol.Seen) throws -> [String] {
1717 return try #require(body["argv"] as? [String])
1818}
1919
20private func stdinOf(_ seen: StubProtocol.Seen) throws -> String? {
21 let body = try #require(try JSONSerialization.jsonObject(with: seen.body) as? [String: Any])
22 return body["stdin"] as? String
23}
24
2025private let okJSON = #"{"protocol_version":1,"data":{},"exit_code":0}"#
2126
2227private let hooksJSON = """
@@ -81,9 +86,11 @@ struct WebhookListViewModelTests {
8186 #expect(first && second)
8287 let writes = try stub.seen.filter { $0.method == "POST" }.map(argvOf)
8388 #expect(writes == [
84 ["webhook", "add", "krz/gitbay", "https://a.example/h", "--secret", "s3", "--events", "push"],
89 ["webhook", "add", "krz/gitbay", "https://a.example/h", "--secret", "-", "--events", "push"],
8590 ["webhook", "add", "krz/gitbay", "https://b.example/h"],
8691 ])
92 let posts = stub.seen.filter { $0.method == "POST" }
93 #expect(try posts.map(stdinOf) == ["s3\n", nil])
8794 }
8895
8996 @Test func removeSendsTheIdThenReloads() async throws {