From 5852e230d925bfdbd829dea1c26a71aca3f2b007 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Apr 2026 04:09:13 +0000 Subject: [PATCH] fix: normalize INPUT_JOB_NAME hyphen form in OTLP span scripts Some GitHub Actions runner versions preserve the original hyphen in input env var names (INPUT_JOB-NAME) instead of converting to underscore (INPUT_JOB_NAME). This caused OTLP spans to fall back to the generic 'gh-aw.job.setup' / 'gh-aw.job.conclusion' names instead of using the actual job name. Apply the same two-key normalization already used for INPUT_TRACE_ID: - index.js: normalize INPUT_JOB_NAME at startup, pass to setup.sh env and set in process.env before calling action_setup_otlp.cjs - action_setup_otlp.cjs: normalize INPUT_JOB_NAME and canonicalize to underscore form so sendJobSetupSpan always finds the value - action_conclusion_otlp.cjs: normalize INPUT_JOB_NAME inline - setup.sh: explicitly pass INPUT_JOB_NAME to action_setup_otlp.cjs in script mode (alongside INPUT_TRACE_ID) Tests added for the hyphen form in action_conclusion_otlp.test.cjs and action_otlp.test.cjs. Agent-Logs-Url: https://github.com/github/gh-aw/sessions/099777bf-2d2a-4e33-b4a6-60eff2ca3d20 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/index.js | 9 +++++++ actions/setup/js/action_conclusion_otlp.cjs | 5 +++- .../setup/js/action_conclusion_otlp.test.cjs | 16 ++++++++++++ actions/setup/js/action_otlp.test.cjs | 26 +++++++++++++++++++ actions/setup/js/action_setup_otlp.cjs | 12 +++++++++ actions/setup/setup.sh | 2 +- 6 files changed, 68 insertions(+), 2 deletions(-) diff --git a/actions/setup/index.js b/actions/setup/index.js index d7e96af5aef..be5d12d0219 100644 --- a/actions/setup/index.js +++ b/actions/setup/index.js @@ -23,11 +23,19 @@ const inputTraceId = process.env["INPUT_TRACE-ID"] || ""; +// Normalize job-name input: handle both INPUT_JOB_NAME (underscore, standard) +// and INPUT_JOB-NAME (hyphen, used by some runner versions). +const inputJobName = + process.env["INPUT_JOB_NAME"] || + process.env["INPUT_JOB-NAME"] || + ""; + const result = spawnSync(path.join(__dirname, "setup.sh"), [], { stdio: "inherit", env: Object.assign({}, process.env, { INPUT_SAFE_OUTPUT_CUSTOM_TOKENS: safeOutputCustomTokens, INPUT_TRACE_ID: inputTraceId, + INPUT_JOB_NAME: inputJobName, // Tell setup.sh to skip the OTLP span: in action mode index.js sends it // after setup.sh returns so that the startMs captured here is used. GH_AW_SKIP_SETUP_OTLP: "1", @@ -54,6 +62,7 @@ if (result.status !== 0) { try { process.env.SETUP_START_MS = String(setupStartMs); process.env.INPUT_TRACE_ID = inputTraceId; + process.env.INPUT_JOB_NAME = inputJobName; const { run } = require(path.join(__dirname, "js", "action_setup_otlp.cjs")); await run(); } catch { diff --git a/actions/setup/js/action_conclusion_otlp.cjs b/actions/setup/js/action_conclusion_otlp.cjs index 5d273f8c725..3fa4b0c3c98 100644 --- a/actions/setup/js/action_conclusion_otlp.cjs +++ b/actions/setup/js/action_conclusion_otlp.cjs @@ -48,7 +48,10 @@ async function run() { const rawJobStartMs = parseInt(process.env.GITHUB_AW_OTEL_JOB_START_MS || "0", 10); const startMs = rawJobStartMs > 0 ? rawJobStartMs : undefined; - const spanName = process.env.INPUT_JOB_NAME ? `gh-aw.${process.env.INPUT_JOB_NAME}.conclusion` : "gh-aw.job.conclusion"; + // Normalize job-name input: handle both INPUT_JOB_NAME (underscore, standard) + // and INPUT_JOB-NAME (hyphen, used by some runner versions). + const jobName = (process.env.INPUT_JOB_NAME || process.env["INPUT_JOB-NAME"] || "").trim(); + const spanName = jobName ? `gh-aw.${jobName}.conclusion` : "gh-aw.job.conclusion"; console.log(`[otlp] sending conclusion span "${spanName}" to ${endpoint}`); await sendOtlpSpan.sendJobConclusionSpan(spanName, { startMs }); diff --git a/actions/setup/js/action_conclusion_otlp.test.cjs b/actions/setup/js/action_conclusion_otlp.test.cjs index 3bb516ff83a..7b414437076 100644 --- a/actions/setup/js/action_conclusion_otlp.test.cjs +++ b/actions/setup/js/action_conclusion_otlp.test.cjs @@ -28,10 +28,12 @@ describe("action_conclusion_otlp.cjs", () => { originalEnv = { OTEL_EXPORTER_OTLP_ENDPOINT: process.env.OTEL_EXPORTER_OTLP_ENDPOINT, INPUT_JOB_NAME: process.env.INPUT_JOB_NAME, + "INPUT_JOB-NAME": process.env["INPUT_JOB-NAME"], GITHUB_AW_OTEL_JOB_START_MS: process.env.GITHUB_AW_OTEL_JOB_START_MS, }; delete process.env.OTEL_EXPORTER_OTLP_ENDPOINT; delete process.env.INPUT_JOB_NAME; + delete process.env["INPUT_JOB-NAME"]; delete process.env.GITHUB_AW_OTEL_JOB_START_MS; }); @@ -49,6 +51,11 @@ describe("action_conclusion_otlp.cjs", () => { } else { delete process.env.INPUT_JOB_NAME; } + if (originalEnv["INPUT_JOB-NAME"] !== undefined) { + process.env["INPUT_JOB-NAME"] = originalEnv["INPUT_JOB-NAME"]; + } else { + delete process.env["INPUT_JOB-NAME"]; + } if (originalEnv.GITHUB_AW_OTEL_JOB_START_MS !== undefined) { process.env.GITHUB_AW_OTEL_JOB_START_MS = originalEnv.GITHUB_AW_OTEL_JOB_START_MS; } else { @@ -113,6 +120,15 @@ describe("action_conclusion_otlp.cjs", () => { expect(mockSendJobConclusionSpan).toHaveBeenCalledWith("gh-aw.agent.conclusion", { startMs: undefined }); }); + it("should use job name from INPUT_JOB-NAME (hyphen form) when INPUT_JOB_NAME is not set", async () => { + delete process.env.INPUT_JOB_NAME; + process.env["INPUT_JOB-NAME"] = "agent"; + + await run(); + + expect(mockSendJobConclusionSpan).toHaveBeenCalledWith("gh-aw.agent.conclusion", { startMs: undefined }); + }); + it("should log the full span name in the sending message", async () => { process.env.INPUT_JOB_NAME = "setup"; diff --git a/actions/setup/js/action_otlp.test.cjs b/actions/setup/js/action_otlp.test.cjs index b3abc2700e0..4c323f010bb 100644 --- a/actions/setup/js/action_otlp.test.cjs +++ b/actions/setup/js/action_otlp.test.cjs @@ -152,6 +152,32 @@ describe("action_setup_otlp run()", () => { fetchSpy.mockRestore(); }); + it("uses job name from INPUT_JOB-NAME (hyphen form) in setup span when INPUT_JOB_NAME is not set", async () => { + const tmpOut = path.join(path.dirname(__dirname), `action_setup_otlp_test_job_name_hyphen_${Date.now()}.txt`); + try { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "http://localhost:14317"; + process.env["INPUT_JOB-NAME"] = "agent"; + delete process.env.INPUT_JOB_NAME; + process.env.GITHUB_OUTPUT = tmpOut; + process.env.GITHUB_ENV = tmpOut; + + let capturedBody; + const fetchSpy = vi.spyOn(global, "fetch").mockImplementation((_url, opts) => { + capturedBody = opts?.body; + return Promise.resolve(new Response(null, { status: 200 })); + }); + + await runSetup(); + + const payload = JSON.parse(capturedBody); + const spanName = payload?.resourceSpans?.[0]?.scopeSpans?.[0]?.spans?.[0]?.name; + expect(spanName).toBe("gh-aw.agent.setup"); + fetchSpy.mockRestore(); + } finally { + fs.rmSync(tmpOut, { force: true }); + } + }); + it("includes github.repository, github.run_id resource attributes in setup span", async () => { const tmpOut = path.join(path.dirname(__dirname), `action_setup_otlp_test_resource_attrs_${Date.now()}.txt`); try { diff --git a/actions/setup/js/action_setup_otlp.cjs b/actions/setup/js/action_setup_otlp.cjs index a2a24080a7e..22fc3e3888a 100644 --- a/actions/setup/js/action_setup_otlp.cjs +++ b/actions/setup/js/action_setup_otlp.cjs @@ -57,6 +57,18 @@ async function run() { console.log("[otlp] INPUT_TRACE_ID not set, a new trace ID will be generated"); } + // Normalize job-name input: handle both INPUT_JOB_NAME (underscore, standard) + // and INPUT_JOB-NAME (hyphen, used by some runner versions). Mirror the same + // two-key lookup that INPUT_TRACE_ID uses above so script-mode invocations + // (setup.sh → node action_setup_otlp.cjs) always resolve the job name even + // when the runner preserves the original hyphen in the env var name. + const inputJobName = (process.env.INPUT_JOB_NAME || process.env["INPUT_JOB-NAME"] || "").trim(); + if (inputJobName) { + // Normalise to the canonical underscore form so sendJobSetupSpan (which + // reads process.env.INPUT_JOB_NAME) always finds the value. + process.env.INPUT_JOB_NAME = inputJobName; + } + if (!endpoint) { console.log("[otlp] OTEL_EXPORTER_OTLP_ENDPOINT not set, skipping setup span"); } else { diff --git a/actions/setup/setup.sh b/actions/setup/setup.sh index 4f3f823f19c..19858cb72d0 100755 --- a/actions/setup/setup.sh +++ b/actions/setup/setup.sh @@ -407,7 +407,7 @@ fi # Skipped when GH_AW_SKIP_SETUP_OTLP=1 because index.js will send the span itself. if [ -z "${GH_AW_SKIP_SETUP_OTLP}" ] && command -v node &>/dev/null && [ -f "${DESTINATION}/action_setup_otlp.cjs" ]; then debug_log "Sending OTLP setup span..." - SETUP_START_MS="${SETUP_START_MS}" INPUT_TRACE_ID="${INPUT_TRACE_ID:-}" node "${DESTINATION}/action_setup_otlp.cjs" || true + SETUP_START_MS="${SETUP_START_MS}" INPUT_TRACE_ID="${INPUT_TRACE_ID:-}" INPUT_JOB_NAME="${INPUT_JOB_NAME:-}" node "${DESTINATION}/action_setup_otlp.cjs" || true debug_log "OTLP setup span step complete" fi