Make PR detail load resilient to per-endpoint 403; bump to 1.3
Build iOS App / Build .ipa and publish source (push) Successful in 23s

Opening a PR could fail the whole screen when any one detail sub-request
returns 401/403 (e.g. a token scope the repo's Actions endpoint needs).
Refactor load() so the PR fetch is the only fatal call; comments/reviews/
files/commits/runs each fail independently, are logged, and the rest still
render. Also log actions/runs failures for visibility. Bump to 1.3.
This commit is contained in:
Hermes committed 2026-08-12 21:00:22 +00:00
1 parent 58eb145717
commit 9e71b6a8b9
2 files changed
+26 -19

No files matched your search

+4 -4
View File
@@ -187,7 +187,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
@@ -197,7 +197,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2;
MARKETING_VERSION = 1.3;
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -211,7 +211,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
@@ -221,7 +221,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2;
MARKETING_VERSION = 1.3;
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
+22 -15
View File
@@ -41,23 +41,28 @@ final class PRDetailViewModel: ObservableObject {
isLoading = true
errorMessage = nil
defer { isLoading = false }
// Fetch each piece independently. A single sub-request returning 403
// (e.g. a token scope the repo's Actions needs) must NOT wipe the whole
// detail screen — catch it, log it, keep the rest. Only a failure to
// load the PR itself is fatal.
do {
async let prT = client.pullRequest(owner: owner, repo: repo.name, number: prNumber)
async let commentsT = client.comments(owner: owner, repo: repo.name, number: prNumber)
async let reviewsT = client.reviews(owner: owner, repo: repo.name, number: prNumber)
async let filesT = client.changedFiles(owner: owner, repo: repo.name, number: prNumber)
async let commitsT = client.commits(owner: owner, repo: repo.name, number: prNumber)
pr = try await prT
comments = try await commentsT
reviews = try await reviewsT
files = try await filesT
commits = try await commitsT
// CI runs depend on the head SHA from the PR.
if let sha = pr?.head?.sha, !sha.isEmpty {
await loadRuns(client: client, sha: sha)
}
pr = try await client.pullRequest(owner: owner, repo: repo.name, number: prNumber)
} catch {
LogStore.shared.log(.error, "PR \(prNumber): load failed: \(error.localizedDescription)")
errorMessage = error.localizedDescription
return
}
async let commentsT = client.comments(owner: owner, repo: repo.name, number: prNumber)
async let reviewsT = client.reviews(owner: owner, repo: repo.name, number: prNumber)
async let filesT = client.changedFiles(owner: owner, repo: repo.name, number: prNumber)
async let commitsT = client.commits(owner: owner, repo: repo.name, number: prNumber)
comments = (try? await commentsT) ?? []
reviews = (try? await reviewsT) ?? []
files = (try? await filesT) ?? []
commits = (try? await commitsT) ?? []
// CI runs depend on the head SHA from the PR.
if let sha = pr?.head?.sha, !sha.isEmpty {
await loadRuns(client: client, sha: sha)
}
}
@@ -73,7 +78,9 @@ final class PRDetailViewModel: ObservableObject {
jobs[run.id] = jobList
}
} catch {
// CI is best-effort; don't fail the whole detail view.
// CI is best-effort; don't fail the whole detail view, but log it so
// a token-scope 403 on the Actions endpoint is visible.
LogStore.shared.log(.warn, "PR \(prNumber): actions/runs failed: \(error.localizedDescription)")
}
}