Skip to content

Commit a64fde1

Browse files
committed
Fix --branch and --tag for builds start
The flags were sending sourceBranchOrTag as an attribute with a branch name string, but the API requires it as a relationship pointing to a scmGitReferences resource ID. The command now resolves the branch/tag to its resource ID via a two-step lookup: fetch the workflow to get its repository ID, then query the repository's git references to find the matching ref.
1 parent d266141 commit a64fde1

File tree

4 files changed

+57
-33
lines changed

4 files changed

+57
-33
lines changed

Sources/XcodeCloudCLI/Commands/Builds/BuildsCommand.swift

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -759,18 +759,31 @@ struct BuildsStartCommand: ParsableCommand {
759759
let quiet = options.quiet
760760

761761
do {
762-
let gitReference: GitReference?
763-
if let branch = branch {
764-
gitReference = .branch(branch)
765-
} else if let tag = tag {
766-
gitReference = .tag(tag)
762+
let gitReferenceId: String?
763+
if branch != nil || tag != nil {
764+
let kind = branch != nil ? "BRANCH" : "TAG"
765+
let name = branch ?? tag!
766+
printVerbose("Resolving \(kind.lowercased()) '\(name)'...", verbose: verbose)
767+
let workflow = try runAsync { try await client.getWorkflow(id: wfId) }
768+
guard let repoId = workflow.data.relationships?.repository?.data?.id else {
769+
printError("Could not determine repository for workflow \(wfId)")
770+
throw ExitCode.failure
771+
}
772+
let refs = try runAsync { try await client.listGitReferences(repositoryId: repoId) }
773+
guard let ref = refs.data.first(where: {
774+
$0.attributes?.name == name && $0.attributes?.kind == kind && $0.attributes?.isDeleted != true
775+
}) else {
776+
printError("\(kind == "BRANCH" ? "Branch" : "Tag") '\(name)' not found in repository")
777+
throw ExitCode.failure
778+
}
779+
gitReferenceId = ref.id
767780
} else {
768-
gitReference = nil
781+
gitReferenceId = nil
769782
}
770783

771784
printVerbose("Starting build for workflow \(wfId)...", verbose: verbose)
772785
let response = try runAsync {
773-
try await client.startBuildRun(workflowId: wfId, gitReference: gitReference)
786+
try await client.startBuildRun(workflowId: wfId, gitReferenceId: gitReferenceId)
774787
}
775788

776789
if !quiet {

Sources/XcodeCloudKit/API/APIClient.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,17 @@ public actor APIClient {
5050
try await request(.getBuildRun(id: id))
5151
}
5252

53-
public func startBuildRun(workflowId: String, gitReference: GitReference? = nil) async throws -> APIResponse<CiBuildRun> {
54-
let body = CiBuildRunCreateRequest(workflowId: workflowId, gitReference: gitReference)
53+
public func startBuildRun(workflowId: String, gitReferenceId: String? = nil) async throws -> APIResponse<CiBuildRun> {
54+
let body = CiBuildRunCreateRequest(workflowId: workflowId, gitReferenceId: gitReferenceId)
5555
return try await request(.startBuildRun, body: body)
5656
}
5757

58+
// MARK: - SCM Git References
59+
60+
public func listGitReferences(repositoryId: String) async throws -> APIListResponse<ScmGitReference> {
61+
try await request(.listGitReferences(repositoryId: repositoryId))
62+
}
63+
5864
// MARK: - Build Actions
5965

6066
public func listBuildActions(buildRunId: String) async throws -> APIListResponse<CiBuildAction> {

Sources/XcodeCloudKit/API/Endpoints.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public enum Endpoint {
1717
case getIssue(id: String)
1818
case listTestResults(buildActionId: String)
1919
case getTestResult(id: String)
20+
case listGitReferences(repositoryId: String)
2021

2122
public var path: String {
2223
switch self {
@@ -53,6 +54,8 @@ public enum Endpoint {
5354
return "/v1/ciBuildActions/\(buildActionId)/testResults"
5455
case .getTestResult(let id):
5556
return "/v1/ciTestResults/\(id)"
57+
case .listGitReferences(let repositoryId):
58+
return "/v1/scmRepositories/\(repositoryId)/gitReferences"
5659
}
5760
}
5861

@@ -94,6 +97,10 @@ public enum Endpoint {
9497
if let cursor = cursor {
9598
items.append(URLQueryItem(name: "cursor", value: cursor))
9699
}
100+
case .getWorkflow:
101+
items.append(URLQueryItem(name: "include", value: "repository"))
102+
case .listGitReferences:
103+
items.append(URLQueryItem(name: "limit", value: "200"))
97104
default:
98105
break
99106
}

Sources/XcodeCloudKit/API/Models/APIModels.swift

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -373,48 +373,46 @@ public struct RelationshipList: Codable, Sendable {
373373
public struct CiBuildRunCreateRequest: Codable, Sendable {
374374
public let data: CiBuildRunCreateData
375375

376-
public init(workflowId: String, gitReference: GitReference? = nil) {
377-
self.data = CiBuildRunCreateData(workflowId: workflowId, gitReference: gitReference)
376+
public init(workflowId: String, gitReferenceId: String? = nil) {
377+
self.data = CiBuildRunCreateData(workflowId: workflowId, gitReferenceId: gitReferenceId)
378378
}
379379
}
380380

381381
public struct CiBuildRunCreateData: Codable, Sendable {
382382
public var type: String
383-
public let attributes: CiBuildRunCreateAttributes?
384383
public let relationships: CiBuildRunCreateRelationships
385384

386-
public init(workflowId: String, gitReference: GitReference? = nil) {
385+
public init(workflowId: String, gitReferenceId: String? = nil) {
387386
self.type = "ciBuildRuns"
388-
self.attributes = gitReference.map { CiBuildRunCreateAttributes(sourceBranchOrTag: $0) }
389-
self.relationships = CiBuildRunCreateRelationships(workflowId: workflowId)
390-
}
391-
}
392-
393-
public struct CiBuildRunCreateAttributes: Codable, Sendable {
394-
public let sourceBranchOrTag: GitReference?
395-
}
396-
397-
public struct GitReference: Codable, Sendable {
398-
public let kind: String // "BRANCH" or "TAG"
399-
public let name: String
400-
401-
public static func branch(_ name: String) -> GitReference {
402-
GitReference(kind: "BRANCH", name: name)
403-
}
404-
405-
public static func tag(_ name: String) -> GitReference {
406-
GitReference(kind: "TAG", name: name)
387+
self.relationships = CiBuildRunCreateRelationships(workflowId: workflowId, gitReferenceId: gitReferenceId)
407388
}
408389
}
409390

410391
public struct CiBuildRunCreateRelationships: Codable, Sendable {
411392
public let workflow: CreateRelationship
393+
public let sourceBranchOrTag: CreateRelationship?
412394

413-
public init(workflowId: String) {
395+
public init(workflowId: String, gitReferenceId: String? = nil) {
414396
self.workflow = CreateRelationship(id: workflowId, type: "ciWorkflows")
397+
self.sourceBranchOrTag = gitReferenceId.map { CreateRelationship(id: $0, type: "scmGitReferences") }
415398
}
416399
}
417400

401+
// MARK: - SCM Git References
402+
403+
public struct ScmGitReference: Codable, Sendable, Identifiable {
404+
public let type: String
405+
public let id: String
406+
public let attributes: ScmGitReferenceAttributes?
407+
}
408+
409+
public struct ScmGitReferenceAttributes: Codable, Sendable {
410+
public let name: String?
411+
public let canonicalName: String?
412+
public let isDeleted: Bool?
413+
public let kind: String? // "BRANCH" or "TAG"
414+
}
415+
418416
public struct CreateRelationship: Codable, Sendable {
419417
public let data: RelationshipData
420418

0 commit comments

Comments
 (0)