diff --git a/actions/setup/js/parse_mcp_gateway_log.cjs b/actions/setup/js/parse_mcp_gateway_log.cjs index aae854a8961..a4b65537c4f 100644 --- a/actions/setup/js/parse_mcp_gateway_log.cjs +++ b/actions/setup/js/parse_mcp_gateway_log.cjs @@ -167,9 +167,8 @@ function generateTokenUsageSummary(summary) { } /** - * Appends the token usage section to the step summary if data is present, then writes it. - * Also exports GH_AW_EFFECTIVE_TOKENS as a GitHub Actions environment variable so - * subsequent steps can display the ET value in generated footers. + * Writes the step summary and exports GH_AW_EFFECTIVE_TOKENS when token usage data exists. + * Token Usage rendering is handled by parse_token_usage.cjs to avoid duplicate sections. * This is the final call in each main() exit path — it consolidates the summary write * so callers don't need to chain addRaw() + write() themselves. * @param {typeof import('@actions/core')} coreObj - The GitHub Actions core object @@ -182,10 +181,6 @@ function writeStepSummaryWithTokenUsage(coreObj) { if (content?.trim()) { coreObj.info(`Found token-usage.jsonl (${content.length} bytes)`); const parsedSummary = parseTokenUsageJsonl(content); - const markdown = generateTokenUsageSummary(parsedSummary); - if (markdown.length > 0) { - coreObj.summary.addDetails("Token Usage", "\n\n" + markdown); - } // Export total effective tokens as a GitHub Actions env var for use in // generated footers (GH_AW_EFFECTIVE_TOKENS is read by messages_footer.cjs) if (parsedSummary && parsedSummary.totalEffectiveTokens > 0) { diff --git a/actions/setup/js/parse_mcp_gateway_log.test.cjs b/actions/setup/js/parse_mcp_gateway_log.test.cjs index b75a171a5b9..8b78b176f87 100644 --- a/actions/setup/js/parse_mcp_gateway_log.test.cjs +++ b/actions/setup/js/parse_mcp_gateway_log.test.cjs @@ -335,6 +335,78 @@ Some content here.`; fs.rmSync(tmpDir, { recursive: true, force: true }); } }); + + test("does not append token usage details when token usage file exists", async () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "mcp-test-")); + const gatewayMdPath = path.join(tmpDir, "gateway.md"); + const tokenUsagePath = path.join(tmpDir, "token-usage.jsonl"); + const originalExistsSync = fs.existsSync; + const originalReadFileSync = fs.readFileSync; + + try { + fs.writeFileSync(gatewayMdPath, "# Gateway Summary\n\nSome markdown content"); + fs.writeFileSync( + tokenUsagePath, + JSON.stringify({ + model: "claude-haiku-4-5-20251001", + input_tokens: 42, + output_tokens: 2765, + cache_read_tokens: 141738, + cache_write_tokens: 38170, + duration_ms: 26500, + }) + ); + + const mockCore = { + info: vi.fn(), + debug: vi.fn(), + startGroup: vi.fn(), + endGroup: vi.fn(), + notice: vi.fn(), + warning: vi.fn(), + error: vi.fn(), + setFailed: vi.fn(), + exportVariable: vi.fn(), + setOutput: vi.fn(), + summary: { + addRaw: vi.fn().mockReturnThis(), + addDetails: vi.fn().mockReturnThis(), + write: vi.fn(), + }, + }; + + fs.existsSync = vi.fn(filepath => { + if (filepath === "/tmp/gh-aw/mcp-logs/gateway.md") return true; + if (filepath === "/tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl") return true; + return originalExistsSync(filepath); + }); + + fs.readFileSync = vi.fn((filepath, encoding) => { + if (filepath === "/tmp/gh-aw/mcp-logs/gateway.md") { + return originalReadFileSync(gatewayMdPath, encoding); + } + if (filepath === "/tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl") { + return originalReadFileSync(tokenUsagePath, encoding); + } + return originalReadFileSync(filepath, encoding); + }); + + global.core = mockCore; + + const { main } = require("./parse_mcp_gateway_log.cjs"); + await main(); + + expect(mockCore.summary.addRaw).toHaveBeenCalledWith(expect.stringContaining("Gateway Summary")); + expect(mockCore.summary.addDetails).not.toHaveBeenCalledWith("Token Usage", expect.any(String)); + expect(mockCore.exportVariable).toHaveBeenCalledWith("GH_AW_EFFECTIVE_TOKENS", expect.any(String)); + expect(mockCore.summary.write).toHaveBeenCalled(); + } finally { + fs.existsSync = originalExistsSync; + fs.readFileSync = originalReadFileSync; + delete global.core; + fs.rmSync(tmpDir, { recursive: true, force: true }); + } + }); }); describe("printAllGatewayFiles", () => {