a native ios client for gitbay

client ios swift

https://gitbay.org

Commit be7cec8be9

be7cec8be969dd3cf782764c13f76000a3dc40a7

parent: b889230698

Verified · cmc

cmc <hello@cleberg.net> · 2026-08-27T06:41:49Z

fix: stdin-carrying writes need --file - in argv

bodyFrom only reads stdin when argv says --file -; without it the
inline message wins and an empty one is a usage error. Verified against
production: issue comment with bare stdin returns exit 2 'empty
comment', with --file - it lands. Fixes mr comment, mr diff-comment
--reply, and issue comment, which all sent bare stdin.

Ref #11
gitbay/Issues/IssueDetailViewModel.swift +2 −1
@@ -32,7 +32,8 @@ final class IssueDetailViewModel {
3232 }
3333
3434 func comment(_ text: String) async {
35 await perform(["issue", "comment"] + ref, stdin: text)
35 // stdin is only read when argv carries --file -.
36 await perform(["issue", "comment"] + ref + ["--file", "-"], stdin: text)
3637 }
3738
3839 func close() async {
gitbay/MRs/MRDetailViewModel.swift +4 −3
@@ -59,8 +59,9 @@ final class MRDetailViewModel {
5959 }
6060
6161 func comment(_ text: String) async {
62 // Long text goes in stdin, the CLI's --file - discipline.
63 await perform(["mr", "comment"] + ref, stdin: text)
62 // Long text travels in stdin, but the server only reads it when
63 // argv says so: --file - is required, not implied.
64 await perform(["mr", "comment"] + ref + ["--file", "-"], stdin: text)
6465 }
6566
6667 func merge(strategy: String? = nil) async {
@@ -79,7 +80,7 @@ final class MRDetailViewModel {
7980
8081 func reply(to thread: ReviewThread, _ text: String) async {
8182 await perform(
82 ["mr", "diff-comment"] + ref + ["--reply", String(thread.id)],
83 ["mr", "diff-comment"] + ref + ["--reply", String(thread.id), "--file", "-"],
8384 stdin: text
8485 )
8586 }
gitbayTests/IssueBuildViewModelTests.swift +1 −1
@@ -87,7 +87,7 @@ struct IssueDetailViewModelTests {
8787 let write = stub.seen[1]
8888 #expect(write.method == "POST")
8989 let body = try #require(try JSONSerialization.jsonObject(with: write.body) as? [String: Any])
90 #expect(body["argv"] as? [String] == ["issue", "comment", "krz/gitbay", "11"])
90 #expect(body["argv"] as? [String] == ["issue", "comment", "krz/gitbay", "11", "--file", "-"])
9191 #expect(body["stdin"] as? String == "triaged from the phone")
9292 }
9393
gitbayTests/MRViewModelTests.swift +2 −2
@@ -228,7 +228,7 @@ struct MRDetailViewModelTests {
228228
229229 let write = stub.seen[3]
230230 let body = try #require(try JSONSerialization.jsonObject(with: write.body) as? [String: Any])
231 #expect(body["argv"] as? [String] == ["mr", "comment", "krz/gitbay", "7"])
231 #expect(body["argv"] as? [String] == ["mr", "comment", "krz/gitbay", "7", "--file", "-"])
232232 #expect(body["stdin"] as? String == "long review text\nwith lines")
233233 }
234234
@@ -273,7 +273,7 @@ struct MRDetailViewModelTests {
273273 let write = stub.seen[3]
274274 let body = try #require(try JSONSerialization.jsonObject(with: write.body) as? [String: Any])
275275 #expect(body["argv"] as? [String] ==
276 ["mr", "diff-comment", "krz/gitbay", "7", "--reply", "3"])
276 ["mr", "diff-comment", "krz/gitbay", "7", "--reply", "3", "--file", "-"])
277277 #expect(body["stdin"] as? String == "because 5xx is transient")
278278 }
279279 }