gitbay/Builds/BuildListViewModel.swift
56 lines · 1818 bytes
1import Foundation
2import Observation
3
4/// `build list <repo>`.
5@Observable
6@MainActor
7final class BuildListViewModel {
8
9 private(set) var state: LoadState<[Build]> = .loading
10 /// The jobs a trigger can name. Empty when the repo has no CI
11 /// config — the web hides its trigger form in that case.
12 private(set) var jobs: [CIJob] = []
13 private(set) var actionError: String?
14 private(set) var working = false
15
16 private let client: GitbayClient
17 let repoPath: String
18
19 init(client: GitbayClient, repoPath: String) {
20 self.client = client
21 self.repoPath = repoPath
22 }
23
24 func load() async {
25 do {
26 let builds = try await client.readList(["build", "list", repoPath], of: Build.self)
27 state = builds.isEmpty
28 ? .empty("No builds. Builds run when a push touches a repo with a .gitbay/ job file.")
29 : .loaded(builds.sorted { $0.number > $1.number })
30 } catch {
31 state = .from(error)
32 }
33 }
34
35 /// `build jobs <owner/name>` — what the picker offers. A repo
36 /// without a CI config answers "not found"; that means no jobs, not
37 /// a failure worth showing.
38 func loadJobs() async {
39 jobs = (try? await client.readList(["build", "jobs", repoPath], of: CIJob.self)) ?? []
40 }
41
42 /// `build trigger <owner/name> <job>` — queue a job now.
43 func trigger(job: String) async {
44 working = true
45 actionError = nil
46 defer { working = false }
47 do {
48 try await client.run(["build", "trigger", repoPath, job])
49 await load()
50 } catch let error as GitbayError {
51 actionError = error.userFacingMessage
52 } catch {
53 actionError = GitbayError.transport(error).userFacingMessage
54 }
55 }
56}