* Send a `gh-aw. .setup` span (or `gh-aw.job.setup` when no job name * is configured) to the configured OTLP endpoint. * * This is designed to be called from `actions/setup/index.js` immediately after * the setup script completes. It always returns `{ traceId, spanId, parentSpanId }` so
(options = {})
| 1212 | * @returns {Promise<{ traceId: string, spanId: string, parentSpanId: string }>} The trace/span IDs used and resolved parent span ID. |
| 1213 | */ |
| 1214 | async function sendJobSetupSpan(options = {}) { |
| 1215 | // Resolve the trace ID before the early-return so it is always available as |
| 1216 | // an action output regardless of whether OTLP is configured. |
| 1217 | // Priority: options.traceId > INPUT_TRACE_ID > aw_info.context.otel_trace_id > newly generated ID. |
| 1218 | // Invalid (wrong length, non-hex) values are silently discarded. |
| 1219 | |
| 1220 | // Validate options.traceId if supplied; callers may pass raw user input. |
| 1221 | const optionsTraceId = options.traceId && isValidTraceId(options.traceId) ? options.traceId : ""; |
| 1222 | const optionsParentSpanId = options.parentSpanId && isValidSpanId(options.parentSpanId) ? options.parentSpanId : ""; |
| 1223 | |
| 1224 | // Normalize INPUT_TRACE_ID to lowercase before validating: OTLP requires lowercase |
| 1225 | // hex, but trace IDs pasted from external tools may use uppercase characters. |
| 1226 | // Also handle INPUT_TRACE-ID (with hyphen) in case the runner preserves the original |
| 1227 | // input name hyphen instead of converting it to an underscore. |
| 1228 | const rawInputTraceId = (process.env.INPUT_TRACE_ID || process.env["INPUT_TRACE-ID"] || "").trim().toLowerCase(); |
| 1229 | const inputTraceId = isValidTraceId(rawInputTraceId) ? rawInputTraceId : ""; |
| 1230 | const rawInputParentSpanId = (process.env.INPUT_PARENT_SPAN_ID || process.env["INPUT_PARENT-SPAN-ID"] || "").trim().toLowerCase(); |
| 1231 | const inputParentSpanId = isValidSpanId(rawInputParentSpanId) ? rawInputParentSpanId : ""; |
| 1232 | |
| 1233 | // When this job was dispatched by a parent workflow, the parent's trace ID is |
| 1234 | // propagated via aw_context.otel_trace_id → aw_info.context.otel_trace_id so that |
| 1235 | // composite-action spans share a single trace with their caller. |
| 1236 | const awInfo = readJSONIfExists("/tmp/gh-aw/aw_info.json") || {}; |
| 1237 | const setupAwContext = parseSetupAwContext(process.env.GH_AW_SETUP_AW_CONTEXT); |
| 1238 | if ((!awInfo.context || typeof awInfo.context !== "object") && Object.keys(setupAwContext).length > 0) { |
| 1239 | awInfo.context = setupAwContext; |
| 1240 | } |
| 1241 | const rawContextTraceId = typeof awInfo.context?.otel_trace_id === "string" ? awInfo.context.otel_trace_id.trim().toLowerCase() : ""; |
| 1242 | const contextTraceId = isValidTraceId(rawContextTraceId) ? rawContextTraceId : ""; |
| 1243 | // When this job was dispatched by a parent workflow, the parent's setup span ID is |
| 1244 | // propagated via aw_context.otel_parent_span_id → aw_info.context.otel_parent_span_id so |
| 1245 | // that the child's setup span is nested under the parent's setup span in the trace. |
| 1246 | const rawContextParentSpanId = typeof awInfo.context?.otel_parent_span_id === "string" ? awInfo.context.otel_parent_span_id.trim().toLowerCase() : ""; |
| 1247 | const contextParentSpanId = isValidSpanId(rawContextParentSpanId) ? rawContextParentSpanId : ""; |
| 1248 | const staged = awInfo.staged === true || process.env.GH_AW_INFO_STAGED === "true"; |
| 1249 | const itemType = typeof awInfo.context?.item_type === "string" ? awInfo.context.item_type : ""; |
| 1250 | const itemNumber = typeof awInfo.context?.item_number === "string" ? awInfo.context.item_number : ""; |
| 1251 | const triggerLabel = typeof awInfo.context?.trigger_label === "string" ? awInfo.context.trigger_label : ""; |
| 1252 | const commentId = typeof awInfo.context?.comment_id === "string" ? awInfo.context.comment_id : ""; |
| 1253 | const frontmatterSource = (typeof awInfo.frontmatter_source === "string" ? awInfo.frontmatter_source : "") || process.env.GH_AW_INFO_FRONTMATTER_SOURCE || ""; |
| 1254 | const frontmatterEmoji = (typeof awInfo.frontmatter_emoji === "string" ? awInfo.frontmatter_emoji : "") || process.env.GH_AW_INFO_FRONTMATTER_EMOJI || ""; |
| 1255 | const awfVersion = (typeof awInfo.awf_version === "string" ? awInfo.awf_version : "") || process.env.GH_AW_INFO_AWF_VERSION || ""; |
| 1256 | const awmgVersion = (typeof awInfo.awmg_version === "string" ? awInfo.awmg_version : "") || process.env.GH_AW_INFO_AWMG_VERSION || ""; |
| 1257 | const bodyModified = typeof awInfo.body_modified === "boolean" ? awInfo.body_modified : parseBooleanEnv(process.env.GH_AW_INFO_BODY_MODIFIED); |
| 1258 | |
| 1259 | const traceId = optionsTraceId || inputTraceId || contextTraceId || generateTraceId(); |
| 1260 | const parentSpanId = optionsParentSpanId || inputParentSpanId || contextParentSpanId || ""; |
| 1261 | |
| 1262 | // Always generate a span ID so it can be written to GITHUB_ENV as |
| 1263 | // GITHUB_AW_OTEL_PARENT_SPAN_ID even when OTLP is not configured, allowing downstream |
| 1264 | // scripts to establish the correct parent span context. |
| 1265 | const spanId = generateSpanId(); |
| 1266 | |
| 1267 | // Build the full payload unconditionally so the JSONL mirror is always written, |
| 1268 | // enabling artifact-based debugging even without a live OTLP collector. |
| 1269 | const startMs = options.startMs ?? nowMs(); |
| 1270 | const endMs = nowMs(); |
| 1271 |
no test coverage detected