fix: normalize INPUT_JOB_NAME hyphen variant so OTLP spans include the actual job name#24823
fix: normalize INPUT_JOB_NAME hyphen variant so OTLP spans include the actual job name#24823
Conversation
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>
There was a problem hiding this comment.
Pull request overview
Fixes OTLP span naming so job lifecycle spans consistently include the real job name even when some runner versions expose the input env var as INPUT_JOB-NAME (hyphen) instead of INPUT_JOB_NAME (underscore).
Changes:
- Normalize
INPUT_JOB[-_]NAMEto the canonical underscore form in the JS entrypoint and setup-span sender. - Use a normalized job-name lookup in the conclusion-span sender.
- Add tests to validate the hyphen-form job-name behavior for both setup and conclusion spans.
Show a summary per file
| File | Description |
|---|---|
| actions/setup/setup.sh | Forwards INPUT_JOB_NAME into the script-mode action_setup_otlp.cjs invocation. |
| actions/setup/js/action_setup_otlp.cjs | Normalizes job name from INPUT_JOB-NAME/INPUT_JOB_NAME and sets canonical process.env.INPUT_JOB_NAME. |
| actions/setup/js/action_otlp.test.cjs | Adds a setup-span test covering the hyphen-form job-name env var. |
| actions/setup/js/action_conclusion_otlp.cjs | Uses normalized job-name lookup when constructing conclusion span names. |
| actions/setup/js/action_conclusion_otlp.test.cjs | Extends conclusion-span tests to cover hyphen-form env var and restores env properly. |
| actions/setup/index.js | Normalizes job-name env var at startup and passes canonical value into setup.sh and action_setup_otlp.cjs. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 6/6 changed files
- Comments generated: 3
| 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; |
There was a problem hiding this comment.
The fallback order uses process.env.INPUT_JOB_NAME || process.env["INPUT_JOB-NAME"] before trimming, so a whitespace-only INPUT_JOB_NAME will shadow a valid hyphen-form value and resolve to an empty string after .trim(). Consider trimming each candidate first and then selecting the first non-empty value; also delete/clear process.env.INPUT_JOB_NAME when the trimmed result is empty so downstream sendJobSetupSpan() doesn’t treat whitespace as a real job 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; | |
| const inputJobNameUnderscore = (process.env.INPUT_JOB_NAME || "").trim(); | |
| const inputJobNameHyphen = (process.env["INPUT_JOB-NAME"] || "").trim(); | |
| const inputJobName = inputJobNameUnderscore || inputJobNameHyphen; | |
| 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; | |
| } else if (process.env.INPUT_JOB_NAME && !inputJobNameUnderscore) { | |
| // Clear whitespace-only underscore-form input so downstream consumers do | |
| // not treat it as a real job name. | |
| delete process.env.INPUT_JOB_NAME; |
| 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(); |
There was a problem hiding this comment.
The lookup (process.env.INPUT_JOB_NAME || process.env["INPUT_JOB-NAME"]) occurs before trimming, so a whitespace-only INPUT_JOB_NAME will prevent the hyphen-form value from being used and will fall back to the default span name after .trim(). To make the fallback robust, trim both candidates first and pick the first non-empty trimmed value.
| const jobName = (process.env.INPUT_JOB_NAME || process.env["INPUT_JOB-NAME"] || "").trim(); | |
| const underscoreJobName = (process.env.INPUT_JOB_NAME || "").trim(); | |
| const hyphenJobName = (process.env["INPUT_JOB-NAME"] || "").trim(); | |
| const jobName = underscoreJobName || hyphenJobName; |
| 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 { |
There was a problem hiding this comment.
This test restores fetchSpy only on the success path; if an assertion throws, the spy can leak into later tests (this file doesn’t have a global vi.restoreAllMocks() in afterEach). Move fetchSpy.mockRestore() into the finally block (or add an afterEach that restores mocks) to keep the suite isolated.
… normalization The two-key lookup pattern for normalizing GitHub Actions inputs (handling both INPUT_X and INPUT_X-HYPHEN forms) was introduced in PR #24823 and repeated in three places: index.js, action_setup_otlp.cjs, and action_conclusion_otlp.cjs. Extract a getActionInput(name) helper in action_input_utils.cjs that: - Checks both the underscore (standard) and hyphen (some runner versions) forms - Trims whitespace from the result (aligning with GitHub Actions conventions) - Documents the rationale once, rather than repeating it at each call site This reduces code duplication, removes repetitive comments, and makes it easy to add new inputs with the same normalization in the future. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
OTLP spans were emitting
gh-aw.job.setup/gh-aw.job.conclusioninstead ofgh-aw.<jobname>.setup/gh-aw.<jobname>.conclusionbecause some runner versions setINPUT_JOB-NAME(hyphen) instead of the standardINPUT_JOB_NAME(underscore) — the same runner quirk already handled forINPUT_TRACE_ID.Changes
index.js— normalizeINPUT_JOB-NAME→INPUT_JOB_NAMEat startup; pass canonical value intosetup.shenv and setprocess.env.INPUT_JOB_NAMEbefore delegating toaction_setup_otlp.cjsaction_setup_otlp.cjs— apply the same two-key lookup asINPUT_TRACE_ID; canonicalize to underscore form inprocess.envsosendJobSetupSpan(which readsprocess.env.INPUT_JOB_NAMEdirectly) always finds itaction_conclusion_otlp.cjs— replace directprocess.env.INPUT_JOB_NAMEread with normalized lookup before constructing the span namesetup.sh— explicitly forwardINPUT_JOB_NAMEtoaction_setup_otlp.cjsin script mode, alongside the existingINPUT_TRACE_ID