Skip to content

[otel-advisor] add github.ref_name to span resource attributes for branch-level filtering #27900

@github-actions

Description

@github-actions

📡 OTel Instrumentation Improvement: add github.ref_name to span resource attributes

Analysis Date: 2026-04-22
Priority: Medium
Effort: Small (< 1h)

Problem

Every span currently captures github.ref (the full Git ref, e.g. refs/heads/main or refs/pull/123/merge), but never captures GITHUB_REF_NAME — the short, human-readable branch or tag name (e.g. main, v1.2.3, 123/merge). This env var is freely available in every GitHub Actions job via process.env.GITHUB_REF_NAME.

As a result, any query that filters or groups by branch name requires a string-strip operation on the full ref path. Most OTel backends (Grafana Tempo, Honeycomb, Datadog) do not support inline string transformation in their filter UIs, so branch-level dashboards are either impossible or require a derived field workaround.

The codebase already knows about GITHUB_REF_NAME — it is read in actions/setup/js/get_current_branch.cjs (line 32) — but send_otlp_span.cjs never reads it.

Why This Matters (DevOps Perspective)

The most common observability questions for a CI workflow system are:

  • "Is the failure rate higher on PRs than on main?"
  • "Which feature branch is consuming the most tokens?"
  • "Are staging runs on main slower than on release branches?"

All of these require filtering by branch name. With github.ref = "refs/heads/main" and no github.ref_name, engineers must use workarounds like github.ref contains "heads/main" — which breaks for refs/pull/*/merge PR refs entirely.

Adding github.ref_name unblocks these queries in every backend with zero backend-side configuration. Adding github.head_ref (the source branch for PRs) enables grouping by PR source branch specifically.

Current Behavior

// actions/setup/js/send_otlp_span.cjs — sendJobSetupSpan (lines ~490–528)
const ref = process.env.GITHUB_REF || "";   // "refs/heads/main"
// GITHUB_REF_NAME is never read here

// resource attributes include:
if (ref) {
  resourceAttributes.push(buildAttr("github.ref", ref));  // "refs/heads/main"
}
// github.ref_name is missing — no way to filter by "main" directly

The same pattern repeats in sendJobConclusionSpan (lines ~792–806).

Proposed Change

// In both sendJobSetupSpan and sendJobConclusionSpan, after reading `ref`:
const ref = process.env.GITHUB_REF || "";
const refName = process.env.GITHUB_REF_NAME || "";        // "main", "v1.2.3", "123/merge"
const headRef = process.env.GITHUB_HEAD_REF || "";        // PR source branch, e.g. "feature-x"

// Then in the resourceAttributes block:
if (ref) {
  resourceAttributes.push(buildAttr("github.ref", ref));
}
if (refName) {
  resourceAttributes.push(buildAttr("github.ref_name", refName));
}
if (headRef) {
  resourceAttributes.push(buildAttr("github.head_ref", headRef)); // only set for pull_request events
}

This is a ~6-line addition repeated in two functions. The headRef line is optional but high value: for PR workflows, github.head_ref = "feature-xyz" enables grouping by the real feature branch name (not the merge ref 123/merge).

Expected Outcome

After this change:

  • In Grafana / Honeycomb / Datadog: Filter spans by github.ref_name = "main" or group a heatmap by github.ref_name. PR source-branch grouping becomes possible via github.head_ref.
  • In the JSONL mirror: Each span line will include github.ref_name in its resource.attributes, making jq filtering trivial: jq 'select(.resourceSpans[].resource.attributes[] | select(.key == "github.ref_name") | .value.stringValue == "main")'
  • For on-call engineers: Can immediately answer "does this failure pattern correlate with a specific branch?" without constructing a complex regex against github.ref.
Implementation Steps
  • In actions/setup/js/send_otlp_span.cjs, add const refName = process.env.GITHUB_REF_NAME || ""; and const headRef = process.env.GITHUB_HEAD_REF || ""; in both sendJobSetupSpan (around line 495) and sendJobConclusionSpan (around line 695)
  • Push buildAttr("github.ref_name", refName) (conditional on truthy) into resourceAttributes in both functions, immediately after the existing github.ref push
  • Push buildAttr("github.head_ref", headRef) (conditional on truthy) for PR event enrichment
  • Update the corresponding test file (send_otlp_span.test.cjs) to set process.env.GITHUB_REF_NAME = "main" and assert the attribute appears in the resource attributes of both setup and conclusion payloads
  • Run cd actions/setup/js && npx vitest run to confirm tests pass
  • Run make fmt to ensure formatting
  • Open a PR referencing this issue

Evidence from Static Analysis

The gap was confirmed by cross-referencing:

  1. send_otlp_span.cjs lines 489–527GITHUB_REF is read and emitted as github.ref; GITHUB_REF_NAME is absent entirely.
  2. actions/setup/js/get_current_branch.cjs line 32 — the same codebase already reads GITHUB_REF_NAME for other purposes, confirming the variable is available at runtime.
  3. Live Sentry data: The Sentry MCP server returned an empty tools list (endpoint (host.docker.internal/redacted) returned []), so live span payloads could not be sampled. The recommendation is based solely on static analysis. Once Sentry access is restored, sampling a recent span's resource.attributesarray and confirming the absence ofgithub.ref_name` would validate this finding empirically.

Related Files

  • actions/setup/js/send_otlp_span.cjs — primary change site (both sendJobSetupSpan and sendJobConclusionSpan)
  • actions/setup/js/get_current_branch.cjs — demonstrates GITHUB_REF_NAME is already used in the repo
  • actions/setup/js/action_setup_otlp.cjs — no change needed (delegates to sendJobSetupSpan)
  • actions/setup/js/action_conclusion_otlp.cjs — no change needed (delegates to sendJobConclusionSpan)

Generated by the Daily OTel Instrumentation Advisor workflow

Generated by Daily OTel Instrumentation Advisor · ● 157.4K ·

  • expires on Apr 29, 2026, 9:31 PM UTC

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions