Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion actions/setup/js/send_otlp_span.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ async function sendJobConclusionSpan(spanName, options = {}) {
const rawParentSpanId = (process.env.GITHUB_AW_OTEL_PARENT_SPAN_ID || "").trim().toLowerCase();
const parentSpanId = isValidSpanId(rawParentSpanId) ? rawParentSpanId : "";

Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sendJobConclusionSpan now resolves workflowName from GH_AW_INFO_WORKFLOW_NAME and GITHUB_WORKFLOW as fallbacks. The function’s JSDoc “Environment variables consumed” list should be updated to include these variables so the documentation matches the implementation.

Suggested change
// Resolve workflowName from aw_info first, then fall back to the environment
// variables GH_AW_INFO_WORKFLOW_NAME and GITHUB_WORKFLOW.

Copilot uses AI. Check for mistakes.
const workflowName = awInfo.workflow_name || "";
const workflowName = awInfo.workflow_name || process.env.GH_AW_INFO_WORKFLOW_NAME || process.env.GITHUB_WORKFLOW || "";
const engineId = awInfo.engine_id || "";
const model = awInfo.model || "";
const staged = awInfo.staged === true;
Expand Down
71 changes: 71 additions & 0 deletions actions/setup/js/send_otlp_span.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -1500,6 +1500,8 @@ describe("sendJobConclusionSpan", () => {
"GITHUB_SHA",
"INPUT_JOB_NAME",
"GH_AW_AGENT_CONCLUSION",
"GH_AW_INFO_WORKFLOW_NAME",
"GITHUB_WORKFLOW",
];
let mkdirSpy, appendSpy;

Expand Down Expand Up @@ -1584,6 +1586,75 @@ describe("sendJobConclusionSpan", () => {
expect(attrs["gh-aw.run.attempt"]).toBe("1");
});

it("reads gh-aw.workflow.name from aw_info.json when present", async () => {
const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" });
vi.stubGlobal("fetch", mockFetch);

process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "https://traces.example.com";
process.env.GH_AW_INFO_WORKFLOW_NAME = "env-workflow";
process.env.GITHUB_WORKFLOW = "github-workflow";

const readFileSpy = vi.spyOn(fs, "readFileSync").mockImplementation(filePath => {
if (filePath === "/tmp/gh-aw/aw_info.json") {
return JSON.stringify({ workflow_name: "aw-info-workflow" });
}
throw Object.assign(new Error("ENOENT"), { code: "ENOENT" });
});

await sendJobConclusionSpan("gh-aw.job.conclusion");
readFileSpy.mockRestore();

const body = JSON.parse(mockFetch.mock.calls[0][1].body);
const span = body.resourceSpans[0].scopeSpans[0].spans[0];
const attrs = Object.fromEntries(span.attributes.map(a => [a.key, a.value.stringValue]));
expect(attrs["gh-aw.workflow.name"]).toBe("aw-info-workflow");
});

it("falls back to GH_AW_INFO_WORKFLOW_NAME when aw_info.json is absent", async () => {
const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" });
vi.stubGlobal("fetch", mockFetch);

process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "https://traces.example.com";
process.env.GH_AW_INFO_WORKFLOW_NAME = "env-workflow";
process.env.GITHUB_WORKFLOW = "github-workflow";

await sendJobConclusionSpan("gh-aw.job.conclusion");

Comment on lines +1613 to +1622
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test asserts behavior "when aw_info.json is absent" but doesn’t stub fs.readFileSync/readJSONIfExists to force that condition. If /tmp/gh-aw/aw_info.json exists on the machine running tests, the test will read it and may fail. Consider stubbing fs.readFileSync to throw ENOENT (or adding a default readFileSync stub in beforeEach for this describe) and overriding it only in tests that need file contents.

This issue also appears in the following locations of the same file:

  • line 1629
  • line 1644

Copilot uses AI. Check for mistakes.
const body = JSON.parse(mockFetch.mock.calls[0][1].body);
const span = body.resourceSpans[0].scopeSpans[0].spans[0];
const attrs = Object.fromEntries(span.attributes.map(a => [a.key, a.value.stringValue]));
expect(attrs["gh-aw.workflow.name"]).toBe("env-workflow");
});

it("falls back to GITHUB_WORKFLOW when aw_info.json and GH_AW_INFO_WORKFLOW_NAME are absent", async () => {
const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" });
vi.stubGlobal("fetch", mockFetch);

process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "https://traces.example.com";
process.env.GITHUB_WORKFLOW = "github-workflow";

await sendJobConclusionSpan("gh-aw.job.conclusion");

const body = JSON.parse(mockFetch.mock.calls[0][1].body);
const span = body.resourceSpans[0].scopeSpans[0].spans[0];
const attrs = Object.fromEntries(span.attributes.map(a => [a.key, a.value.stringValue]));
expect(attrs["gh-aw.workflow.name"]).toBe("github-workflow");
});

it("sets gh-aw.workflow.name to empty string when all sources are absent", async () => {
const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" });
vi.stubGlobal("fetch", mockFetch);

process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "https://traces.example.com";

await sendJobConclusionSpan("gh-aw.job.conclusion");

const body = JSON.parse(mockFetch.mock.calls[0][1].body);
const span = body.resourceSpans[0].scopeSpans[0].spans[0];
const attrs = Object.fromEntries(span.attributes.map(a => [a.key, a.value.stringValue]));
expect(attrs["gh-aw.workflow.name"]).toBe("");
});

it("includes effective_tokens attribute when GH_AW_EFFECTIVE_TOKENS is set", async () => {
const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" });
vi.stubGlobal("fetch", mockFetch);
Expand Down
Loading