Skip to content

Commit f42052b

Browse files
authored
Prevent false CLI Version Checker failures when safe outputs were already produced (#26570)
1 parent fdcdc35 commit f42052b

2 files changed

Lines changed: 63 additions & 11 deletions

File tree

actions/setup/js/log_parser_bootstrap.cjs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,33 @@ async function runLogParser(options) {
4545
return null;
4646
}
4747

48+
/**
49+
* Count valid JSONL entries from a safe outputs file.
50+
* @param {string} content - Raw safe outputs JSONL content
51+
* @returns {number} Number of valid entries
52+
*/
53+
function countSafeOutputEntries(content) {
54+
if (!content || content.trim().length === 0) {
55+
return 0;
56+
}
57+
58+
let count = 0;
59+
const lines = content.trim().split(/\r?\n/);
60+
for (const line of lines) {
61+
const trimmedLine = line.trim();
62+
if (!trimmedLine) {
63+
continue;
64+
}
65+
try {
66+
JSON.parse(trimmedLine);
67+
count++;
68+
} catch (e) {
69+
// Ignore invalid JSONL lines
70+
}
71+
}
72+
return count;
73+
}
74+
4875
try {
4976
const logPath = process.env.GH_AW_AGENT_OUTPUT;
5077
if (!logPath) {
@@ -120,18 +147,20 @@ async function runLogParser(options) {
120147
logEntries = result.logEntries || null;
121148
}
122149

123-
if (markdown) {
124-
// Read safe outputs file if available
125-
let safeOutputsContent = "";
126-
const safeOutputsPath = process.env.GH_AW_SAFE_OUTPUTS;
127-
if (safeOutputsPath && fs.existsSync(safeOutputsPath)) {
128-
try {
129-
safeOutputsContent = fs.readFileSync(safeOutputsPath, "utf8");
130-
} catch (error) {
131-
core.warning(`Failed to read safe outputs file: ${getErrorMessage(error)}`);
132-
}
150+
// Read safe outputs file if available
151+
let safeOutputsContent = "";
152+
let safeOutputEntriesCount = 0;
153+
const safeOutputsPath = process.env.GH_AW_SAFE_OUTPUTS;
154+
if (safeOutputsPath && fs.existsSync(safeOutputsPath)) {
155+
try {
156+
safeOutputsContent = fs.readFileSync(safeOutputsPath, "utf8");
157+
safeOutputEntriesCount = countSafeOutputEntries(safeOutputsContent);
158+
} catch (error) {
159+
core.warning(`Failed to read safe outputs file: ${getErrorMessage(error)}`);
133160
}
161+
}
134162

163+
if (markdown) {
135164
// Generate lightweight plain text summary for core.info and Copilot CLI style for step summary
136165
if (logEntries && Array.isArray(logEntries) && logEntries.length > 0) {
137166
// Extract model from init entry if available
@@ -215,7 +244,11 @@ async function runLogParser(options) {
215244
// Handle MCP server failures if present
216245
if (mcpFailures && mcpFailures.length > 0) {
217246
const failedServers = mcpFailures.join(", ");
218-
core.setFailed(`${ERR_API}: MCP server(s) failed to launch: ${failedServers}`);
247+
if (safeOutputEntriesCount > 0) {
248+
core.warning(`MCP server(s) failed to launch (${failedServers}), but agent completed with ${safeOutputEntriesCount} safe output ${safeOutputEntriesCount === 1 ? "entry" : "entries"}`);
249+
} else {
250+
core.setFailed(`${ERR_API}: MCP server(s) failed to launch: ${failedServers}`);
251+
}
219252
}
220253

221254
// Handle max-turns limit if hit

actions/setup/js/log_parser_bootstrap.test.cjs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,25 @@ describe("log_parser_bootstrap.cjs", () => {
112112
const mockParseLog = vi.fn().mockReturnValue({ markdown: "## Result\n", mcpFailures: ["server1", "server2"], maxTurnsHit: !1 });
113113
(runLogParser({ parseLog: mockParseLog, parserName: "TestParser" }), expect(mockCore.setFailed).toHaveBeenCalledWith(`${ERR_API}: MCP server(s) failed to launch: server1, server2`), fs.unlinkSync(logFile), fs.rmdirSync(tmpDir));
114114
}),
115+
it("should warn instead of failing MCP failures when safe outputs exist", () => {
116+
const tmpDir = fs.mkdtempSync(path.join(__dirname, "test-"));
117+
const logFile = path.join(tmpDir, "test.log");
118+
const safeOutputsFile = path.join(tmpDir, "safe-outputs.jsonl");
119+
120+
fs.writeFileSync(logFile, "content");
121+
fs.writeFileSync(safeOutputsFile, ` ${JSON.stringify({ type: "create_issue", title: "Test", body: "Test body" })}\r\n`);
122+
process.env.GH_AW_AGENT_OUTPUT = logFile;
123+
process.env.GH_AW_SAFE_OUTPUTS = safeOutputsFile;
124+
125+
const mockParseLog = vi.fn().mockReturnValue({ markdown: "## Result\n", mcpFailures: ["server1"], maxTurnsHit: !1 });
126+
127+
(runLogParser({ parseLog: mockParseLog, parserName: "TestParser" }),
128+
expect(mockCore.warning).toHaveBeenCalledWith("MCP server(s) failed to launch (server1), but agent completed with 1 safe output entry"),
129+
expect(mockCore.setFailed).not.toHaveBeenCalled(),
130+
fs.unlinkSync(logFile),
131+
fs.unlinkSync(safeOutputsFile),
132+
fs.rmdirSync(tmpDir));
133+
}),
115134
it("should handle max-turns limit reached", () => {
116135
const tmpDir = fs.mkdtempSync(path.join(__dirname, "test-")),
117136
logFile = path.join(tmpDir, "test.log");

0 commit comments

Comments
 (0)