Fix commits tab + add CI job logs; bump to 1.7
Build iOS App / Build .ipa and publish source (push) Successful in 21s
Build iOS App / Build .ipa and publish source (push) Successful in 21s
Commits tab was empty: Gitea PR commits have no 'id' field (only sha), but
PRCommit.id was a non-optional let decoded from 'id', so decoding [PRCommit]
always threw and the tab silently showed nothing. Make PRCommit.id a computed
sha-based identity so the array decodes and renders.
CI: add a Logs button per job that fetches the job's raw log text
(/actions/jobs/{id}/logs) into a viewable, shareable screen. Bump to 1.7.
This commit is contained in:
1 parent
a3b8eefdb2
commit
9f3fb95cbe
5 files changed
+95
-9
No files matched your search
@@ -187,7 +187,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 7;
|
CURRENT_PROJECT_VERSION = 8;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
||||||
@@ -197,7 +197,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.6;
|
MARKETING_VERSION = 1.7;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 7;
|
CURRENT_PROJECT_VERSION = 8;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
||||||
@@ -221,7 +221,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.6;
|
MARKETING_VERSION = 1.7;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// Displays a Gitea Actions job's raw log text, with a share button.
|
||||||
|
struct JobLogsView: View {
|
||||||
|
let repo: Repository
|
||||||
|
let runID: Int64
|
||||||
|
let jobID: Int64
|
||||||
|
let jobName: String
|
||||||
|
|
||||||
|
@State private var text = ""
|
||||||
|
@State private var isLoading = true
|
||||||
|
@State private var errorMessage: String?
|
||||||
|
@State private var showShare = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Group {
|
||||||
|
if isLoading {
|
||||||
|
ProgressView("Loading logs…")
|
||||||
|
} else if let errorMessage {
|
||||||
|
ContentUnavailableView(
|
||||||
|
"Couldn't load logs",
|
||||||
|
systemImage: "doc.text",
|
||||||
|
description: Text(errorMessage))
|
||||||
|
} else {
|
||||||
|
ScrollView {
|
||||||
|
Text(text)
|
||||||
|
.font(.caption.monospaced())
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.textSelection(.enabled)
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle(jobName)
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .primaryAction) {
|
||||||
|
Button {
|
||||||
|
showShare = true
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "square.and.arrow.up")
|
||||||
|
}
|
||||||
|
.disabled(text.isEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $showShare) {
|
||||||
|
ShareSheet(text: text)
|
||||||
|
}
|
||||||
|
.task { await load() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private func load() async {
|
||||||
|
isLoading = true
|
||||||
|
errorMessage = nil
|
||||||
|
defer { isLoading = false }
|
||||||
|
guard let client = SettingsStore.shared.client() else {
|
||||||
|
errorMessage = "Not configured."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
text = try await client.jobLogs(owner: repo.owner.login, repo: repo.name, jobID: jobID)
|
||||||
|
} catch {
|
||||||
|
errorMessage = error.localizedDescription
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -218,6 +218,14 @@ struct ChecksTab: View {
|
|||||||
.font(.caption)
|
.font(.caption)
|
||||||
.buttonStyle(.bordered)
|
.buttonStyle(.bordered)
|
||||||
}
|
}
|
||||||
|
NavigationLink {
|
||||||
|
JobLogsView(repo: vm.repo, runID: run.id, jobID: job.id,
|
||||||
|
jobName: job.name)
|
||||||
|
} label: {
|
||||||
|
Text("Logs")
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
.buttonStyle(.bordered)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,22 +82,24 @@ struct IssueComment: Codable, Identifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A commit in a pull request.
|
/// A commit in a pull request.
|
||||||
struct PRCommit: Codable, Identifiable {
|
struct PRCommit: Codable, Hashable, Identifiable {
|
||||||
struct Meta: Codable {
|
struct Meta: Codable, Hashable {
|
||||||
let sha: String?
|
let sha: String?
|
||||||
let message: String?
|
let message: String?
|
||||||
}
|
}
|
||||||
struct Author: Codable {
|
struct Author: Codable, Hashable {
|
||||||
let name: String?
|
let name: String?
|
||||||
let email: String?
|
let email: String?
|
||||||
let login: String?
|
let login: String?
|
||||||
}
|
}
|
||||||
let id: String // sha
|
|
||||||
let sha: String?
|
let sha: String?
|
||||||
let commit: Meta?
|
let commit: Meta?
|
||||||
let author: Author?
|
let author: Author?
|
||||||
|
/// Gitea's PR commits have no `id` field — only `sha`. Use sha as the
|
||||||
|
/// identity so the array decodes and SwiftUI can key rows on it.
|
||||||
|
var id: String { sha ?? commit?.sha ?? "\(author?.name ?? "")-\(commit?.message?.hashValue ?? 0)" }
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case id, sha, commit, author
|
case sha, commit, author
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,4 +250,14 @@ struct GiteaClient {
|
|||||||
func rerun(owner: String, repo: String, runID: Int64) async throws {
|
func rerun(owner: String, repo: String, runID: Int64) async throws {
|
||||||
try await postNoBody("repos/\(owner)/\(repo)/actions/runs/\(runID)/rerun")
|
try await postNoBody("repos/\(owner)/\(repo)/actions/runs/\(runID)/rerun")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Raw log text for a single job (from `/actions/jobs/{job_id}/logs`).
|
||||||
|
/// The response is plain text, not JSON.
|
||||||
|
func jobLogs(owner: String, repo: String, jobID: Int64) async throws -> String {
|
||||||
|
let data = try await request("repos/\(owner)/\(repo)/actions/jobs/\(jobID)/logs")
|
||||||
|
guard let text = String(data: data, encoding: .utf8) else {
|
||||||
|
throw APIError.invalidResponse
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user