Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
0fbbf41
Surface PR CI status in sidebar
Jun 26, 2026
e11b3cf
Update Swift file length budget
Jun 26, 2026
ae62cca
Merge origin/main into issue 5748
Jun 29, 2026
c4a8a98
Address PR CI status review feedback
Jun 29, 2026
1162ccd
Merge remote-tracking branch 'origin/main' into issue-5748-surface-pe…
austinywang Jun 29, 2026
038eabf
Address PR CI review nits: drop unused branch-keyed rollup helper
austinywang Jun 29, 2026
17f11c6
Add failing test: branch-lookup PRs should receive fetched CI status
austinywang Jun 30, 2026
ecf9c0e
Fetch CI rollups for branch-lookup PRs; restore bounded test waiter t…
austinywang Jun 30, 2026
8faf7ae
Degrade unknown resolved CI status to neutral instead of dropping the…
austinywang Jun 30, 2026
910af2e
Merge remote-tracking branch 'origin/main' into issue-5748-surface-pe…
austinywang Jul 2, 2026
11b393d
Merge remote-tracking branch 'origin/main' into issue-5748-surface-pe…
austinywang Jul 2, 2026
575d38e
Keep best-effort CI rollup off the critical PR-badge refresh path
austinywang Jul 2, 2026
a4895b5
Merge remote-tracking branch 'origin/main' into issue-5748-surface-pe…
austinywang Jul 2, 2026
396be69
Merge remote-tracking branch 'origin/main' into issue-5748-surface-pe…
austinywang Jul 2, 2026
a8857a4
Merge remote-tracking branch 'origin/main' into issue-5748-surface-pe…
austinywang Jul 3, 2026
9f00aa2
Add CI status decoder regression tests
austinywang Jul 3, 2026
4eec84b
Handle future CI status payloads
austinywang Jul 3, 2026
62199eb
Limit PR CI status lookups to candidates
austinywang Jul 3, 2026
414ed64
Merge remote-tracking branch 'origin/main' into issue-5748-surface-pe…
austinywang Jul 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/swift-file-length-budget.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
# Reduce counts as files shrink. CI fails if tracked files exceed this budget.
34660 CLI/cmux.swift
17954 Sources/AppDelegate.swift
16427 Sources/ContentView.swift
16474 Sources/ContentView.swift
14270 Sources/TerminalController.swift
13172 Sources/Workspace.swift
13174 Sources/Workspace.swift
12348 cmuxTests/AppDelegateShortcutRoutingTests.swift
12212 Sources/GhosttyTerminalView.swift
11669 Sources/Panels/BrowserPanel.swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public struct GitHubPullRequestProbeItem: Decodable, Equatable, Sendable {
public let headRefName: String?
/// The PR's base (target) branch name, if known.
public let baseRefName: String?
/// CI check rollup state for the PR head commit.
public let ciStatus: PullRequestCheckStatus

/// Creates a probe item.
public init(
Expand All @@ -29,7 +31,8 @@ public struct GitHubPullRequestProbeItem: Decodable, Equatable, Sendable {
updatedAt: String?,
mergedAt: String? = nil,
headRefName: String? = nil,
baseRefName: String? = nil
baseRefName: String? = nil,
ciStatus: PullRequestCheckStatus = .neutral
) {
self.number = number
self.state = state
Expand All @@ -38,5 +41,31 @@ public struct GitHubPullRequestProbeItem: Decodable, Equatable, Sendable {
self.mergedAt = mergedAt
self.headRefName = headRefName
self.baseRefName = baseRefName
self.ciStatus = ciStatus
}

private enum CodingKeys: String, CodingKey {
case number
case state
case url
case updatedAt
case mergedAt
case headRefName
case baseRefName
case ciStatus
}

public init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.number = try container.decode(Int.self, forKey: .number)
self.state = try container.decode(String.self, forKey: .state)
self.url = try container.decode(String.self, forKey: .url)
self.updatedAt = try container.decodeIfPresent(String.self, forKey: .updatedAt)
self.mergedAt = try container.decodeIfPresent(String.self, forKey: .mergedAt)
self.headRefName = try container.decodeIfPresent(String.self, forKey: .headRefName)
self.baseRefName = try container.decodeIfPresent(String.self, forKey: .baseRefName)
self.ciStatus = (
try? container.decodeIfPresent(PullRequestCheckStatus.self, forKey: .ciStatus)
) ?? .neutral
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Foundation

/// Rollup state for a pull request's CI checks.
public enum PullRequestCheckStatus: String, Sendable, Equatable, Decodable {
/// Checks are absent, queued, in progress, or unavailable.
case neutral
/// The check rollup passed.
case success
/// The check rollup failed or errored.
case failure

/// Maps GitHub GraphQL `statusCheckRollup.state` values to sidebar states.
public init(githubStatusCheckRollupState rawState: String?) {
switch rawState?.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() {
case "SUCCESS":
self = .success
case "FAILURE", "ERROR":
self = .failure
default:
self = .neutral
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

struct WorkspacePullRequestGraphQLCommit: Decodable, Sendable {
let statusCheckRollup: WorkspacePullRequestGraphQLStatusCheckRollup?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

struct WorkspacePullRequestGraphQLCommitConnection: Decodable, Sendable {
let nodes: [WorkspacePullRequestGraphQLCommitNode?]?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

struct WorkspacePullRequestGraphQLCommitNode: Decodable, Sendable {
let commit: WorkspacePullRequestGraphQLCommit
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

struct WorkspacePullRequestGraphQLData: Decodable, Sendable {
let repository: WorkspacePullRequestGraphQLRepository?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

struct WorkspacePullRequestGraphQLPullRequestConnection: Decodable, Sendable {
let nodes: [WorkspacePullRequestGraphQLPullRequestNode?]?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Foundation

struct WorkspacePullRequestGraphQLPullRequestNode: Decodable, Sendable {
let number: Int
let headRefName: String?
let commits: WorkspacePullRequestGraphQLCommitConnection?

var ciStatus: PullRequestCheckStatus {
let state = commits?.nodes?.compactMap { $0?.commit.statusCheckRollup?.state }.last
return PullRequestCheckStatus(githubStatusCheckRollupState: state)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Foundation

struct WorkspacePullRequestGraphQLRepository: Decodable, Sendable {
let pullRequests: WorkspacePullRequestGraphQLPullRequestConnection?
let aliasedPullRequests: [WorkspacePullRequestGraphQLPullRequestNode]

var nodes: [WorkspacePullRequestGraphQLPullRequestNode?] {
(pullRequests?.nodes ?? []) + aliasedPullRequests.map(Optional.some)
}

private struct DynamicCodingKey: CodingKey {
let stringValue: String
let intValue: Int?

init?(stringValue: String) {
self.stringValue = stringValue
self.intValue = nil
}

init?(intValue: Int) {
return nil
}
}

private enum CodingKeys: String, CodingKey {
case pullRequests
}

init(from decoder: any Decoder) throws {
let fixedContainer = try decoder.container(keyedBy: CodingKeys.self)
self.pullRequests = try fixedContainer.decodeIfPresent(
WorkspacePullRequestGraphQLPullRequestConnection.self,
forKey: .pullRequests
)
let container = try decoder.container(keyedBy: DynamicCodingKey.self)
var aliasedPullRequests: [WorkspacePullRequestGraphQLPullRequestNode] = []
for key in container.allKeys {
guard key.stringValue.hasPrefix("pr"),
key.stringValue.dropFirst(2).allSatisfy(\.isNumber) else {
continue
}
if try container.decodeNil(forKey: key) {
continue
}
aliasedPullRequests.append(
try container.decode(
WorkspacePullRequestGraphQLPullRequestNode.self,
forKey: key
)
)
}
self.aliasedPullRequests = aliasedPullRequests
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Foundation

struct WorkspacePullRequestGraphQLRequestBody: Encodable, Sendable {
let query: String
let variables: WorkspacePullRequestGraphQLVariables
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

struct WorkspacePullRequestGraphQLResponse: Decodable, Sendable {
let data: WorkspacePullRequestGraphQLData?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

struct WorkspacePullRequestGraphQLStatusCheckRollup: Decodable, Sendable {
let state: String?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Foundation

struct WorkspacePullRequestGraphQLVariables: Encodable, Sendable {
let owner: String
let name: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public import Foundation
public struct WorkspacePullRequestRepoCacheEntry: Sendable {
/// When this entry was fetched.
public let fetchedAt: Date
/// The best pull request per normalized branch name.
/// The best pull request per normalized branch name, including CI rollups.
public let pullRequestsByBranch: [String: GitHubPullRequestProbeItem]
/// Branches positively known to have no pull request (so a cached entry
/// doesn't re-trigger per-branch lookups for them).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,23 @@ public struct WorkspacePullRequestResolvedItem: Sendable {
/// The ``PullRequestStatus`` raw value (`"open"`/`"merged"`/`"closed"`),
/// kept as a string so app-side status enums can bridge via `rawValue`.
public let statusRawValue: String
/// The ``PullRequestCheckStatus`` raw value (`"neutral"`/`"success"`/`"failure"`).
public let ciStatusRawValue: String
/// The branch the PR was matched for.
public let branch: String

/// Creates a resolved item.
public init(number: Int, urlString: String, statusRawValue: String, branch: String) {
public init(
number: Int,
urlString: String,
statusRawValue: String,
ciStatusRawValue: String = PullRequestCheckStatus.neutral.rawValue,
branch: String
) {
self.number = number
self.urlString = urlString
self.statusRawValue = statusRawValue
self.ciStatusRawValue = ciStatusRawValue
self.branch = branch
}
}
Loading
Loading