Merge pull request #2485 from pikasTech/fix/2477-action-log-not-ready
Pipelines as Code CI / hwlab-web-probe-sentinel-nc01- Success
Pipelines as Code CI / platform-infra-gitea-nc01- Success
Pipelines as Code CI / unidesk-host- Success

fix: 正确分类尚未生成的 Actions 日志
This commit is contained in:
Lyon
2026-07-18 05:38:16 +08:00
committed by GitHub
3 changed files with 30 additions and 3 deletions
+6 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test";
import { redactAndTailActionLog } from "./gh/actions-runs";
import { isActionLogNotReady, redactAndTailActionLog } from "./gh/actions-runs";
import { withGhDefaultRendered } from "./gh/default-render";
describe("GitHub Actions run diagnostics", () => {
@@ -22,6 +22,11 @@ describe("GitHub Actions run diagnostics", () => {
expect(redactAndTailActionLog("a".repeat(100), 10, 12)).toBe("a".repeat(12));
});
test("classifies a pending job log without claiming the repository is missing", () => {
expect(isActionLogNotReady({ status: 404, message: "The specified blob does not exist." })).toBe(true);
expect(isActionLogNotReady({ status: 404, message: "Not Found" })).toBe(false);
});
test("renders startup failures and bounded logs without hiding evidence", () => {
const startup = withGhDefaultRendered(["run", "view", "42"], {
ok: true,
+23 -2
View File
@@ -1,4 +1,4 @@
import { commandError, githubRequest, githubTextRequest, isGitHubError, validationError } from "./client";
import { commandError, errorPayload, githubRequest, githubTextRequest, isGitHubError, validationError } from "./client";
interface WorkflowRun {
id: number;
@@ -77,6 +77,12 @@ export function redactAndTailActionLog(text: string, lineLimit = 80, charLimit =
return redacted.split(/\r?\n/u).slice(-lineLimit).join("\n").slice(-charLimit);
}
export function isActionLogNotReady(error: unknown): boolean {
if (typeof error !== "object" || error === null) return false;
const value = error as { status?: unknown; message?: unknown };
return value.status === 404 && typeof value.message === "string" && value.message.toLowerCase().includes("blob does not exist");
}
export async function actionRunList(repo: string, token: string, limit: number): Promise<Record<string, unknown>> {
const parts = splitRepo(repo);
if (parts === null) return validationError("run list", repo, "--repo must use owner/name format");
@@ -137,7 +143,22 @@ export async function actionRunLogs(repo: string, token: string, runId: number,
if (parts === null) return validationError("run logs", repo, "--repo must use owner/name format");
const [owner, name] = parts;
const log = await githubTextRequest(token, `/repos/${owner}/${name}/actions/jobs/${jobId}/logs`);
if (isGitHubError(log)) return commandError("run logs", repo, log, { runId, jobId, mutation: false });
if (isGitHubError(log)) {
if (isActionLogNotReady(log)) {
return commandError("run logs", repo, errorPayload("action-log-not-ready", "job log is not available yet", {
status: 404,
retryable: true,
details: { runId, jobId, jobStatus: job.status },
}), {
runId,
jobId,
mutation: false,
retryable: true,
next: `bun scripts/cli.ts gh run view ${runId} --repo ${repo}`,
});
}
return commandError("run logs", repo, log, { runId, jobId, mutation: false });
}
const logTail = redactAndTailActionLog(log);
return {
ok: true,
+1
View File
@@ -187,6 +187,7 @@ export type GitHubDegradedReason =
| "auth-failed"
| "egress-failed"
| "github-transient"
| "action-log-not-ready"
| "network-proxy-failed"
| "permission-denied"
| "repo-not-found"