From eb678dc8266350a2b21742e1cb6569e1f451f31d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Jul 2026 22:33:37 +0000
Subject: [PATCH 1/5] Initial plan
From ee05c3c935d5bd1694ab9f735b53f7180c6eb5c3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Jul 2026 22:45:00 +0000
Subject: [PATCH 2/5] fix(setup-js): use core.info in OTLP and outcome scripts
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
actions/setup/js/action_conclusion_otlp.cjs | 7 ++--
actions/setup/js/action_setup_otlp.cjs | 17 +++++-----
actions/setup/js/ai_credits_context.cjs | 11 ++++---
actions/setup/js/emit_outcome_spans.cjs | 9 ++---
actions/setup/js/evaluate_outcomes.cjs | 13 ++++----
.../setup/js/generate_safe_outputs_tools.cjs | 7 ++--
actions/setup/js/test-live-github-api.cjs | 33 ++++++++++---------
7 files changed, 50 insertions(+), 47 deletions(-)
diff --git a/actions/setup/js/action_conclusion_otlp.cjs b/actions/setup/js/action_conclusion_otlp.cjs
index 55e2e1cb2af..080581b3cab 100644
--- a/actions/setup/js/action_conclusion_otlp.cjs
+++ b/actions/setup/js/action_conclusion_otlp.cjs
@@ -1,5 +1,6 @@
// @ts-check
"use strict";
+require("./shim.cjs");
/**
* action_conclusion_otlp.cjs
@@ -77,15 +78,15 @@ async function run() {
const startMs = parseJobStartMs(process.env.GITHUB_AW_OTEL_JOB_START_MS);
if (endpoints) {
- console.log(`[otlp] sending conclusion span "${spanName}" to configured endpoints`);
+ core.info(`[otlp] sending conclusion span "${spanName}" to configured endpoints`);
} else {
- console.log("[otlp] GH_AW_OTLP_ENDPOINTS not set, skipping OTLP export (will attempt JSONL mirror)");
+ core.info("[otlp] GH_AW_OTLP_ENDPOINTS not set, skipping OTLP export (will attempt JSONL mirror)");
}
await sendOtlpSpan.sendJobConclusionSpan(spanName, { startMs });
if (endpoints) {
- console.log("[otlp] conclusion span export attempted");
+ core.info("[otlp] conclusion span export attempted");
}
}
diff --git a/actions/setup/js/action_setup_otlp.cjs b/actions/setup/js/action_setup_otlp.cjs
index bd4d764159b..374987b25ad 100644
--- a/actions/setup/js/action_setup_otlp.cjs
+++ b/actions/setup/js/action_setup_otlp.cjs
@@ -1,5 +1,6 @@
// @ts-check
"use strict";
+require("./shim.cjs");
/**
* action_setup_otlp.cjs
@@ -42,7 +43,7 @@ function writeEnvLine(filePath, key, value, logLabel, fileLabel) {
if (!filePath || !value) return;
try {
appendFileSync(filePath, `${key}=${value}\n`);
- console.log(`[otlp] ${logLabel} written to ${fileLabel}`);
+ core.info(`[otlp] ${logLabel} written to ${fileLabel}`);
} catch {
/* ignore */
}
@@ -112,13 +113,13 @@ async function run() {
// through GitHub Actions expression evaluation is unreliable.
const inputTraceId = getActionInput("TRACE_ID").toLowerCase();
if (inputTraceId) {
- console.log(`[otlp] INPUT_TRACE_ID=${inputTraceId} (will reuse activation trace)`);
+ core.info(`[otlp] INPUT_TRACE_ID=${inputTraceId} (will reuse activation trace)`);
} else {
- console.log("[otlp] INPUT_TRACE_ID not set, a new trace ID will be generated");
+ core.info("[otlp] INPUT_TRACE_ID not set, a new trace ID will be generated");
}
const inputParentSpanId = getActionInput("PARENT_SPAN_ID").toLowerCase();
if (inputParentSpanId) {
- console.log(`[otlp] INPUT_PARENT_SPAN_ID=${inputParentSpanId} (will parent setup span)`);
+ core.info(`[otlp] INPUT_PARENT_SPAN_ID=${inputParentSpanId} (will parent setup span)`);
}
// Normalize to the canonical underscore form so sendJobSetupSpan (which
@@ -148,9 +149,9 @@ async function run() {
}
if (!endpoints) {
- console.log("[otlp] GH_AW_OTLP_ENDPOINTS not set, skipping setup span");
+ core.info("[otlp] GH_AW_OTLP_ENDPOINTS not set, skipping setup span");
} else {
- console.log(`[otlp] sending setup span to configured endpoints`);
+ core.info(`[otlp] sending setup span to configured endpoints`);
}
const { traceId, spanId, parentSpanId } = await sendJobSetupSpan({
@@ -159,10 +160,10 @@ async function run() {
parentSpanId: inputParentSpanId || undefined,
});
- console.log(`[otlp] resolved trace-id=${traceId}`);
+ core.info(`[otlp] resolved trace-id=${traceId}`);
if (endpoints) {
- console.log(`[otlp] setup span sent (traceId=${traceId}, spanId=${spanId})`);
+ core.info(`[otlp] setup span sent (traceId=${traceId}, spanId=${spanId})`);
}
const githubOutput = process.env.GITHUB_OUTPUT;
diff --git a/actions/setup/js/ai_credits_context.cjs b/actions/setup/js/ai_credits_context.cjs
index 01ecef07888..bd5611f543e 100644
--- a/actions/setup/js/ai_credits_context.cjs
+++ b/actions/setup/js/ai_credits_context.cjs
@@ -1,4 +1,5 @@
// @ts-check
+require("./shim.cjs");
const fs = require("fs");
const path = require("path");
@@ -323,10 +324,10 @@ function parseAuditLogCombined(auditJsonlPathOverride) {
* @param {string} envVarName
*/
function logAICreditSource(label, auditValue, stdioValue, envValue, envVarName) {
- if (auditValue) console.log(`[ai-credits] ${label} source=audit_log value=${auditValue}`);
- else if (stdioValue) console.log(`[ai-credits] ${label} source=agent_stdio value=${stdioValue}`);
- else if (envValue) console.log(`[ai-credits] ${label} source=env(${envVarName}) value=${envValue}`);
- else console.log(`[ai-credits] ${label} source=none ${envVarName}=${process.env[envVarName] || "(unset)"}`);
+ if (auditValue) core.info(`[ai-credits] ${label} source=audit_log value=${auditValue}`);
+ else if (stdioValue) core.info(`[ai-credits] ${label} source=agent_stdio value=${stdioValue}`);
+ else if (envValue) core.info(`[ai-credits] ${label} source=env(${envVarName}) value=${envValue}`);
+ else core.info(`[ai-credits] ${label} source=none ${envVarName}=${process.env[envVarName] || "(unset)"}`);
}
/**
@@ -355,7 +356,7 @@ function resolveAICreditsFailureState({ logProvenance = true } = {}) {
: envRateLimitSignal
? "env_ignored_no_ai_credits"
: "none";
- console.log(`[ai-credits] rateLimitSignal source=${rawRateLimitSignalSource}`);
+ core.info(`[ai-credits] rateLimitSignal source=${rawRateLimitSignalSource}`);
}
const aiCredits = auditAICredits || stdioSignals.aiCredits || envAICredits || "";
diff --git a/actions/setup/js/emit_outcome_spans.cjs b/actions/setup/js/emit_outcome_spans.cjs
index 01d73655ca9..49d6fd0b74e 100644
--- a/actions/setup/js/emit_outcome_spans.cjs
+++ b/actions/setup/js/emit_outcome_spans.cjs
@@ -1,5 +1,6 @@
// @ts-check
///
+require("./shim.cjs");
/**
* emit_outcome_spans.cjs
@@ -76,7 +77,7 @@ async function main() {
const awInfo = readJSONIfExists(AW_INFO_PATH) || {};
if (evaluations.length === 0 && (!summary || summary.total_outcomes === 0)) {
- console.log("[outcome-otel] No outcome evaluations to export");
+ core.info("[outcome-otel] No outcome evaluations to export");
return;
}
@@ -84,7 +85,7 @@ async function main() {
const hasEndpoints = endpoints.length > 0;
if (!hasEndpoints) {
- console.log("[outcome-otel] No OTLP endpoints configured, writing JSONL mirror only");
+ core.info("[outcome-otel] No OTLP endpoints configured, writing JSONL mirror only");
}
// Read aw_info.json first: GH_AW_INFO_* env vars are only present during setup,
@@ -304,11 +305,11 @@ async function main() {
// Always write to local JSONL mirror
appendToOTLPJSONL(payload);
- console.log(`[outcome-otel] Emitting ${evaluations.length} outcome span(s) + 1 summary span`);
+ core.info(`[outcome-otel] Emitting ${evaluations.length} outcome span(s) + 1 summary span`);
if (hasEndpoints) {
await sendOTLPToAllEndpoints(endpoints, payload, { skipJSONL: true });
- console.log(`[outcome-otel] Exported to ${endpoints.length} endpoint(s)`);
+ core.info(`[outcome-otel] Exported to ${endpoints.length} endpoint(s)`);
}
}
diff --git a/actions/setup/js/evaluate_outcomes.cjs b/actions/setup/js/evaluate_outcomes.cjs
index 2211644e97c..50c60daaa91 100644
--- a/actions/setup/js/evaluate_outcomes.cjs
+++ b/actions/setup/js/evaluate_outcomes.cjs
@@ -1,4 +1,5 @@
// @ts-check
+require("./shim.cjs");
/**
* evaluate_outcomes.cjs
@@ -2057,7 +2058,7 @@ function main() {
const runsRaw = gh(["run", "list", "--repo", repo, "--limit", "200", "--json", "databaseId,conclusion,workflowName,event", "--jq", '[.[] | select(.conclusion == "success")] | .[0:150]']);
if (!runsRaw || runsRaw === "[]" || runsRaw === "null") {
- console.log("No recent successful runs found");
+ core.info("No recent successful runs found");
writeJSONAtomic(SUMMARY_PATH, { runs_checked: 0, total_outcomes: 0 });
process.exit(0);
}
@@ -2127,7 +2128,7 @@ function main() {
noop += runNoops;
- console.log(`Run ${runId} (${workflow}): ${runItems} item(s), ${runNoops} noop(s) [trigger: ${event}]`);
+ core.info(`Run ${runId} (${workflow}): ${runItems} item(s), ${runNoops} noop(s) [trigger: ${event}]`);
checked++;
total += runItems;
@@ -2297,10 +2298,10 @@ function main() {
const merged = [...new Set([...seenIds, ...evaluatedIds])].sort((a, b) => a - b).slice(-500);
writeJSONAtomic(SEEN_FILE, merged);
- console.log(`ā Checked ${checked} runs, ${total} outcomes`);
- console.log(` Accepted: ${accepted}, Rejected: ${rejected}, Ignored: ${ignored}, Pending: ${pending}, Noop: ${noop}`);
- console.log(` Acceptance rate: ${acceptanceRate.toFixed(4)}`);
- console.log(JSON.stringify(readJSON(SUMMARY_PATH, {}), null, 2));
+ core.info(`ā Checked ${checked} runs, ${total} outcomes`);
+ core.info(` Accepted: ${accepted}, Rejected: ${rejected}, Ignored: ${ignored}, Pending: ${pending}, Noop: ${noop}`);
+ core.info(` Acceptance rate: ${acceptanceRate.toFixed(4)}`);
+ core.info(JSON.stringify(readJSON(SUMMARY_PATH, {}), null, 2));
}
if (require.main === module) {
diff --git a/actions/setup/js/generate_safe_outputs_tools.cjs b/actions/setup/js/generate_safe_outputs_tools.cjs
index 7822e3062ea..8d91f5a3324 100644
--- a/actions/setup/js/generate_safe_outputs_tools.cjs
+++ b/actions/setup/js/generate_safe_outputs_tools.cjs
@@ -1,6 +1,7 @@
// @ts-check
///
"use strict";
+require("./shim.cjs");
// @safe-outputs-exempt SEC-004 ā schema generator; does not process user body content. The substring "body:" appears only in the comment referencing the "allow-body" config option.
/**
@@ -401,11 +402,7 @@ async function main() {
const debugEnabled = process.env.DEBUG === "*" || (process.env.DEBUG || "").includes("safe_outputs");
if (debugEnabled) {
const infoMsg = `Generated tools.json with ${allFilteredTools.length} tools (${filteredTools.length} static + ${dynamicTools.length} dynamic)`;
- if (typeof core !== "undefined") {
- core.info(infoMsg);
- } else {
- console.log(infoMsg);
- }
+ core.info(infoMsg);
}
}
diff --git a/actions/setup/js/test-live-github-api.cjs b/actions/setup/js/test-live-github-api.cjs
index 912d6861fca..b315b75ca62 100755
--- a/actions/setup/js/test-live-github-api.cjs
+++ b/actions/setup/js/test-live-github-api.cjs
@@ -1,5 +1,6 @@
#!/usr/bin/env node
// @ts-check
+require("./shim.cjs");
/**
* Standalone script to test frontmatter hash computation with live GitHub API
@@ -29,7 +30,7 @@ async function testLiveGitHubAPI() {
process.exit(1);
}
- console.log("š Testing frontmatter hash with live GitHub API\n");
+ core.info("š Testing frontmatter hash with live GitHub API\n");
// Configuration
const owner = "github";
@@ -37,38 +38,38 @@ async function testLiveGitHubAPI() {
const ref = "main";
const workflowPath = ".github/workflows/audit-workflows.md";
- console.log(`Repository: ${owner}/${repo}`);
- console.log(`Branch: ${ref}`);
- console.log(`Workflow: ${workflowPath}\n`);
+ core.info(`Repository: ${owner}/${repo}`);
+ core.info(`Branch: ${ref}`);
+ core.info(`Workflow: ${workflowPath}\n`);
try {
// Use dynamic import for ESM module compatibility
const { getOctokit } = await import("@actions/github");
// Create GitHub API client
- console.log("š” Connecting to GitHub API...");
+ core.info("š” Connecting to GitHub API...");
const octokit = getOctokit(token);
// Create file reader using real GitHub API
const fileReader = createGitHubFileReader(octokit, owner, repo, ref);
// Fetch and compute hash
- console.log(`š„ Fetching workflow from GitHub API...`);
+ core.info(`š„ Fetching workflow from GitHub API...`);
const hash = await computeFrontmatterHash(workflowPath, {
fileReader,
});
- console.log(`\nā
Success! Hash computed from live GitHub API data:`);
- console.log(` ${hash}`);
+ core.info(`\nā
Success! Hash computed from live GitHub API data:`);
+ core.info(` ${hash}`);
// Verify determinism
- console.log(`\nš Verifying determinism (fetching again)...`);
+ core.info(`\nš Verifying determinism (fetching again)...`);
const hash2 = await computeFrontmatterHash(workflowPath, {
fileReader,
});
if (hash === hash2) {
- console.log(`ā
Hashes match - computation is deterministic`);
+ core.info(`ā
Hashes match - computation is deterministic`);
} else {
console.error(`ā Error: Hashes don't match!`);
console.error(` First: ${hash}`);
@@ -77,12 +78,12 @@ async function testLiveGitHubAPI() {
}
// Summary
- console.log(`\nš Summary:`);
- console.log(` - Successfully fetched workflow from live GitHub API`);
- console.log(` - Processed workflow with imports (shared/mcp/tavily.md, etc.)`);
- console.log(` - Computed deterministic SHA-256 hash`);
- console.log(` - Verified hash consistency across multiple API calls`);
- console.log(`\n⨠All tests passed! The JavaScript implementation works correctly with GitHub API.`);
+ core.info(`\nš Summary:`);
+ core.info(` - Successfully fetched workflow from live GitHub API`);
+ core.info(` - Processed workflow with imports (shared/mcp/tavily.md, etc.)`);
+ core.info(` - Computed deterministic SHA-256 hash`);
+ core.info(` - Verified hash consistency across multiple API calls`);
+ core.info(`\n⨠All tests passed! The JavaScript implementation works correctly with GitHub API.`);
} catch (err) {
const error = err;
console.error(`\nā Error: ${getErrorMessage(error)}`);
From 0a9d033cdc0d6ce75bfe7dd240a8bdf5e9e31cba Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Jul 2026 23:25:04 +0000
Subject: [PATCH 3/5] =?UTF-8?q?chore:=20initial=20plan=20=E2=80=94=20updat?=
=?UTF-8?q?e=20test=20spies=20from=20console.log=20to=20core.info?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/skills/agentic-workflows/SKILL.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/skills/agentic-workflows/SKILL.md b/.github/skills/agentic-workflows/SKILL.md
index acec3f146cd..615a51e551e 100644
--- a/.github/skills/agentic-workflows/SKILL.md
+++ b/.github/skills/agentic-workflows/SKILL.md
@@ -15,6 +15,7 @@ Repository overlay (optional):
Read only the files you need:
Load these files from `github/gh-aw` (they are not available locally).
+- `.github/aw/action-container-substitutions.md`
- `.github/aw/agentic-chat.md`
- `.github/aw/agentic-workflows-mcp.md`
- `.github/aw/asciicharts.md`
From 14e097919459cd77dec9b3f8f9665b9fe1f04b13 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Jul 2026 23:32:34 +0000
Subject: [PATCH 4/5] fix(setup-js tests): update test spies from console.log
to core.info
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.../setup/js/action_conclusion_otlp.test.cjs | 22 ++++++++---
actions/setup/js/action_setup_otlp.test.cjs | 38 ++++++++++++-------
actions/setup/js/emit_outcome_spans.test.cjs | 19 ++++++++--
3 files changed, 55 insertions(+), 24 deletions(-)
diff --git a/actions/setup/js/action_conclusion_otlp.test.cjs b/actions/setup/js/action_conclusion_otlp.test.cjs
index 41851bfd307..980433a8f87 100644
--- a/actions/setup/js/action_conclusion_otlp.test.cjs
+++ b/actions/setup/js/action_conclusion_otlp.test.cjs
@@ -5,6 +5,17 @@ import { createRequire } from "module";
// Use CJS require so we share the same module cache as action_conclusion_otlp.cjs
const req = createRequire(import.meta.url);
+// Set up global.core mock before loading the module under test so shim.cjs
+// sees it already present and does not overwrite it with its stderr-based shim.
+const mockCore = {
+ info: vi.fn(),
+ warning: vi.fn(),
+ error: vi.fn(),
+ setFailed: vi.fn(),
+ setOutput: vi.fn(),
+};
+global.core = mockCore;
+
// Load the real send_otlp_span module and capture the original function
const sendOtlpModule = req("./send_otlp_span.cjs");
const originalSendJobConclusionSpan = sendOtlpModule.sendJobConclusionSpan;
@@ -24,7 +35,6 @@ describe("action_conclusion_otlp.cjs", () => {
beforeEach(() => {
vi.clearAllMocks();
- vi.spyOn(console, "log").mockImplementation(() => {});
mockSendJobConclusionSpan.mockResolvedValue(undefined);
// Patch the shared CJS exports object ā run() accesses this at call time
sendOtlpModule.sendJobConclusionSpan = mockSendJobConclusionSpan;
@@ -53,7 +63,7 @@ describe("action_conclusion_otlp.cjs", () => {
it("should log that OTLP export is skipped and JSONL mirror will be attempted", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith("[otlp] GH_AW_OTLP_ENDPOINTS not set, skipping OTLP export (will attempt JSONL mirror)");
+ expect(mockCore.info).toHaveBeenCalledWith("[otlp] GH_AW_OTLP_ENDPOINTS not set, skipping OTLP export (will attempt JSONL mirror)");
});
it("should still call sendJobConclusionSpan for JSONL mirror", async () => {
@@ -78,13 +88,13 @@ describe("action_conclusion_otlp.cjs", () => {
it("should log the conclusion span export as attempted", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith("[otlp] conclusion span export attempted");
+ expect(mockCore.info).toHaveBeenCalledWith("[otlp] conclusion span export attempted");
});
it("should log the endpoint URL in the sending message", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith(expect.stringContaining("configured endpoints"));
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("configured endpoints"));
});
describe("span name construction", () => {
@@ -116,7 +126,7 @@ describe("action_conclusion_otlp.cjs", () => {
await run();
- expect(console.log).toHaveBeenCalledWith('[otlp] sending conclusion span "gh-aw.setup.conclusion" to configured endpoints');
+ expect(mockCore.info).toHaveBeenCalledWith('[otlp] sending conclusion span "gh-aw.setup.conclusion" to configured endpoints');
});
it("should handle different job names correctly", async () => {
@@ -198,7 +208,7 @@ describe("action_conclusion_otlp.cjs", () => {
mockSendJobConclusionSpan.mockRejectedValueOnce(new Error("fail"));
await expect(run()).rejects.toThrow("fail");
- expect(console.log).not.toHaveBeenCalledWith("[otlp] conclusion span export attempted");
+ expect(mockCore.info).not.toHaveBeenCalledWith("[otlp] conclusion span export attempted");
});
});
});
diff --git a/actions/setup/js/action_setup_otlp.test.cjs b/actions/setup/js/action_setup_otlp.test.cjs
index aaaa50a4ffd..55c995d36bc 100644
--- a/actions/setup/js/action_setup_otlp.test.cjs
+++ b/actions/setup/js/action_setup_otlp.test.cjs
@@ -7,6 +7,17 @@ import { writeFileSync, readFileSync, mkdtempSync, rmSync } from "fs";
const req = createRequire(import.meta.url);
+// Set up global.core mock before loading the module under test so shim.cjs
+// sees it already present and does not overwrite it with its stderr-based shim.
+const mockCore = {
+ info: vi.fn(),
+ warning: vi.fn(),
+ error: vi.fn(),
+ setFailed: vi.fn(),
+ setOutput: vi.fn(),
+};
+global.core = mockCore;
+
// Load send_otlp_span.cjs first so we can patch its exports before run() calls require()
// inside action_setup_otlp.cjs. Both share the same CJS module cache, so patching the
// exports object here is reflected when run() destructures from require(...send_otlp_span.cjs).
@@ -39,7 +50,6 @@ describe("action_setup_otlp.cjs", () => {
beforeEach(() => {
vi.clearAllMocks();
- vi.spyOn(console, "log").mockImplementation(() => {});
// Patch the shared CJS module exports ā run() re-destructures on every call
sendOtlpModule.sendJobSetupSpan = mockSendJobSetupSpan;
@@ -105,7 +115,7 @@ describe("action_setup_otlp.cjs", () => {
it("should log that OTLP export is being skipped", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith("[otlp] GH_AW_OTLP_ENDPOINTS not set, skipping setup span");
+ expect(mockCore.info).toHaveBeenCalledWith("[otlp] GH_AW_OTLP_ENDPOINTS not set, skipping setup span");
});
it("should still call sendJobSetupSpan for JSONL mirror", async () => {
@@ -117,7 +127,7 @@ describe("action_setup_otlp.cjs", () => {
it("should not log 'sending setup span to' when endpoint is absent", async () => {
await run();
- const logged = vi.mocked(console.log).mock.calls.flat();
+ const logged = vi.mocked(mockCore.info).mock.calls.flat();
expect(logged.some(msg => typeof msg === "string" && msg.includes("sending setup span to"))).toBe(false);
});
});
@@ -130,7 +140,7 @@ describe("action_setup_otlp.cjs", () => {
it("should log sending the setup span to the configured endpoint", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith("[otlp] sending setup span to configured endpoints");
+ expect(mockCore.info).toHaveBeenCalledWith("[otlp] sending setup span to configured endpoints");
});
it("should call sendJobSetupSpan exactly once", async () => {
@@ -142,14 +152,14 @@ describe("action_setup_otlp.cjs", () => {
it("should log setup span sent with traceId and spanId", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith(expect.stringContaining(`traceId=${VALID_TRACE_ID}`));
- expect(console.log).toHaveBeenCalledWith(expect.stringContaining(`spanId=${VALID_SPAN_ID}`));
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining(`traceId=${VALID_TRACE_ID}`));
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining(`spanId=${VALID_SPAN_ID}`));
});
it("should log the resolved trace ID", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith(expect.stringContaining(`trace-id=${VALID_TRACE_ID}`));
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining(`trace-id=${VALID_TRACE_ID}`));
});
});
@@ -271,13 +281,13 @@ describe("action_setup_otlp.cjs", () => {
await run();
- expect(console.log).toHaveBeenCalledWith(expect.stringContaining("INPUT_TRACE_ID=abcdef0123456789abcdef0123456789"));
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("INPUT_TRACE_ID=abcdef0123456789abcdef0123456789"));
});
it("should log that INPUT_TRACE_ID is not set when absent", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith("[otlp] INPUT_TRACE_ID not set, a new trace ID will be generated");
+ expect(mockCore.info).toHaveBeenCalledWith("[otlp] INPUT_TRACE_ID not set, a new trace ID will be generated");
});
it("should accept the hyphen form INPUT_TRACE-ID as a fallback", async () => {
@@ -326,7 +336,7 @@ describe("action_setup_otlp.cjs", () => {
await run();
- expect(console.log).toHaveBeenCalledWith("[otlp] INPUT_PARENT_SPAN_ID=aabbccddeeff0011 (will parent setup span)");
+ expect(mockCore.info).toHaveBeenCalledWith("[otlp] INPUT_PARENT_SPAN_ID=aabbccddeeff0011 (will parent setup span)");
});
it("should accept the hyphen form INPUT_PARENT-SPAN-ID as a fallback", async () => {
@@ -377,7 +387,7 @@ describe("action_setup_otlp.cjs", () => {
it("should log that trace-id was written to GITHUB_OUTPUT", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith(`[otlp] trace-id=${VALID_TRACE_ID} written to GITHUB_OUTPUT`);
+ expect(mockCore.info).toHaveBeenCalledWith(`[otlp] trace-id=${VALID_TRACE_ID} written to GITHUB_OUTPUT`);
});
it("should write parent-span-id to GITHUB_OUTPUT when parent span ID is valid", async () => {
@@ -466,19 +476,19 @@ describe("action_setup_otlp.cjs", () => {
it("should log the GITHUB_AW_OTEL_TRACE_ID write", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith("[otlp] GITHUB_AW_OTEL_TRACE_ID written to GITHUB_ENV");
+ expect(mockCore.info).toHaveBeenCalledWith("[otlp] GITHUB_AW_OTEL_TRACE_ID written to GITHUB_ENV");
});
it("should log the GITHUB_AW_OTEL_PARENT_SPAN_ID write", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith("[otlp] GITHUB_AW_OTEL_PARENT_SPAN_ID written to GITHUB_ENV");
+ expect(mockCore.info).toHaveBeenCalledWith("[otlp] GITHUB_AW_OTEL_PARENT_SPAN_ID written to GITHUB_ENV");
});
it("should log the GITHUB_AW_OTEL_JOB_START_MS write", async () => {
await run();
- expect(console.log).toHaveBeenCalledWith("[otlp] GITHUB_AW_OTEL_JOB_START_MS written to GITHUB_ENV");
+ expect(mockCore.info).toHaveBeenCalledWith("[otlp] GITHUB_AW_OTEL_JOB_START_MS written to GITHUB_ENV");
});
});
diff --git a/actions/setup/js/emit_outcome_spans.test.cjs b/actions/setup/js/emit_outcome_spans.test.cjs
index cbc8957d4b4..29f0fb0ac0c 100644
--- a/actions/setup/js/emit_outcome_spans.test.cjs
+++ b/actions/setup/js/emit_outcome_spans.test.cjs
@@ -3,6 +3,18 @@ import fs from "fs";
import { createRequire } from "module";
const req = createRequire(import.meta.url);
+
+// Set up global.core mock before loading any module that requires shim.cjs so
+// the shim sees it already present and does not overwrite it.
+const mockCore = {
+ info: vi.fn(),
+ warning: vi.fn(),
+ error: vi.fn(),
+ setFailed: vi.fn(),
+ setOutput: vi.fn(),
+};
+global.core = mockCore;
+
const sendOtlpModule = req("./send_otlp_span.cjs");
const mockGenerateTraceId = vi.fn();
@@ -48,7 +60,6 @@ describe("emit_outcome_spans.cjs", () => {
beforeEach(() => {
vi.clearAllMocks();
- vi.spyOn(console, "log").mockImplementation(() => {});
vi.spyOn(console, "warn").mockImplementation(() => {});
fs.mkdirSync("/tmp/gh-aw", { recursive: true });
@@ -168,7 +179,7 @@ describe("emit_outcome_spans.cjs", () => {
it("no-ops when there are no evaluations and no summary data", async () => {
await moduleUnderTest.main();
- expect(console.log).toHaveBeenCalledWith("[outcome-otel] No outcome evaluations to export");
+ expect(mockCore.info).toHaveBeenCalledWith("[outcome-otel] No outcome evaluations to export");
expect(mockBuildGitHubActionsResourceAttributes).not.toHaveBeenCalled();
expect(mockBuildOTLPBatchPayload).not.toHaveBeenCalled();
expect(mockAppendToOTLPJSONL).not.toHaveBeenCalled();
@@ -323,7 +334,7 @@ describe("emit_outcome_spans.cjs", () => {
expect(mockAppendToOTLPJSONL).toHaveBeenCalledOnce();
expect(mockSendOTLPToAllEndpoints).not.toHaveBeenCalled();
- expect(console.log).toHaveBeenCalledWith("[outcome-otel] No OTLP endpoints configured, writing JSONL mirror only");
+ expect(mockCore.info).toHaveBeenCalledWith("[outcome-otel] No OTLP endpoints configured, writing JSONL mirror only");
});
it("falls back to GH_AW_INFO_* env vars and exports to configured endpoints", async () => {
@@ -356,6 +367,6 @@ describe("emit_outcome_spans.cjs", () => {
);
expect(mockAppendToOTLPJSONL).toHaveBeenCalledOnce();
expect(mockSendOTLPToAllEndpoints).toHaveBeenCalledWith([{ url: "https://otel.example.com" }], expect.any(Object), { skipJSONL: true });
- expect(console.log).toHaveBeenCalledWith("[outcome-otel] Exported to 1 endpoint(s)");
+ expect(mockCore.info).toHaveBeenCalledWith("[outcome-otel] Exported to 1 endpoint(s)");
});
});
From 71698f311a22a8772569ea817ef274413afe7255 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Jul 2026 23:56:04 +0000
Subject: [PATCH 5/5] fix(setup-js): complete console.error migration in
test-live-github-api.cjs
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
---
actions/setup/js/test-live-github-api.cjs | 29 ++++++++++++-----------
1 file changed, 15 insertions(+), 14 deletions(-)
diff --git a/actions/setup/js/test-live-github-api.cjs b/actions/setup/js/test-live-github-api.cjs
index b315b75ca62..853acf34714 100755
--- a/actions/setup/js/test-live-github-api.cjs
+++ b/actions/setup/js/test-live-github-api.cjs
@@ -20,13 +20,15 @@ async function testLiveGitHubAPI() {
// Check for GitHub token
const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
if (!token) {
- console.error("ā Error: No GitHub token found");
- console.error("Please set GITHUB_TOKEN or GH_TOKEN environment variable");
- console.error("\nExample:");
- console.error(" GITHUB_TOKEN=ghp_xxx node test-live-github-api.cjs");
- console.error("\nTo create a token:");
- console.error(" 1. Go to https://github.com/settings/tokens");
- console.error(" 2. Create a token with 'repo' or 'public_repo' scope");
+ core.setFailed(
+ "ā Error: No GitHub token found\n" +
+ "Please set GITHUB_TOKEN or GH_TOKEN environment variable\n" +
+ "\nExample:\n" +
+ " GITHUB_TOKEN=ghp_xxx node test-live-github-api.cjs\n" +
+ "\nTo create a token:\n" +
+ " 1. Go to https://github.com/settings/tokens\n" +
+ " 2. Create a token with 'repo' or 'public_repo' scope"
+ );
process.exit(1);
}
@@ -71,9 +73,7 @@ async function testLiveGitHubAPI() {
if (hash === hash2) {
core.info(`ā
Hashes match - computation is deterministic`);
} else {
- console.error(`ā Error: Hashes don't match!`);
- console.error(` First: ${hash}`);
- console.error(` Second: ${hash2}`);
+ core.setFailed(`ā Error: Hashes don't match!\n First: ${hash}\n Second: ${hash2}`);
process.exit(1);
}
@@ -86,17 +86,18 @@ async function testLiveGitHubAPI() {
core.info(`\n⨠All tests passed! The JavaScript implementation works correctly with GitHub API.`);
} catch (err) {
const error = err;
- console.error(`\nā Error: ${getErrorMessage(error)}`);
+ let msg = `\nā Error: ${getErrorMessage(error)}`;
if (error && typeof error === "object" && "status" in error) {
const statusError = error;
if (statusError.status === 401) {
- console.error(" Authentication failed - check your GitHub token");
+ msg += "\n Authentication failed - check your GitHub token";
} else if (statusError.status === 404) {
- console.error(" File not found - check repository and file path");
+ msg += "\n File not found - check repository and file path";
} else if (statusError.status === 403) {
- console.error(" Rate limit exceeded or insufficient permissions");
+ msg += "\n Rate limit exceeded or insufficient permissions";
}
}
+ core.setFailed(msg);
process.exit(1);
}
}