From d40df62c4682d097902b027f4b5866326b3c6b18 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 04:24:51 +0000 Subject: [PATCH 01/16] Initial plan From 88889a93a3f02fe1dbc181cb36b8e0fe2acb2e61 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 04:47:28 +0000 Subject: [PATCH 02/16] feat: add OTLP trace export configuration to observability section (#issue) Agent-Logs-Url: https://github.com/github/gh-aw/sessions/49301b6f-02ce-44b4-8fd5-1bc9a878d6cc Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/parser/schemas/main_workflow_schema.json | 18 +- .../compiler_orchestrator_workflow.go | 4 + pkg/workflow/frontmatter_types.go | 12 +- pkg/workflow/observability_otlp.go | 86 +++++ pkg/workflow/observability_otlp_test.go | 332 ++++++++++++++++++ 5 files changed, 450 insertions(+), 2 deletions(-) create mode 100644 pkg/workflow/observability_otlp.go create mode 100644 pkg/workflow/observability_otlp_test.go diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 66026536a35..303db6db17d 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -8328,6 +8328,17 @@ "type": "string", "enum": ["on", "off"], "description": "If set to 'on', append a compact observability section to the GitHub Actions job summary. Defaults to off when omitted." + }, + "otlp": { + "type": "object", + "description": "OTLP (OpenTelemetry Protocol) trace export configuration.", + "properties": { + "endpoint": { + "type": "string", + "description": "OTLP collector endpoint URL (e.g. 'https://traces.example.com:4317'). Supports GitHub Actions expressions such as ${{ secrets.OTLP_ENDPOINT }}. When a static URL is provided, its hostname is automatically added to the network firewall allowlist." + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -9161,7 +9172,12 @@ "type": "number", "minimum": 0 }, - "examples": [{ "my-custom-model": 2.5, "gpt-5": 3.0 }] + "examples": [ + { + "my-custom-model": 2.5, + "gpt-5": 3.0 + } + ] }, "token-class-weights": { "type": "object", diff --git a/pkg/workflow/compiler_orchestrator_workflow.go b/pkg/workflow/compiler_orchestrator_workflow.go index 4b6630d9221..57d5ff5d1ac 100644 --- a/pkg/workflow/compiler_orchestrator_workflow.go +++ b/pkg/workflow/compiler_orchestrator_workflow.go @@ -109,6 +109,10 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error) // Extract YAML configuration sections from frontmatter c.extractYAMLSections(result.Frontmatter, workflowData) + // Inject OTLP configuration: add endpoint domain to firewall allowlist and + // set OTEL env vars in the workflow env block (no-op when not configured). + c.injectOTLPConfig(workflowData) + // Merge features from imports if len(engineSetup.importsResult.MergedFeatures) > 0 { mergedFeatures, err := c.MergeFeatures(workflowData.Features, engineSetup.importsResult.MergedFeatures) diff --git a/pkg/workflow/frontmatter_types.go b/pkg/workflow/frontmatter_types.go index c4866381220..ca8c9afa3c8 100644 --- a/pkg/workflow/frontmatter_types.go +++ b/pkg/workflow/frontmatter_types.go @@ -117,9 +117,19 @@ type RateLimitConfig struct { IgnoredRoles []string `json:"ignored-roles,omitempty"` // Roles that are exempt from rate limiting (e.g., ["admin", "maintainer"]) } +// OTLPConfig holds configuration for OTLP (OpenTelemetry Protocol) trace export. +type OTLPConfig struct { + // Endpoint is the OTLP collector endpoint URL (e.g. "https://traces.example.com:4317"). + // Supports GitHub Actions expressions such as ${{ secrets.OTLP_ENDPOINT }}. + // When a static URL is provided, its hostname is automatically added to the + // network firewall allowlist. + Endpoint string `json:"endpoint,omitempty"` +} + // ObservabilityConfig represents workflow observability options. type ObservabilityConfig struct { - JobSummary string `json:"job-summary,omitempty"` + JobSummary string `json:"job-summary,omitempty"` + OTLP *OTLPConfig `json:"otlp,omitempty"` } // FrontmatterConfig represents the structured configuration from workflow frontmatter diff --git a/pkg/workflow/observability_otlp.go b/pkg/workflow/observability_otlp.go new file mode 100644 index 00000000000..e5eb0a29c32 --- /dev/null +++ b/pkg/workflow/observability_otlp.go @@ -0,0 +1,86 @@ +package workflow + +import ( + "fmt" + "net/url" + "strings" + + "github.com/github/gh-aw/pkg/logger" +) + +var otlpLog = logger.New("workflow:observability_otlp") + +// extractOTLPEndpointDomain parses an OTLP endpoint URL and returns its hostname. +// Returns an empty string when the endpoint is a GitHub Actions expression (which +// cannot be resolved at compile time) or when the URL is otherwise invalid. +func extractOTLPEndpointDomain(endpoint string) string { + if endpoint == "" { + return "" + } + + // GitHub Actions expressions (e.g. ${{ secrets.OTLP_ENDPOINT }}) cannot be + // resolved at compile time, so skip domain extraction for them. + if strings.Contains(endpoint, "${{") { + otlpLog.Printf("OTLP endpoint is a GitHub Actions expression, skipping domain extraction: %s", endpoint) + return "" + } + + parsed, err := url.Parse(endpoint) + if err != nil || parsed.Host == "" { + otlpLog.Printf("Failed to extract domain from OTLP endpoint %q: %v", endpoint, err) + return "" + } + + // Strip the port from the host so the AWF domain allowlist entry matches all ports + // (e.g. "traces.example.com:4317" → "traces.example.com"). + host := parsed.Hostname() + otlpLog.Printf("Extracted OTLP domain: %s", host) + return host +} + +// getOTLPEndpointEnvValue returns the raw endpoint value suitable for injecting as an +// environment variable in the generated GitHub Actions workflow YAML. +// Returns an empty string when no OTLP endpoint is configured. +func getOTLPEndpointEnvValue(config *FrontmatterConfig) string { + if config == nil || config.Observability == nil || config.Observability.OTLP == nil { + return "" + } + return config.Observability.OTLP.Endpoint +} + +// injectOTLPConfig modifies workflowData to incorporate any OTLP configuration: +// +// 1. When the endpoint is a static URL, its hostname is appended to +// NetworkPermissions.Allowed so the AWF firewall allows outbound traffic to it. +// +// 2. OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_SERVICE_NAME are appended to the +// workflow-level env: YAML block (workflowData.Env) so they are available to +// every step in the generated GitHub Actions workflow. +// +// When no OTLP endpoint is configured the function is a no-op. +func (c *Compiler) injectOTLPConfig(workflowData *WorkflowData) { + endpoint := getOTLPEndpointEnvValue(workflowData.ParsedFrontmatter) + if endpoint == "" { + return + } + + otlpLog.Printf("Injecting OTLP configuration: endpoint=%s", endpoint) + + // 1. Add OTLP endpoint domain to the firewall allowlist. + if domain := extractOTLPEndpointDomain(endpoint); domain != "" { + if workflowData.NetworkPermissions == nil { + workflowData.NetworkPermissions = &NetworkPermissions{} + } + workflowData.NetworkPermissions.Allowed = append(workflowData.NetworkPermissions.Allowed, domain) + otlpLog.Printf("Added OTLP domain to network allowlist: %s", domain) + } + + // 2. Inject OTEL env vars into the workflow-level env: block. + otlpEnvLines := fmt.Sprintf(" OTEL_EXPORTER_OTLP_ENDPOINT: %s\n OTEL_SERVICE_NAME: gh-aw", endpoint) + if workflowData.Env == "" { + workflowData.Env = "env:\n" + otlpEnvLines + } else { + workflowData.Env = workflowData.Env + "\n" + otlpEnvLines + } + otlpLog.Printf("Injected OTEL env vars into workflow env block") +} diff --git a/pkg/workflow/observability_otlp_test.go b/pkg/workflow/observability_otlp_test.go new file mode 100644 index 00000000000..fbdc52931cc --- /dev/null +++ b/pkg/workflow/observability_otlp_test.go @@ -0,0 +1,332 @@ +//go:build !integration + +package workflow + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestExtractOTLPEndpointDomain verifies hostname extraction from OTLP endpoint URLs. +func TestExtractOTLPEndpointDomain(t *testing.T) { + tests := []struct { + name string + endpoint string + expected string + }{ + { + name: "empty endpoint returns empty string", + endpoint: "", + expected: "", + }, + { + name: "GitHub Actions expression returns empty string", + endpoint: "${{ secrets.OTLP_ENDPOINT }}", + expected: "", + }, + { + name: "inline expression returns empty string", + endpoint: "https://${{ secrets.HOST }}:4317", + expected: "", + }, + { + name: "HTTPS URL without port", + endpoint: "https://traces.example.com", + expected: "traces.example.com", + }, + { + name: "HTTPS URL with port", + endpoint: "https://traces.example.com:4317", + expected: "traces.example.com", + }, + { + name: "HTTP URL with path", + endpoint: "http://otel-collector.internal:4318/v1/traces", + expected: "otel-collector.internal", + }, + { + name: "gRPC URL", + endpoint: "grpc://traces.example.com:4317", + expected: "traces.example.com", + }, + { + name: "subdomain", + endpoint: "https://otel.collector.corp.example.com:4317", + expected: "otel.collector.corp.example.com", + }, + { + name: "invalid URL (no scheme) returns empty string", + endpoint: "traces.example.com:4317", + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := extractOTLPEndpointDomain(tt.endpoint) + assert.Equal(t, tt.expected, got, "extractOTLPEndpointDomain(%q)", tt.endpoint) + }) + } +} + +// TestGetOTLPEndpointEnvValue verifies endpoint value extraction from FrontmatterConfig. +func TestGetOTLPEndpointEnvValue(t *testing.T) { + tests := []struct { + name string + config *FrontmatterConfig + expected string + }{ + { + name: "nil config returns empty string", + config: nil, + expected: "", + }, + { + name: "nil observability returns empty string", + config: &FrontmatterConfig{}, + expected: "", + }, + { + name: "nil OTLP returns empty string", + config: &FrontmatterConfig{ + Observability: &ObservabilityConfig{}, + }, + expected: "", + }, + { + name: "empty endpoint returns empty string", + config: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{Endpoint: ""}, + }, + }, + expected: "", + }, + { + name: "static URL endpoint", + config: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{Endpoint: "https://traces.example.com:4317"}, + }, + }, + expected: "https://traces.example.com:4317", + }, + { + name: "secret expression endpoint", + config: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{Endpoint: "${{ secrets.OTLP_ENDPOINT }}"}, + }, + }, + expected: "${{ secrets.OTLP_ENDPOINT }}", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getOTLPEndpointEnvValue(tt.config) + assert.Equal(t, tt.expected, got, "getOTLPEndpointEnvValue") + }) + } +} + +// TestInjectOTLPConfig verifies that injectOTLPConfig correctly modifies WorkflowData. +func TestInjectOTLPConfig(t *testing.T) { + newCompiler := func() *Compiler { return &Compiler{} } + + t.Run("no-op when OTLP is not configured", func(t *testing.T) { + c := newCompiler() + wd := &WorkflowData{ + ParsedFrontmatter: &FrontmatterConfig{}, + } + c.injectOTLPConfig(wd) + assert.Nil(t, wd.NetworkPermissions, "NetworkPermissions should remain nil") + assert.Empty(t, wd.Env, "Env should remain empty") + }) + + t.Run("no-op when ParsedFrontmatter is nil", func(t *testing.T) { + c := newCompiler() + wd := &WorkflowData{} + c.injectOTLPConfig(wd) + assert.Nil(t, wd.NetworkPermissions, "NetworkPermissions should remain nil") + assert.Empty(t, wd.Env, "Env should remain empty") + }) + + t.Run("injects env vars when endpoint is a secret expression", func(t *testing.T) { + c := newCompiler() + wd := &WorkflowData{ + ParsedFrontmatter: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{Endpoint: "${{ secrets.OTLP_ENDPOINT }}"}, + }, + }, + } + c.injectOTLPConfig(wd) + + // NetworkPermissions.Allowed should NOT be populated (can't resolve expression) + if wd.NetworkPermissions != nil { + assert.Empty(t, wd.NetworkPermissions.Allowed, "Allowed should be empty for expression endpoints") + } + + // Env should contain the OTEL vars + require.NotEmpty(t, wd.Env, "Env should be set") + assert.Contains(t, wd.Env, "OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTLP_ENDPOINT }}", "should contain endpoint var") + assert.Contains(t, wd.Env, "OTEL_SERVICE_NAME: gh-aw", "should contain service name") + }) + + t.Run("adds domain to new NetworkPermissions and injects env vars for static URL", func(t *testing.T) { + c := newCompiler() + wd := &WorkflowData{ + ParsedFrontmatter: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{Endpoint: "https://traces.example.com:4317"}, + }, + }, + } + c.injectOTLPConfig(wd) + + require.NotNil(t, wd.NetworkPermissions, "NetworkPermissions should be created") + assert.Contains(t, wd.NetworkPermissions.Allowed, "traces.example.com", "should contain OTLP domain") + + require.NotEmpty(t, wd.Env, "Env should be set") + assert.Contains(t, wd.Env, "OTEL_EXPORTER_OTLP_ENDPOINT: https://traces.example.com:4317") + assert.Contains(t, wd.Env, "OTEL_SERVICE_NAME: gh-aw") + assert.True(t, strings.HasPrefix(wd.Env, "env:"), "Env should start with 'env:'") + }) + + t.Run("appends domain to existing NetworkPermissions.Allowed", func(t *testing.T) { + c := newCompiler() + wd := &WorkflowData{ + ParsedFrontmatter: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{Endpoint: "https://traces.example.com:4317"}, + }, + }, + NetworkPermissions: &NetworkPermissions{ + Allowed: []string{"api.github.com", "pypi.org"}, + }, + } + c.injectOTLPConfig(wd) + + assert.Contains(t, wd.NetworkPermissions.Allowed, "api.github.com", "existing domains should remain") + assert.Contains(t, wd.NetworkPermissions.Allowed, "pypi.org", "existing domains should remain") + assert.Contains(t, wd.NetworkPermissions.Allowed, "traces.example.com", "OTLP domain should be appended") + }) + + t.Run("appends OTEL vars to existing Env block", func(t *testing.T) { + c := newCompiler() + wd := &WorkflowData{ + ParsedFrontmatter: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{Endpoint: "https://traces.example.com"}, + }, + }, + Env: "env:\n MY_VAR: hello", + } + c.injectOTLPConfig(wd) + + assert.Contains(t, wd.Env, "MY_VAR: hello", "existing env var should remain") + assert.Contains(t, wd.Env, "OTEL_EXPORTER_OTLP_ENDPOINT: https://traces.example.com") + assert.Contains(t, wd.Env, "OTEL_SERVICE_NAME: gh-aw") + // Should still be a single env: block + assert.Equal(t, 1, strings.Count(wd.Env, "env:"), "should have exactly one env: key") + }) + + t.Run("OTEL_SERVICE_NAME is always gh-aw", func(t *testing.T) { + c := newCompiler() + wd := &WorkflowData{ + ParsedFrontmatter: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{Endpoint: "https://otel.corp.com"}, + }, + }, + } + c.injectOTLPConfig(wd) + assert.Contains(t, wd.Env, "OTEL_SERVICE_NAME: gh-aw", "service name should always be gh-aw") + }) +} + +// TestObservabilityConfigParsing verifies that the OTLPConfig is correctly parsed +// from raw frontmatter via ParseFrontmatterConfig. +func TestObservabilityConfigParsing(t *testing.T) { + tests := []struct { + name string + frontmatter map[string]any + wantOTLPConfig bool + expectedEndpoint string + }{ + { + name: "no observability section", + frontmatter: map[string]any{}, + wantOTLPConfig: false, + }, + { + name: "observability without otlp", + frontmatter: map[string]any{ + "observability": map[string]any{ + "job-summary": "on", + }, + }, + wantOTLPConfig: false, + }, + { + name: "observability with otlp endpoint", + frontmatter: map[string]any{ + "observability": map[string]any{ + "otlp": map[string]any{ + "endpoint": "https://traces.example.com:4317", + }, + }, + }, + wantOTLPConfig: true, + expectedEndpoint: "https://traces.example.com:4317", + }, + { + name: "observability with otlp secret expression", + frontmatter: map[string]any{ + "observability": map[string]any{ + "otlp": map[string]any{ + "endpoint": "${{ secrets.OTLP_ENDPOINT }}", + }, + }, + }, + wantOTLPConfig: true, + expectedEndpoint: "${{ secrets.OTLP_ENDPOINT }}", + }, + { + name: "observability with both job-summary and otlp", + frontmatter: map[string]any{ + "observability": map[string]any{ + "job-summary": "on", + "otlp": map[string]any{ + "endpoint": "https://traces.example.com", + }, + }, + }, + wantOTLPConfig: true, + expectedEndpoint: "https://traces.example.com", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config, err := ParseFrontmatterConfig(tt.frontmatter) + require.NoError(t, err, "ParseFrontmatterConfig should not fail") + require.NotNil(t, config, "Config should not be nil") + + if !tt.wantOTLPConfig { + if config.Observability != nil { + assert.Nil(t, config.Observability.OTLP, "OTLP should be nil") + } + return + } + + require.NotNil(t, config.Observability, "Observability should not be nil") + require.NotNil(t, config.Observability.OTLP, "OTLP should not be nil") + assert.Equal(t, tt.expectedEndpoint, config.Observability.OTLP.Endpoint, "Endpoint should match") + }) + } +} From 233b74c4cba94b60664a35b69cc02a3d13860636 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 06:08:39 +0000 Subject: [PATCH 03/16] feat: add send_otlp_span.cjs and instrument action setup with job-name input Agent-Logs-Url: https://github.com/github/gh-aw/sessions/5738fc76-45bf-47ab-af6c-8de1dc9ec689 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/action.yml | 4 + actions/setup/index.js | 15 ++ actions/setup/js/send_otlp_span.cjs | 218 ++++++++++++++++++ actions/setup/js/send_otlp_span.test.cjs | 280 +++++++++++++++++++++++ 4 files changed, 517 insertions(+) create mode 100644 actions/setup/js/send_otlp_span.cjs create mode 100644 actions/setup/js/send_otlp_span.test.cjs diff --git a/actions/setup/action.yml b/actions/setup/action.yml index a03aabb6296..f93f57767ab 100644 --- a/actions/setup/action.yml +++ b/actions/setup/action.yml @@ -10,6 +10,10 @@ inputs: description: 'Install @actions/github for handlers that use a per-handler github-token (creates Octokit via getOctokit)' required: false default: 'false' + job-name: + description: 'Name of the job being set up. When OTEL_EXPORTER_OTLP_ENDPOINT is configured, a gh-aw.job.setup span is pushed to the OTLP endpoint.' + required: false + default: '' outputs: files_copied: diff --git a/actions/setup/index.js b/actions/setup/index.js index cda1a0cd265..bf332757927 100644 --- a/actions/setup/index.js +++ b/actions/setup/index.js @@ -4,6 +4,9 @@ const { spawnSync } = require("child_process"); const path = require("path"); +// Record start time for the OTLP span before any setup work begins. +const setupStartMs = Date.now(); + // GitHub Actions sets INPUT_* env vars for JavaScript actions by converting // input names to uppercase and replacing hyphens with underscores. Explicitly // normalise the safe-output-custom-tokens input to ensure setup.sh finds it. @@ -27,3 +30,15 @@ if (result.error) { if (result.status !== 0) { process.exit(result.status ?? 1); } + +// Send a gh-aw.job.setup span to the OTLP endpoint when configured. +// This is intentionally fire-and-forget with error suppression: trace export +// failures must never break the workflow. +(async () => { + try { + const { sendJobSetupSpan } = require(path.join(__dirname, "js", "send_otlp_span.cjs")); + await sendJobSetupSpan({ startMs: setupStartMs }); + } catch { + // Non-fatal: silently ignore any OTLP export errors. + } +})(); diff --git a/actions/setup/js/send_otlp_span.cjs b/actions/setup/js/send_otlp_span.cjs new file mode 100644 index 00000000000..8bc0cff58a2 --- /dev/null +++ b/actions/setup/js/send_otlp_span.cjs @@ -0,0 +1,218 @@ +// @ts-check +/// + +const { randomBytes } = require("crypto"); + +/** + * send_otlp_span.cjs + * + * Sends a single OTLP (OpenTelemetry Protocol) trace span to the configured + * HTTP/JSON endpoint. Used by actions/setup to instrument each job execution + * with basic telemetry. + * + * Design constraints: + * - No-op when OTEL_EXPORTER_OTLP_ENDPOINT is not set (zero overhead). + * - Errors are non-fatal: export failures must never break the workflow. + * - No third-party dependencies: uses only Node built-ins + native fetch. + */ + +// --------------------------------------------------------------------------- +// Low-level helpers +// --------------------------------------------------------------------------- + +/** + * Generate a random 16-byte trace ID encoded as a 32-character hex string. + * @returns {string} + */ +function generateTraceId() { + return randomBytes(16).toString("hex"); +} + +/** + * Generate a random 8-byte span ID encoded as a 16-character hex string. + * @returns {string} + */ +function generateSpanId() { + return randomBytes(8).toString("hex"); +} + +/** + * Convert a Unix timestamp in milliseconds to a nanosecond string suitable for + * OTLP's `startTimeUnixNano` / `endTimeUnixNano` fields. + * + * BigInt arithmetic avoids floating-point precision loss for large timestamps. + * + * @param {number} ms - milliseconds since Unix epoch + * @returns {string} nanoseconds since Unix epoch as a decimal string + */ +function toNanoString(ms) { + return (BigInt(Math.floor(ms)) * 1_000_000n).toString(); +} + +/** + * Build a single OTLP attribute object in the key-value format expected by the + * OTLP/HTTP JSON wire format. + * + * @param {string} key + * @param {string | number | boolean} value + * @returns {{ key: string, value: object }} + */ +function buildAttr(key, value) { + if (typeof value === "boolean") { + return { key, value: { boolValue: value } }; + } + if (typeof value === "number") { + return { key, value: { intValue: value } }; + } + return { key, value: { stringValue: String(value) } }; +} + +// --------------------------------------------------------------------------- +// OTLP payload builder +// --------------------------------------------------------------------------- + +/** + * @typedef {Object} OTLPSpanOptions + * @property {string} traceId - 32-char hex trace ID + * @property {string} spanId - 16-char hex span ID + * @property {string} spanName - Human-readable span name + * @property {number} startMs - Span start time (ms since epoch) + * @property {number} endMs - Span end time (ms since epoch) + * @property {string} serviceName - Value for the service.name resource attribute + * @property {Array<{key: string, value: object}>} attributes - Span attributes + */ + +/** + * Build an OTLP/HTTP JSON traces payload wrapping a single span. + * + * @param {OTLPSpanOptions} opts + * @returns {object} - Ready to be serialised as JSON and POSTed to `/v1/traces` + */ +function buildOTLPPayload({ traceId, spanId, spanName, startMs, endMs, serviceName, attributes }) { + return { + resourceSpans: [ + { + resource: { + attributes: [buildAttr("service.name", serviceName)], + }, + scopeSpans: [ + { + scope: { name: "gh-aw.setup", version: "1.0.0" }, + spans: [ + { + traceId, + spanId, + name: spanName, + kind: 2, // SPAN_KIND_SERVER + startTimeUnixNano: toNanoString(startMs), + endTimeUnixNano: toNanoString(endMs), + status: { code: 1 }, // STATUS_CODE_OK + attributes, + }, + ], + }, + ], + }, + ], + }; +} + +// --------------------------------------------------------------------------- +// HTTP transport +// --------------------------------------------------------------------------- + +/** + * POST an OTLP traces payload to `{endpoint}/v1/traces`. + * + * @param {string} endpoint - OTLP base URL (e.g. https://traces.example.com:4317) + * @param {object} payload - Serialisable OTLP JSON object + * @returns {Promise} + * @throws {Error} when the server returns a non-2xx status + */ +async function sendOTLPSpan(endpoint, payload) { + const url = endpoint.replace(/\/$/, "") + "/v1/traces"; + const response = await fetch(url, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + if (!response.ok) { + throw new Error(`OTLP export failed: HTTP ${response.status} ${response.statusText}`); + } +} + +// --------------------------------------------------------------------------- +// High-level: job setup span +// --------------------------------------------------------------------------- + +/** + * @typedef {Object} SendJobSetupSpanOptions + * @property {number} [startMs] - Override for the span start time (ms). Defaults to `Date.now()`. + */ + +/** + * Send a `gh-aw.job.setup` span to the configured OTLP endpoint. + * + * This is designed to be called from `actions/setup/index.js` immediately after + * the setup script completes. It is a no-op when `OTEL_EXPORTER_OTLP_ENDPOINT` + * is not set, and errors are swallowed so the workflow is never broken by tracing + * failures. + * + * Environment variables consumed: + * - `OTEL_EXPORTER_OTLP_ENDPOINT` – collector endpoint (required to send anything) + * - `OTEL_SERVICE_NAME` – service name (defaults to "gh-aw") + * - `INPUT_JOB_NAME` – job name passed via the `job-name` action input + * - `GH_AW_INFO_WORKFLOW_NAME` – workflow name injected by the gh-aw compiler + * - `GH_AW_INFO_ENGINE_ID` – engine ID injected by the gh-aw compiler + * - `GITHUB_RUN_ID` – GitHub Actions run ID + * - `GITHUB_ACTOR` – GitHub Actions actor (user / bot) + * - `GITHUB_REPOSITORY` – `owner/repo` string + * + * @param {SendJobSetupSpanOptions} [options] + * @returns {Promise} + */ +async function sendJobSetupSpan(options = {}) { + const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || ""; + if (!endpoint) { + return; + } + + const startMs = options.startMs ?? Date.now(); + const endMs = Date.now(); + + const serviceName = process.env.OTEL_SERVICE_NAME || "gh-aw"; + const jobName = process.env.INPUT_JOB_NAME || ""; + const workflowName = process.env.GH_AW_INFO_WORKFLOW_NAME || process.env.GITHUB_WORKFLOW || ""; + const engineId = process.env.GH_AW_INFO_ENGINE_ID || ""; + const runId = process.env.GITHUB_RUN_ID || ""; + const actor = process.env.GITHUB_ACTOR || ""; + const repository = process.env.GITHUB_REPOSITORY || ""; + + const attributes = [buildAttr("gh-aw.job.name", jobName), buildAttr("gh-aw.workflow.name", workflowName), buildAttr("gh-aw.run.id", runId), buildAttr("gh-aw.run.actor", actor), buildAttr("gh-aw.repository", repository)]; + + if (engineId) { + attributes.push(buildAttr("gh-aw.engine.id", engineId)); + } + + const payload = buildOTLPPayload({ + traceId: generateTraceId(), + spanId: generateSpanId(), + spanName: "gh-aw.job.setup", + startMs, + endMs, + serviceName, + attributes, + }); + + await sendOTLPSpan(endpoint, payload); +} + +module.exports = { + generateTraceId, + generateSpanId, + toNanoString, + buildAttr, + buildOTLPPayload, + sendOTLPSpan, + sendJobSetupSpan, +}; diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs new file mode 100644 index 00000000000..84c8549d989 --- /dev/null +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -0,0 +1,280 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +// --------------------------------------------------------------------------- +// Module import +// --------------------------------------------------------------------------- + +const { generateTraceId, generateSpanId, toNanoString, buildAttr, buildOTLPPayload, sendOTLPSpan, sendJobSetupSpan } = await import("./send_otlp_span.cjs"); + +// --------------------------------------------------------------------------- +// generateTraceId +// --------------------------------------------------------------------------- + +describe("generateTraceId", () => { + it("returns a 32-character hex string", () => { + const id = generateTraceId(); + expect(id).toMatch(/^[0-9a-f]{32}$/); + }); + + it("returns a unique value on each call", () => { + expect(generateTraceId()).not.toBe(generateTraceId()); + }); +}); + +// --------------------------------------------------------------------------- +// generateSpanId +// --------------------------------------------------------------------------- + +describe("generateSpanId", () => { + it("returns a 16-character hex string", () => { + const id = generateSpanId(); + expect(id).toMatch(/^[0-9a-f]{16}$/); + }); + + it("returns a unique value on each call", () => { + expect(generateSpanId()).not.toBe(generateSpanId()); + }); +}); + +// --------------------------------------------------------------------------- +// toNanoString +// --------------------------------------------------------------------------- + +describe("toNanoString", () => { + it("converts milliseconds to nanoseconds string", () => { + expect(toNanoString(1000)).toBe("1000000000"); + }); + + it("handles zero", () => { + expect(toNanoString(0)).toBe("0"); + }); + + it("handles a realistic GitHub Actions timestamp without precision loss", () => { + const ms = 1700000000000; // 2023-11-14T22:13:20Z + const nanos = toNanoString(ms); + expect(nanos).toBe("1700000000000000000"); + }); + + it("truncates fractional milliseconds", () => { + // 1500.9 ms should truncate to 1500 + expect(toNanoString(1500.9)).toBe("1500000000"); + }); +}); + +// --------------------------------------------------------------------------- +// buildAttr +// --------------------------------------------------------------------------- + +describe("buildAttr", () => { + it("returns stringValue for string input", () => { + expect(buildAttr("k", "v")).toEqual({ key: "k", value: { stringValue: "v" } }); + }); + + it("returns intValue for number input", () => { + expect(buildAttr("k", 42)).toEqual({ key: "k", value: { intValue: 42 } }); + }); + + it("returns boolValue for boolean input", () => { + expect(buildAttr("k", true)).toEqual({ key: "k", value: { boolValue: true } }); + expect(buildAttr("k", false)).toEqual({ key: "k", value: { boolValue: false } }); + }); + + it("coerces non-string non-number non-boolean to stringValue", () => { + // @ts-expect-error intentional type violation for coverage + expect(buildAttr("k", null).value).toHaveProperty("stringValue"); + }); +}); + +// --------------------------------------------------------------------------- +// buildOTLPPayload +// --------------------------------------------------------------------------- + +describe("buildOTLPPayload", () => { + it("produces a valid OTLP resourceSpans structure", () => { + const traceId = "a".repeat(32); + const spanId = "b".repeat(16); + const payload = buildOTLPPayload({ + traceId, + spanId, + spanName: "gh-aw.job.setup", + startMs: 1000, + endMs: 2000, + serviceName: "gh-aw", + attributes: [buildAttr("foo", "bar")], + }); + + expect(payload.resourceSpans).toHaveLength(1); + const rs = payload.resourceSpans[0]; + + // Resource + expect(rs.resource.attributes).toContainEqual({ key: "service.name", value: { stringValue: "gh-aw" } }); + + // Scope + expect(rs.scopeSpans).toHaveLength(1); + expect(rs.scopeSpans[0].scope.name).toBe("gh-aw.setup"); + + // Span + const span = rs.scopeSpans[0].spans[0]; + expect(span.traceId).toBe(traceId); + expect(span.spanId).toBe(spanId); + expect(span.name).toBe("gh-aw.job.setup"); + expect(span.kind).toBe(2); + expect(span.startTimeUnixNano).toBe(toNanoString(1000)); + expect(span.endTimeUnixNano).toBe(toNanoString(2000)); + expect(span.status.code).toBe(1); + expect(span.attributes).toContainEqual({ key: "foo", value: { stringValue: "bar" } }); + }); +}); + +// --------------------------------------------------------------------------- +// sendOTLPSpan +// --------------------------------------------------------------------------- + +describe("sendOTLPSpan", () => { + beforeEach(() => { + vi.stubGlobal("fetch", vi.fn()); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it("POSTs JSON payload to endpoint/v1/traces", async () => { + const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" }); + vi.stubGlobal("fetch", mockFetch); + + const payload = { resourceSpans: [] }; + await sendOTLPSpan("https://traces.example.com:4317", payload); + + expect(mockFetch).toHaveBeenCalledOnce(); + const [url, init] = mockFetch.mock.calls[0]; + expect(url).toBe("https://traces.example.com:4317/v1/traces"); + expect(init.method).toBe("POST"); + expect(init.headers["Content-Type"]).toBe("application/json"); + expect(JSON.parse(init.body)).toEqual(payload); + }); + + it("strips trailing slash from endpoint before appending /v1/traces", async () => { + const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" }); + vi.stubGlobal("fetch", mockFetch); + + await sendOTLPSpan("https://traces.example.com/", {}); + const [url] = mockFetch.mock.calls[0]; + expect(url).toBe("https://traces.example.com/v1/traces"); + }); + + it("throws when server returns non-2xx status", async () => { + const mockFetch = vi.fn().mockResolvedValue({ ok: false, status: 400, statusText: "Bad Request" }); + vi.stubGlobal("fetch", mockFetch); + + await expect(sendOTLPSpan("https://traces.example.com", {})).rejects.toThrow("OTLP export failed: HTTP 400 Bad Request"); + }); +}); + +// --------------------------------------------------------------------------- +// sendJobSetupSpan +// --------------------------------------------------------------------------- + +describe("sendJobSetupSpan", () => { + const savedEnv = {}; + const envKeys = ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_SERVICE_NAME", "INPUT_JOB_NAME", "GH_AW_INFO_WORKFLOW_NAME", "GH_AW_INFO_ENGINE_ID", "GITHUB_RUN_ID", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]; + + beforeEach(() => { + vi.stubGlobal("fetch", vi.fn()); + for (const k of envKeys) { + savedEnv[k] = process.env[k]; + delete process.env[k]; + } + }); + + afterEach(() => { + vi.unstubAllGlobals(); + for (const k of envKeys) { + if (savedEnv[k] !== undefined) { + process.env[k] = savedEnv[k]; + } else { + delete process.env[k]; + } + } + }); + + it("is a no-op when OTEL_EXPORTER_OTLP_ENDPOINT is not set", async () => { + await sendJobSetupSpan(); + expect(fetch).not.toHaveBeenCalled(); + }); + + it("sends a span when endpoint is configured", 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.INPUT_JOB_NAME = "agent"; + process.env.GH_AW_INFO_WORKFLOW_NAME = "my-workflow"; + process.env.GH_AW_INFO_ENGINE_ID = "copilot"; + process.env.GITHUB_RUN_ID = "123456789"; + process.env.GITHUB_ACTOR = "octocat"; + process.env.GITHUB_REPOSITORY = "owner/repo"; + + await sendJobSetupSpan(); + + expect(mockFetch).toHaveBeenCalledOnce(); + const [url, init] = mockFetch.mock.calls[0]; + expect(url).toBe("https://traces.example.com/v1/traces"); + expect(init.method).toBe("POST"); + + const body = JSON.parse(init.body); + const span = body.resourceSpans[0].scopeSpans[0].spans[0]; + expect(span.name).toBe("gh-aw.job.setup"); + expect(span.traceId).toMatch(/^[0-9a-f]{32}$/); + expect(span.spanId).toMatch(/^[0-9a-f]{16}$/); + + const attrs = Object.fromEntries(span.attributes.map(a => [a.key, a.value.stringValue ?? a.value.intValue ?? a.value.boolValue])); + expect(attrs["gh-aw.job.name"]).toBe("agent"); + expect(attrs["gh-aw.workflow.name"]).toBe("my-workflow"); + expect(attrs["gh-aw.engine.id"]).toBe("copilot"); + expect(attrs["gh-aw.run.id"]).toBe("123456789"); + expect(attrs["gh-aw.run.actor"]).toBe("octocat"); + expect(attrs["gh-aw.repository"]).toBe("owner/repo"); + }); + + it("uses the provided startMs for the span start time", 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"; + const startMs = 1_700_000_000_000; + await sendJobSetupSpan({ startMs }); + + const body = JSON.parse(mockFetch.mock.calls[0][1].body); + const span = body.resourceSpans[0].scopeSpans[0].spans[0]; + expect(span.startTimeUnixNano).toBe(toNanoString(startMs)); + }); + + it("uses OTEL_SERVICE_NAME for the resource service.name attribute", 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.OTEL_SERVICE_NAME = "my-service"; + + await sendJobSetupSpan(); + + const body = JSON.parse(mockFetch.mock.calls[0][1].body); + const resourceAttrs = body.resourceSpans[0].resource.attributes; + expect(resourceAttrs).toContainEqual({ key: "service.name", value: { stringValue: "my-service" } }); + }); + + it("omits gh-aw.engine.id attribute when engine is not set", 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 sendJobSetupSpan(); + + const body = JSON.parse(mockFetch.mock.calls[0][1].body); + const span = body.resourceSpans[0].scopeSpans[0].spans[0]; + const keys = span.attributes.map(a => a.key); + expect(keys).not.toContain("gh-aw.engine.id"); + }); +}); From c890c5389838363b2935446b3062fd7e3e086eb9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 06:11:22 +0000 Subject: [PATCH 04/16] fix: address code review feedback on send_otlp_span.cjs and test file Agent-Logs-Url: https://github.com/github/gh-aw/sessions/5738fc76-45bf-47ab-af6c-8de1dc9ec689 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/index.js | 6 ++++-- actions/setup/js/send_otlp_span.test.cjs | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/actions/setup/index.js b/actions/setup/index.js index bf332757927..eeaab29b59a 100644 --- a/actions/setup/index.js +++ b/actions/setup/index.js @@ -32,8 +32,10 @@ if (result.status !== 0) { } // Send a gh-aw.job.setup span to the OTLP endpoint when configured. -// This is intentionally fire-and-forget with error suppression: trace export -// failures must never break the workflow. +// The IIFE returns a Promise that keeps the Node.js event loop alive until +// the fetch request completes, so the span is delivered before the process +// exits naturally. Errors are swallowed: trace export failures must never +// break the workflow. (async () => { try { const { sendJobSetupSpan } = require(path.join(__dirname, "js", "send_otlp_span.cjs")); diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index 84c8549d989..11b359855bc 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -176,6 +176,7 @@ describe("sendOTLPSpan", () => { // --------------------------------------------------------------------------- describe("sendJobSetupSpan", () => { + /** @type {Record} */ const savedEnv = {}; const envKeys = ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_SERVICE_NAME", "INPUT_JOB_NAME", "GH_AW_INFO_WORKFLOW_NAME", "GH_AW_INFO_ENGINE_ID", "GITHUB_RUN_ID", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]; @@ -198,6 +199,20 @@ describe("sendJobSetupSpan", () => { } }); + /** + * Extract the scalar value from an OTLP attribute's `value` union, covering all + * known OTLP value types (stringValue, intValue, boolValue). + * + * @param {{ key: string, value: { stringValue?: string, intValue?: number, boolValue?: boolean } }} attr + * @returns {string | number | boolean | undefined} + */ + function attrValue(attr) { + if (attr.value.stringValue !== undefined) return attr.value.stringValue; + if (attr.value.intValue !== undefined) return attr.value.intValue; + if (attr.value.boolValue !== undefined) return attr.value.boolValue; + return undefined; + } + it("is a no-op when OTEL_EXPORTER_OTLP_ENDPOINT is not set", async () => { await sendJobSetupSpan(); expect(fetch).not.toHaveBeenCalled(); @@ -228,7 +243,7 @@ describe("sendJobSetupSpan", () => { expect(span.traceId).toMatch(/^[0-9a-f]{32}$/); expect(span.spanId).toMatch(/^[0-9a-f]{16}$/); - const attrs = Object.fromEntries(span.attributes.map(a => [a.key, a.value.stringValue ?? a.value.intValue ?? a.value.boolValue])); + const attrs = Object.fromEntries(span.attributes.map(a => [a.key, attrValue(a)])); expect(attrs["gh-aw.job.name"]).toBe("agent"); expect(attrs["gh-aw.workflow.name"]).toBe("my-workflow"); expect(attrs["gh-aw.engine.id"]).toBe("copilot"); From ffd3965b8c8804c960edb65242f160a01e2b0e27 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 06:50:45 +0000 Subject: [PATCH 05/16] feat: add trace-id input/output to actions/setup for cross-job span correlation Agent-Logs-Url: https://github.com/github/gh-aw/sessions/461b1d13-5dec-4bb7-8daa-d36d583465a4 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/action.yml | 6 +++ actions/setup/index.js | 11 +++- actions/setup/js/send_otlp_span.cjs | 28 ++++++++--- actions/setup/js/send_otlp_span.test.cjs | 64 +++++++++++++++++++++--- 4 files changed, 94 insertions(+), 15 deletions(-) diff --git a/actions/setup/action.yml b/actions/setup/action.yml index f93f57767ab..45cbbab4b89 100644 --- a/actions/setup/action.yml +++ b/actions/setup/action.yml @@ -14,10 +14,16 @@ inputs: description: 'Name of the job being set up. When OTEL_EXPORTER_OTLP_ENDPOINT is configured, a gh-aw.job.setup span is pushed to the OTLP endpoint.' required: false default: '' + trace-id: + description: 'OTLP trace ID to reuse for cross-job span correlation. Pass the trace-id output of the activation job setup step to correlate all job spans under the same trace. When omitted a new trace ID is generated.' + required: false + default: '' outputs: files_copied: description: 'Number of files copied' + trace-id: + description: 'The OTLP trace ID used for the gh-aw.job.setup span. Pass this to subsequent job setup steps via the trace-id input to correlate all job spans under a single trace.' runs: using: 'node24' diff --git a/actions/setup/index.js b/actions/setup/index.js index eeaab29b59a..ea873ecc647 100644 --- a/actions/setup/index.js +++ b/actions/setup/index.js @@ -38,9 +38,16 @@ if (result.status !== 0) { // break the workflow. (async () => { try { + const { appendFileSync } = require("fs"); const { sendJobSetupSpan } = require(path.join(__dirname, "js", "send_otlp_span.cjs")); - await sendJobSetupSpan({ startMs: setupStartMs }); + const traceId = await sendJobSetupSpan({ startMs: setupStartMs }); + // Always expose the trace ID as an action output so downstream jobs can + // reference it via `steps..outputs.trace-id` and pass it to their own + // setup steps to correlate all job spans under a single trace. + if (traceId && process.env.GITHUB_OUTPUT) { + appendFileSync(process.env.GITHUB_OUTPUT, `trace-id=${traceId}\n`); + } } catch { - // Non-fatal: silently ignore any OTLP export errors. + // Non-fatal: silently ignore any OTLP export or output-write errors. } })(); diff --git a/actions/setup/js/send_otlp_span.cjs b/actions/setup/js/send_otlp_span.cjs index 8bc0cff58a2..d9a963b5c1e 100644 --- a/actions/setup/js/send_otlp_span.cjs +++ b/actions/setup/js/send_otlp_span.cjs @@ -147,21 +147,28 @@ async function sendOTLPSpan(endpoint, payload) { /** * @typedef {Object} SendJobSetupSpanOptions - * @property {number} [startMs] - Override for the span start time (ms). Defaults to `Date.now()`. + * @property {number} [startMs] - Override for the span start time (ms). Defaults to `Date.now()`. + * @property {string} [traceId] - Existing trace ID to reuse for cross-job correlation. + * When omitted the value is taken from the `INPUT_TRACE_ID` environment variable (the + * `trace-id` action input); if that is also absent a new random trace ID is generated. + * Pass the `trace-id` output of the activation job's setup step to correlate all + * subsequent job spans under the same trace. */ /** * Send a `gh-aw.job.setup` span to the configured OTLP endpoint. * * This is designed to be called from `actions/setup/index.js` immediately after - * the setup script completes. It is a no-op when `OTEL_EXPORTER_OTLP_ENDPOINT` - * is not set, and errors are swallowed so the workflow is never broken by tracing - * failures. + * the setup script completes. It always returns the trace ID so callers can + * expose it as an action output for cross-job correlation — even when + * `OTEL_EXPORTER_OTLP_ENDPOINT` is not set (no span is sent in that case). + * Errors are swallowed so the workflow is never broken by tracing failures. * * Environment variables consumed: * - `OTEL_EXPORTER_OTLP_ENDPOINT` – collector endpoint (required to send anything) * - `OTEL_SERVICE_NAME` – service name (defaults to "gh-aw") * - `INPUT_JOB_NAME` – job name passed via the `job-name` action input + * - `INPUT_TRACE_ID` – optional trace ID passed via the `trace-id` action input * - `GH_AW_INFO_WORKFLOW_NAME` – workflow name injected by the gh-aw compiler * - `GH_AW_INFO_ENGINE_ID` – engine ID injected by the gh-aw compiler * - `GITHUB_RUN_ID` – GitHub Actions run ID @@ -169,12 +176,18 @@ async function sendOTLPSpan(endpoint, payload) { * - `GITHUB_REPOSITORY` – `owner/repo` string * * @param {SendJobSetupSpanOptions} [options] - * @returns {Promise} + * @returns {Promise} The trace ID used for the span (generated or passed in). */ async function sendJobSetupSpan(options = {}) { + // Resolve the trace ID before the early-return so it is always available as + // an action output regardless of whether OTLP is configured. + // Priority: options.traceId > INPUT_TRACE_ID env var > newly generated ID. + const inputTraceId = (process.env.INPUT_TRACE_ID || "").trim(); + const traceId = options.traceId || inputTraceId || generateTraceId(); + const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || ""; if (!endpoint) { - return; + return traceId; } const startMs = options.startMs ?? Date.now(); @@ -195,7 +208,7 @@ async function sendJobSetupSpan(options = {}) { } const payload = buildOTLPPayload({ - traceId: generateTraceId(), + traceId, spanId: generateSpanId(), spanName: "gh-aw.job.setup", startMs, @@ -205,6 +218,7 @@ async function sendJobSetupSpan(options = {}) { }); await sendOTLPSpan(endpoint, payload); + return traceId; } module.exports = { diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index 11b359855bc..3bdec534a72 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -178,7 +178,7 @@ describe("sendOTLPSpan", () => { describe("sendJobSetupSpan", () => { /** @type {Record} */ const savedEnv = {}; - const envKeys = ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_SERVICE_NAME", "INPUT_JOB_NAME", "GH_AW_INFO_WORKFLOW_NAME", "GH_AW_INFO_ENGINE_ID", "GITHUB_RUN_ID", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]; + const envKeys = ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_SERVICE_NAME", "INPUT_JOB_NAME", "INPUT_TRACE_ID", "GH_AW_INFO_WORKFLOW_NAME", "GH_AW_INFO_ENGINE_ID", "GITHUB_RUN_ID", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]; beforeEach(() => { vi.stubGlobal("fetch", vi.fn()); @@ -213,12 +213,20 @@ describe("sendJobSetupSpan", () => { return undefined; } - it("is a no-op when OTEL_EXPORTER_OTLP_ENDPOINT is not set", async () => { - await sendJobSetupSpan(); + it("returns a trace ID even when OTEL_EXPORTER_OTLP_ENDPOINT is not set", async () => { + const traceId = await sendJobSetupSpan(); + expect(traceId).toMatch(/^[0-9a-f]{32}$/); expect(fetch).not.toHaveBeenCalled(); }); - it("sends a span when endpoint is configured", async () => { + it("returns the same trace ID when called with INPUT_TRACE_ID and no endpoint", async () => { + process.env.INPUT_TRACE_ID = "a".repeat(32); + const traceId = await sendJobSetupSpan(); + expect(traceId).toBe("a".repeat(32)); + expect(fetch).not.toHaveBeenCalled(); + }); + + it("sends a span when endpoint is configured and returns the trace ID", async () => { const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" }); vi.stubGlobal("fetch", mockFetch); @@ -230,8 +238,9 @@ describe("sendJobSetupSpan", () => { process.env.GITHUB_ACTOR = "octocat"; process.env.GITHUB_REPOSITORY = "owner/repo"; - await sendJobSetupSpan(); + const traceId = await sendJobSetupSpan(); + expect(traceId).toMatch(/^[0-9a-f]{32}$/); expect(mockFetch).toHaveBeenCalledOnce(); const [url, init] = mockFetch.mock.calls[0]; expect(url).toBe("https://traces.example.com/v1/traces"); @@ -240,7 +249,8 @@ describe("sendJobSetupSpan", () => { const body = JSON.parse(init.body); const span = body.resourceSpans[0].scopeSpans[0].spans[0]; expect(span.name).toBe("gh-aw.job.setup"); - expect(span.traceId).toMatch(/^[0-9a-f]{32}$/); + // Span traceId must match the returned value (cross-job correlation) + expect(span.traceId).toBe(traceId); expect(span.spanId).toMatch(/^[0-9a-f]{16}$/); const attrs = Object.fromEntries(span.attributes.map(a => [a.key, attrValue(a)])); @@ -252,6 +262,48 @@ describe("sendJobSetupSpan", () => { expect(attrs["gh-aw.repository"]).toBe("owner/repo"); }); + it("uses trace ID from options.traceId for cross-job correlation", 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"; + const correlationTraceId = "b".repeat(32); + + const returned = await sendJobSetupSpan({ traceId: correlationTraceId }); + + expect(returned).toBe(correlationTraceId); + const body = JSON.parse(mockFetch.mock.calls[0][1].body); + expect(body.resourceSpans[0].scopeSpans[0].spans[0].traceId).toBe(correlationTraceId); + }); + + it("uses trace ID from INPUT_TRACE_ID env var when options.traceId 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.INPUT_TRACE_ID = "c".repeat(32); + + const returned = await sendJobSetupSpan(); + + expect(returned).toBe("c".repeat(32)); + const body = JSON.parse(mockFetch.mock.calls[0][1].body); + expect(body.resourceSpans[0].scopeSpans[0].spans[0].traceId).toBe("c".repeat(32)); + }); + + it("options.traceId takes priority over INPUT_TRACE_ID", 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.INPUT_TRACE_ID = "d".repeat(32); + + const returned = await sendJobSetupSpan({ traceId: "e".repeat(32) }); + + expect(returned).toBe("e".repeat(32)); + const body = JSON.parse(mockFetch.mock.calls[0][1].body); + expect(body.resourceSpans[0].scopeSpans[0].spans[0].traceId).toBe("e".repeat(32)); + }); + it("uses the provided startMs for the span start time", async () => { const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" }); vi.stubGlobal("fetch", mockFetch); From 6a3a6b90765ec9d8f409be6662ae207598fda18c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 06:54:06 +0000 Subject: [PATCH 06/16] fix: validate trace-id format, add isValidTraceId helper + tests Agent-Logs-Url: https://github.com/github/gh-aw/sessions/461b1d13-5dec-4bb7-8daa-d36d583465a4 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/action.yml | 2 +- actions/setup/index.js | 2 +- actions/setup/js/send_otlp_span.cjs | 25 ++++++++++++++-- actions/setup/js/send_otlp_span.test.cjs | 37 +++++++++++++++++++++++- 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/actions/setup/action.yml b/actions/setup/action.yml index 45cbbab4b89..8f32cb2503c 100644 --- a/actions/setup/action.yml +++ b/actions/setup/action.yml @@ -15,7 +15,7 @@ inputs: required: false default: '' trace-id: - description: 'OTLP trace ID to reuse for cross-job span correlation. Pass the trace-id output of the activation job setup step to correlate all job spans under the same trace. When omitted a new trace ID is generated.' + description: 'OTLP trace ID (32-character hexadecimal string) to reuse for cross-job span correlation. Pass the trace-id output of the activation job setup step to correlate all job spans under the same trace. When omitted a new trace ID is generated.' required: false default: '' diff --git a/actions/setup/index.js b/actions/setup/index.js index ea873ecc647..4ff788d42e8 100644 --- a/actions/setup/index.js +++ b/actions/setup/index.js @@ -44,7 +44,7 @@ if (result.status !== 0) { // Always expose the trace ID as an action output so downstream jobs can // reference it via `steps..outputs.trace-id` and pass it to their own // setup steps to correlate all job spans under a single trace. - if (traceId && process.env.GITHUB_OUTPUT) { + if (/^[0-9a-f]{32}$/.test(traceId) && process.env.GITHUB_OUTPUT) { appendFileSync(process.env.GITHUB_OUTPUT, `trace-id=${traceId}\n`); } } catch { diff --git a/actions/setup/js/send_otlp_span.cjs b/actions/setup/js/send_otlp_span.cjs index d9a963b5c1e..f2bae3bc2da 100644 --- a/actions/setup/js/send_otlp_span.cjs +++ b/actions/setup/js/send_otlp_span.cjs @@ -145,13 +145,28 @@ async function sendOTLPSpan(endpoint, payload) { // High-level: job setup span // --------------------------------------------------------------------------- +/** + * Regular expression that matches a valid OTLP trace ID: 32 lowercase hex characters. + * @type {RegExp} + */ +const TRACE_ID_RE = /^[0-9a-f]{32}$/; + +/** + * Validate that a string is a well-formed OTLP trace ID (32 lowercase hex chars). + * @param {string} id + * @returns {boolean} + */ +function isValidTraceId(id) { + return TRACE_ID_RE.test(id); +} + /** * @typedef {Object} SendJobSetupSpanOptions * @property {number} [startMs] - Override for the span start time (ms). Defaults to `Date.now()`. * @property {string} [traceId] - Existing trace ID to reuse for cross-job correlation. * When omitted the value is taken from the `INPUT_TRACE_ID` environment variable (the * `trace-id` action input); if that is also absent a new random trace ID is generated. - * Pass the `trace-id` output of the activation job's setup step to correlate all + * Pass the `trace-id` output of the activation job setup step to correlate all * subsequent job spans under the same trace. */ @@ -182,8 +197,11 @@ async function sendJobSetupSpan(options = {}) { // Resolve the trace ID before the early-return so it is always available as // an action output regardless of whether OTLP is configured. // Priority: options.traceId > INPUT_TRACE_ID env var > newly generated ID. - const inputTraceId = (process.env.INPUT_TRACE_ID || "").trim(); - const traceId = options.traceId || inputTraceId || generateTraceId(); + // Invalid (non-hex, wrong-length) values are silently discarded in favour of a new ID. + const rawInputTraceId = (process.env.INPUT_TRACE_ID || "").trim().toLowerCase(); + const inputTraceId = isValidTraceId(rawInputTraceId) ? rawInputTraceId : ""; + const candidateTraceId = options.traceId || inputTraceId; + const traceId = candidateTraceId && isValidTraceId(candidateTraceId) ? candidateTraceId : generateTraceId(); const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || ""; if (!endpoint) { @@ -222,6 +240,7 @@ async function sendJobSetupSpan(options = {}) { } module.exports = { + isValidTraceId, generateTraceId, generateSpanId, toNanoString, diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index 3bdec534a72..a277954a45b 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -4,7 +4,35 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; // Module import // --------------------------------------------------------------------------- -const { generateTraceId, generateSpanId, toNanoString, buildAttr, buildOTLPPayload, sendOTLPSpan, sendJobSetupSpan } = await import("./send_otlp_span.cjs"); +const { isValidTraceId, generateTraceId, generateSpanId, toNanoString, buildAttr, buildOTLPPayload, sendOTLPSpan, sendJobSetupSpan } = await import("./send_otlp_span.cjs"); + +// --------------------------------------------------------------------------- +// isValidTraceId +// --------------------------------------------------------------------------- + +describe("isValidTraceId", () => { + it("accepts a valid 32-character lowercase hex trace ID", () => { + expect(isValidTraceId("a".repeat(32))).toBe(true); + expect(isValidTraceId("0123456789abcdef0123456789abcdef")).toBe(true); + }); + + it("rejects uppercase hex characters", () => { + expect(isValidTraceId("A".repeat(32))).toBe(false); + }); + + it("rejects strings that are too short or too long", () => { + expect(isValidTraceId("a".repeat(31))).toBe(false); + expect(isValidTraceId("a".repeat(33))).toBe(false); + }); + + it("rejects empty string", () => { + expect(isValidTraceId("")).toBe(false); + }); + + it("rejects non-hex characters", () => { + expect(isValidTraceId("z".repeat(32))).toBe(false); + }); +}); // --------------------------------------------------------------------------- // generateTraceId @@ -226,6 +254,13 @@ describe("sendJobSetupSpan", () => { expect(fetch).not.toHaveBeenCalled(); }); + it("generates a new trace ID when INPUT_TRACE_ID is invalid", async () => { + process.env.INPUT_TRACE_ID = "not-a-valid-trace-id"; + const traceId = await sendJobSetupSpan(); + expect(traceId).toMatch(/^[0-9a-f]{32}$/); + expect(traceId).not.toBe("not-a-valid-trace-id"); + }); + it("sends a span when endpoint is configured and returns the trace ID", async () => { const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" }); vi.stubGlobal("fetch", mockFetch); From 4a405219f2dcb420ef27b6e9bcfabf6cd0e0f085 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 06:56:21 +0000 Subject: [PATCH 07/16] fix: validate options.traceId, normalize uppercase input, reuse isValidTraceId in index.js Agent-Logs-Url: https://github.com/github/gh-aw/sessions/461b1d13-5dec-4bb7-8daa-d36d583465a4 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/index.js | 4 ++-- actions/setup/js/send_otlp_span.cjs | 12 +++++++++--- actions/setup/js/send_otlp_span.test.cjs | 13 +++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/actions/setup/index.js b/actions/setup/index.js index 4ff788d42e8..e71b8f47aff 100644 --- a/actions/setup/index.js +++ b/actions/setup/index.js @@ -39,12 +39,12 @@ if (result.status !== 0) { (async () => { try { const { appendFileSync } = require("fs"); - const { sendJobSetupSpan } = require(path.join(__dirname, "js", "send_otlp_span.cjs")); + const { isValidTraceId, sendJobSetupSpan } = require(path.join(__dirname, "js", "send_otlp_span.cjs")); const traceId = await sendJobSetupSpan({ startMs: setupStartMs }); // Always expose the trace ID as an action output so downstream jobs can // reference it via `steps..outputs.trace-id` and pass it to their own // setup steps to correlate all job spans under a single trace. - if (/^[0-9a-f]{32}$/.test(traceId) && process.env.GITHUB_OUTPUT) { + if (isValidTraceId(traceId) && process.env.GITHUB_OUTPUT) { appendFileSync(process.env.GITHUB_OUTPUT, `trace-id=${traceId}\n`); } } catch { diff --git a/actions/setup/js/send_otlp_span.cjs b/actions/setup/js/send_otlp_span.cjs index f2bae3bc2da..1a75a9eb202 100644 --- a/actions/setup/js/send_otlp_span.cjs +++ b/actions/setup/js/send_otlp_span.cjs @@ -197,11 +197,17 @@ async function sendJobSetupSpan(options = {}) { // Resolve the trace ID before the early-return so it is always available as // an action output regardless of whether OTLP is configured. // Priority: options.traceId > INPUT_TRACE_ID env var > newly generated ID. - // Invalid (non-hex, wrong-length) values are silently discarded in favour of a new ID. + // Invalid (wrong length, non-hex) values are silently discarded. + + // Validate options.traceId if supplied; callers may pass raw user input. + const optionsTraceId = options.traceId && isValidTraceId(options.traceId) ? options.traceId : ""; + + // Normalise INPUT_TRACE_ID to lowercase before validating: OTLP requires lowercase + // hex, but trace IDs pasted from external tools may use uppercase characters. const rawInputTraceId = (process.env.INPUT_TRACE_ID || "").trim().toLowerCase(); const inputTraceId = isValidTraceId(rawInputTraceId) ? rawInputTraceId : ""; - const candidateTraceId = options.traceId || inputTraceId; - const traceId = candidateTraceId && isValidTraceId(candidateTraceId) ? candidateTraceId : generateTraceId(); + + const traceId = optionsTraceId || inputTraceId || generateTraceId(); const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || ""; if (!endpoint) { diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index a277954a45b..7853b1f653a 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -261,6 +261,19 @@ describe("sendJobSetupSpan", () => { expect(traceId).not.toBe("not-a-valid-trace-id"); }); + it("normalises uppercase INPUT_TRACE_ID to lowercase and accepts it", async () => { + // Trace IDs pasted from external tools may be uppercase; we normalise them. + process.env.INPUT_TRACE_ID = "A".repeat(32); + const traceId = await sendJobSetupSpan(); + expect(traceId).toBe("a".repeat(32)); + }); + + it("rejects an invalid options.traceId and generates a new trace ID", async () => { + const returned = await sendJobSetupSpan({ traceId: "too-short" }); + expect(returned).toMatch(/^[0-9a-f]{32}$/); + expect(returned).not.toBe("too-short"); + }); + it("sends a span when endpoint is configured and returns the trace ID", async () => { const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" }); vi.stubGlobal("fetch", mockFetch); From 965d5d7414ba4dfe4cb9406b2e77019274f326db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 07:28:01 +0000 Subject: [PATCH 08/16] feat: scope name gh-aw + version, retry/warn sendOTLPSpan, conclusion spans for safe-outputs/conclusion jobs" Agent-Logs-Url: https://github.com/github/gh-aw/sessions/2a7e539d-2a7e-452d-8b26-8de15a7ebabe Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../agent-performance-analyzer.lock.yml | 16 ++ .../workflows/agent-persona-explorer.lock.yml | 16 ++ .../agentic-observability-kit.lock.yml | 16 ++ .github/workflows/ai-moderator.lock.yml | 16 ++ .github/workflows/archie.lock.yml | 16 ++ .github/workflows/artifacts-summary.lock.yml | 16 ++ .github/workflows/audit-workflows.lock.yml | 16 ++ .github/workflows/auto-triage-issues.lock.yml | 16 ++ .github/workflows/blog-auditor.lock.yml | 16 ++ .github/workflows/bot-detection.lock.yml | 16 ++ .github/workflows/brave.lock.yml | 16 ++ .../breaking-change-checker.lock.yml | 16 ++ .github/workflows/changeset.lock.yml | 16 ++ .github/workflows/ci-coach.lock.yml | 16 ++ .github/workflows/ci-doctor.lock.yml | 16 ++ .../claude-code-user-docs-review.lock.yml | 16 ++ .../workflows/claude-token-optimizer.lock.yml | 16 ++ .../claude-token-usage-analyzer.lock.yml | 16 ++ .../cli-consistency-checker.lock.yml | 16 ++ .../workflows/cli-version-checker.lock.yml | 16 ++ .github/workflows/cloclo.lock.yml | 16 ++ .../workflows/code-scanning-fixer.lock.yml | 16 ++ .github/workflows/code-simplifier.lock.yml | 16 ++ .../commit-changes-analyzer.lock.yml | 16 ++ .../constraint-solving-potd.lock.yml | 16 ++ .github/workflows/contribution-check.lock.yml | 16 ++ .../workflows/copilot-agent-analysis.lock.yml | 16 ++ .../copilot-cli-deep-research.lock.yml | 16 ++ .../copilot-pr-merged-report.lock.yml | 16 ++ .../copilot-pr-nlp-analysis.lock.yml | 16 ++ .../copilot-pr-prompt-analysis.lock.yml | 16 ++ .../copilot-session-insights.lock.yml | 16 ++ .../copilot-token-optimizer.lock.yml | 16 ++ .../copilot-token-usage-analyzer.lock.yml | 16 ++ .github/workflows/craft.lock.yml | 16 ++ .../daily-architecture-diagram.lock.yml | 16 ++ .../daily-assign-issue-to-user.lock.yml | 16 ++ .github/workflows/daily-choice-test.lock.yml | 16 ++ .../workflows/daily-cli-performance.lock.yml | 16 ++ .../workflows/daily-cli-tools-tester.lock.yml | 16 ++ .github/workflows/daily-code-metrics.lock.yml | 16 ++ .../daily-community-attribution.lock.yml | 16 ++ .../workflows/daily-compiler-quality.lock.yml | 16 ++ .../daily-copilot-token-report.lock.yml | 16 ++ .github/workflows/daily-doc-healer.lock.yml | 16 ++ .github/workflows/daily-doc-updater.lock.yml | 16 ++ .github/workflows/daily-fact.lock.yml | 16 ++ .github/workflows/daily-file-diet.lock.yml | 16 ++ .../workflows/daily-firewall-report.lock.yml | 16 ++ .../workflows/daily-function-namer.lock.yml | 16 ++ .../daily-integrity-analysis.lock.yml | 16 ++ .../workflows/daily-issues-report.lock.yml | 16 ++ .../daily-malicious-code-scan.lock.yml | 16 ++ .../daily-mcp-concurrency-analysis.lock.yml | 16 ++ .../daily-multi-device-docs-tester.lock.yml | 16 ++ .github/workflows/daily-news.lock.yml | 16 ++ .../daily-observability-report.lock.yml | 16 ++ .../daily-performance-summary.lock.yml | 16 ++ .github/workflows/daily-regulatory.lock.yml | 16 ++ .../daily-rendering-scripts-verifier.lock.yml | 16 ++ .../workflows/daily-repo-chronicle.lock.yml | 16 ++ .../daily-safe-output-integrator.lock.yml | 16 ++ .../daily-safe-output-optimizer.lock.yml | 16 ++ .../daily-safe-outputs-conformance.lock.yml | 16 ++ .../workflows/daily-secrets-analysis.lock.yml | 16 ++ .../daily-security-red-team.lock.yml | 16 ++ .github/workflows/daily-semgrep-scan.lock.yml | 16 ++ .../daily-syntax-error-quality.lock.yml | 16 ++ .../daily-team-evolution-insights.lock.yml | 16 ++ .github/workflows/daily-team-status.lock.yml | 16 ++ .../daily-testify-uber-super-expert.lock.yml | 16 ++ .../workflows/daily-workflow-updater.lock.yml | 16 ++ .github/workflows/dead-code-remover.lock.yml | 16 ++ .github/workflows/deep-report.lock.yml | 16 ++ .github/workflows/delight.lock.yml | 16 ++ .github/workflows/dependabot-burner.lock.yml | 16 ++ .../workflows/dependabot-go-checker.lock.yml | 16 ++ .github/workflows/dev-hawk.lock.yml | 16 ++ .github/workflows/dev.lock.yml | 16 ++ .../developer-docs-consolidator.lock.yml | 16 ++ .github/workflows/dictation-prompt.lock.yml | 16 ++ .../workflows/discussion-task-miner.lock.yml | 16 ++ .github/workflows/docs-noob-tester.lock.yml | 16 ++ .github/workflows/draft-pr-cleanup.lock.yml | 16 ++ .../duplicate-code-detector.lock.yml | 16 ++ .../example-workflow-analyzer.lock.yml | 16 ++ .github/workflows/firewall-escape.lock.yml | 16 ++ .../workflows/functional-pragmatist.lock.yml | 16 ++ .../github-mcp-structural-analysis.lock.yml | 16 ++ .../github-mcp-tools-report.lock.yml | 16 ++ .../github-remote-mcp-auth-test.lock.yml | 16 ++ .../workflows/glossary-maintainer.lock.yml | 16 ++ .github/workflows/go-fan.lock.yml | 16 ++ .github/workflows/go-logger.lock.yml | 16 ++ .../workflows/go-pattern-detector.lock.yml | 16 ++ .github/workflows/gpclean.lock.yml | 16 ++ .github/workflows/grumpy-reviewer.lock.yml | 16 ++ .github/workflows/hourly-ci-cleaner.lock.yml | 16 ++ .../workflows/instructions-janitor.lock.yml | 16 ++ .github/workflows/issue-arborist.lock.yml | 16 ++ .github/workflows/issue-monster.lock.yml | 16 ++ .github/workflows/issue-triage-agent.lock.yml | 16 ++ .github/workflows/jsweep.lock.yml | 16 ++ .../workflows/layout-spec-maintainer.lock.yml | 16 ++ .github/workflows/lockfile-stats.lock.yml | 16 ++ .github/workflows/mcp-inspector.lock.yml | 16 ++ .github/workflows/mergefest.lock.yml | 16 ++ .../workflows/notion-issue-summary.lock.yml | 16 ++ .github/workflows/org-health-report.lock.yml | 16 ++ .github/workflows/pdf-summary.lock.yml | 16 ++ .github/workflows/plan.lock.yml | 16 ++ .github/workflows/poem-bot.lock.yml | 16 ++ .github/workflows/portfolio-analyst.lock.yml | 16 ++ .../workflows/pr-nitpick-reviewer.lock.yml | 16 ++ .github/workflows/pr-triage-agent.lock.yml | 16 ++ .../prompt-clustering-analysis.lock.yml | 16 ++ .github/workflows/python-data-charts.lock.yml | 16 ++ .github/workflows/q.lock.yml | 16 ++ .github/workflows/refiner.lock.yml | 16 ++ .github/workflows/release.lock.yml | 16 ++ .../workflows/repo-audit-analyzer.lock.yml | 16 ++ .github/workflows/repo-tree-map.lock.yml | 16 ++ .../repository-quality-improver.lock.yml | 16 ++ .github/workflows/research.lock.yml | 16 ++ .github/workflows/safe-output-health.lock.yml | 16 ++ .../schema-consistency-checker.lock.yml | 16 ++ .../schema-feature-coverage.lock.yml | 16 ++ .github/workflows/scout.lock.yml | 16 ++ .../workflows/security-compliance.lock.yml | 16 ++ .github/workflows/security-review.lock.yml | 16 ++ .../semantic-function-refactor.lock.yml | 16 ++ .github/workflows/sergo.lock.yml | 16 ++ .../workflows/slide-deck-maintainer.lock.yml | 16 ++ .../workflows/smoke-agent-all-merged.lock.yml | 16 ++ .../workflows/smoke-agent-all-none.lock.yml | 16 ++ .../smoke-agent-public-approved.lock.yml | 16 ++ .../smoke-agent-public-none.lock.yml | 16 ++ .../smoke-agent-scoped-approved.lock.yml | 16 ++ .../workflows/smoke-call-workflow.lock.yml | 16 ++ .github/workflows/smoke-claude.lock.yml | 16 ++ .github/workflows/smoke-codex.lock.yml | 16 ++ .github/workflows/smoke-copilot-arm.lock.yml | 16 ++ .github/workflows/smoke-copilot.lock.yml | 16 ++ .../smoke-create-cross-repo-pr.lock.yml | 16 ++ .github/workflows/smoke-gemini.lock.yml | 16 ++ .github/workflows/smoke-multi-pr.lock.yml | 16 ++ .github/workflows/smoke-project.lock.yml | 16 ++ .../workflows/smoke-service-ports.lock.yml | 16 ++ .github/workflows/smoke-temporary-id.lock.yml | 16 ++ .github/workflows/smoke-test-tools.lock.yml | 16 ++ .../smoke-update-cross-repo-pr.lock.yml | 16 ++ .../smoke-workflow-call-with-inputs.lock.yml | 16 ++ .../workflows/smoke-workflow-call.lock.yml | 16 ++ .../workflows/stale-repo-identifier.lock.yml | 16 ++ .../workflows/static-analysis-report.lock.yml | 16 ++ .../workflows/step-name-alignment.lock.yml | 16 ++ .github/workflows/sub-issue-closer.lock.yml | 16 ++ .github/workflows/super-linter.lock.yml | 16 ++ .../workflows/technical-doc-writer.lock.yml | 16 ++ .github/workflows/terminal-stylist.lock.yml | 16 ++ .../test-create-pr-error-handling.lock.yml | 16 ++ .github/workflows/test-dispatcher.lock.yml | 16 ++ .../test-project-url-default.lock.yml | 16 ++ .github/workflows/tidy.lock.yml | 16 ++ .github/workflows/token-logs-fetch.lock.yml | 16 ++ .github/workflows/typist.lock.yml | 16 ++ .../workflows/ubuntu-image-analyzer.lock.yml | 16 ++ .github/workflows/unbloat-docs.lock.yml | 16 ++ .github/workflows/update-astro.lock.yml | 16 ++ .github/workflows/video-analyzer.lock.yml | 16 ++ .../weekly-blog-post-writer.lock.yml | 16 ++ .../weekly-editors-health-check.lock.yml | 16 ++ .../workflows/weekly-issue-summary.lock.yml | 16 ++ .../weekly-safe-outputs-spec-review.lock.yml | 16 ++ .github/workflows/workflow-generator.lock.yml | 16 ++ .../workflow-health-manager.lock.yml | 16 ++ .../workflows/workflow-normalizer.lock.yml | 16 ++ .../workflow-skill-extractor.lock.yml | 16 ++ .../js/generate_observability_summary.cjs | 9 + actions/setup/js/send_otlp_span.cjs | 169 +++++++++++++++--- actions/setup/js/send_otlp_span.test.cjs | 158 +++++++++++++++- pkg/workflow/compiler_safe_outputs_job.go | 3 + pkg/workflow/notify_comment.go | 3 + pkg/workflow/observability_otlp.go | 22 ++- pkg/workflow/observability_otlp_test.go | 19 ++ 185 files changed, 3205 insertions(+), 26 deletions(-) diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml index c657032a00b..f3cc273fb66 100644 --- a/.github/workflows/agent-performance-analyzer.lock.yml +++ b/.github/workflows/agent-performance-analyzer.lock.yml @@ -1017,6 +1017,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1377,4 +1385,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/agent-persona-explorer.lock.yml b/.github/workflows/agent-persona-explorer.lock.yml index b9c46d1a1b1..4524c7b3239 100644 --- a/.github/workflows/agent-persona-explorer.lock.yml +++ b/.github/workflows/agent-persona-explorer.lock.yml @@ -965,6 +965,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1233,6 +1241,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/agentic-observability-kit.lock.yml b/.github/workflows/agentic-observability-kit.lock.yml index c0b18581084..90fca0a1496 100644 --- a/.github/workflows/agentic-observability-kit.lock.yml +++ b/.github/workflows/agentic-observability-kit.lock.yml @@ -966,6 +966,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1208,4 +1216,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/ai-moderator.lock.yml b/.github/workflows/ai-moderator.lock.yml index e98e1ab042b..19f174d1568 100644 --- a/.github/workflows/ai-moderator.lock.yml +++ b/.github/workflows/ai-moderator.lock.yml @@ -932,6 +932,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); pre_activation: runs-on: ubuntu-slim @@ -1079,6 +1087,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); unlock: needs: diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml index d51da953a75..2433183d044 100644 --- a/.github/workflows/archie.lock.yml +++ b/.github/workflows/archie.lock.yml @@ -986,6 +986,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1272,4 +1280,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index 7bf4a704e2c..6fbed2d7433 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -846,6 +846,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1084,4 +1092,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 215b62300d0..6a710524a1d 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -1122,6 +1122,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1459,6 +1467,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/auto-triage-issues.lock.yml b/.github/workflows/auto-triage-issues.lock.yml index 69cd2fe3b02..427ca520781 100644 --- a/.github/workflows/auto-triage-issues.lock.yml +++ b/.github/workflows/auto-triage-issues.lock.yml @@ -901,6 +901,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1188,4 +1196,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml index ae19ddfe702..94dcc39c499 100644 --- a/.github/workflows/blog-auditor.lock.yml +++ b/.github/workflows/blog-auditor.lock.yml @@ -981,6 +981,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1233,4 +1241,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/bot-detection.lock.yml b/.github/workflows/bot-detection.lock.yml index 24613322133..e055a5b4532 100644 --- a/.github/workflows/bot-detection.lock.yml +++ b/.github/workflows/bot-detection.lock.yml @@ -925,6 +925,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); precompute: runs-on: ubuntu-latest @@ -1815,4 +1823,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index ad4e493d55c..47e7ea25c0b 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -932,6 +932,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1218,4 +1226,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/breaking-change-checker.lock.yml b/.github/workflows/breaking-change-checker.lock.yml index 20be33672f2..5423b80fca7 100644 --- a/.github/workflows/breaking-change-checker.lock.yml +++ b/.github/workflows/breaking-change-checker.lock.yml @@ -883,6 +883,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1186,4 +1194,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index 3b9f3c36c24..56c206d7506 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -947,6 +947,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); pre_activation: if: > @@ -1097,6 +1105,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/ci-coach.lock.yml b/.github/workflows/ci-coach.lock.yml index 0b522b09a09..20b34afff6c 100644 --- a/.github/workflows/ci-coach.lock.yml +++ b/.github/workflows/ci-coach.lock.yml @@ -945,6 +945,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1216,6 +1224,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index f419cafd0a0..eb1c0ed6a9e 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -1105,6 +1105,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1380,6 +1388,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/claude-code-user-docs-review.lock.yml b/.github/workflows/claude-code-user-docs-review.lock.yml index ca6bb900713..aaa01219d3c 100644 --- a/.github/workflows/claude-code-user-docs-review.lock.yml +++ b/.github/workflows/claude-code-user-docs-review.lock.yml @@ -952,6 +952,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1205,6 +1213,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/claude-token-optimizer.lock.yml b/.github/workflows/claude-token-optimizer.lock.yml index 19003b55602..62b15765f5e 100644 --- a/.github/workflows/claude-token-optimizer.lock.yml +++ b/.github/workflows/claude-token-optimizer.lock.yml @@ -888,6 +888,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1172,4 +1180,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/claude-token-usage-analyzer.lock.yml b/.github/workflows/claude-token-usage-analyzer.lock.yml index d475ff952c1..b01d9d1efcf 100644 --- a/.github/workflows/claude-token-usage-analyzer.lock.yml +++ b/.github/workflows/claude-token-usage-analyzer.lock.yml @@ -871,6 +871,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1110,4 +1118,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml index 26b8ee0ccc2..900433d7f22 100644 --- a/.github/workflows/cli-consistency-checker.lock.yml +++ b/.github/workflows/cli-consistency-checker.lock.yml @@ -846,6 +846,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1085,4 +1093,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index b1ae0f0a1ca..808c741ffab 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -955,6 +955,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1207,6 +1215,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index 2cb5eb38776..4c849b7b82d 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -1316,6 +1316,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1647,6 +1655,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml index 4bed62d0c7e..d4bf8895e60 100644 --- a/.github/workflows/code-scanning-fixer.lock.yml +++ b/.github/workflows/code-scanning-fixer.lock.yml @@ -940,6 +940,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1340,6 +1348,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/code-simplifier.lock.yml b/.github/workflows/code-simplifier.lock.yml index 89083e05b14..2eb7f331904 100644 --- a/.github/workflows/code-simplifier.lock.yml +++ b/.github/workflows/code-simplifier.lock.yml @@ -875,6 +875,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1189,6 +1197,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml index e9fe2d85fdb..aa8c84bac2d 100644 --- a/.github/workflows/commit-changes-analyzer.lock.yml +++ b/.github/workflows/commit-changes-analyzer.lock.yml @@ -911,6 +911,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1162,4 +1170,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/constraint-solving-potd.lock.yml b/.github/workflows/constraint-solving-potd.lock.yml index 7b653c74978..a5d520b43b4 100644 --- a/.github/workflows/constraint-solving-potd.lock.yml +++ b/.github/workflows/constraint-solving-potd.lock.yml @@ -862,6 +862,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1098,6 +1106,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml index 25700435c94..833b0a29813 100644 --- a/.github/workflows/contribution-check.lock.yml +++ b/.github/workflows/contribution-check.lock.yml @@ -897,6 +897,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1138,4 +1146,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index 3137a1d86ed..efad9475ab4 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -1002,6 +1002,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1338,6 +1346,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/copilot-cli-deep-research.lock.yml b/.github/workflows/copilot-cli-deep-research.lock.yml index dc3fc7a79b7..d9369c1c647 100644 --- a/.github/workflows/copilot-cli-deep-research.lock.yml +++ b/.github/workflows/copilot-cli-deep-research.lock.yml @@ -909,6 +909,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1232,4 +1240,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml index ee0ed960fc8..41404df4802 100644 --- a/.github/workflows/copilot-pr-merged-report.lock.yml +++ b/.github/workflows/copilot-pr-merged-report.lock.yml @@ -1032,6 +1032,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1270,6 +1278,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml index 8b1594e11fe..899bf51b21d 100644 --- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml +++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml @@ -1003,6 +1003,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1326,6 +1334,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml index 68c68005e6c..5ea6416f99b 100644 --- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml +++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml @@ -938,6 +938,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1261,6 +1269,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index 4eb630f78e1..651178ed553 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -1065,6 +1065,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1401,6 +1409,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/copilot-token-optimizer.lock.yml b/.github/workflows/copilot-token-optimizer.lock.yml index 9270436f58d..726a553ad92 100644 --- a/.github/workflows/copilot-token-optimizer.lock.yml +++ b/.github/workflows/copilot-token-optimizer.lock.yml @@ -888,6 +888,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1172,4 +1180,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/copilot-token-usage-analyzer.lock.yml b/.github/workflows/copilot-token-usage-analyzer.lock.yml index ee4a6f962b5..24b56069802 100644 --- a/.github/workflows/copilot-token-usage-analyzer.lock.yml +++ b/.github/workflows/copilot-token-usage-analyzer.lock.yml @@ -965,6 +965,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1204,6 +1212,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml index 2b76b67fad1..a51881ddfb7 100644 --- a/.github/workflows/craft.lock.yml +++ b/.github/workflows/craft.lock.yml @@ -938,6 +938,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1256,6 +1264,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/daily-architecture-diagram.lock.yml b/.github/workflows/daily-architecture-diagram.lock.yml index 9209b95832c..c9e4a6ae662 100644 --- a/.github/workflows/daily-architecture-diagram.lock.yml +++ b/.github/workflows/daily-architecture-diagram.lock.yml @@ -925,6 +925,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1197,6 +1205,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/daily-assign-issue-to-user.lock.yml b/.github/workflows/daily-assign-issue-to-user.lock.yml index c6b127436df..62d058bd92b 100644 --- a/.github/workflows/daily-assign-issue-to-user.lock.yml +++ b/.github/workflows/daily-assign-issue-to-user.lock.yml @@ -851,6 +851,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1093,4 +1101,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-choice-test.lock.yml b/.github/workflows/daily-choice-test.lock.yml index 9657b6c3f5a..ed78f7cfab0 100644 --- a/.github/workflows/daily-choice-test.lock.yml +++ b/.github/workflows/daily-choice-test.lock.yml @@ -904,6 +904,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1149,6 +1157,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); test_environment: name: Test Environment Deployment diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml index ac74bd834ed..9d75d004532 100644 --- a/.github/workflows/daily-cli-performance.lock.yml +++ b/.github/workflows/daily-cli-performance.lock.yml @@ -1098,6 +1098,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1481,4 +1489,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-cli-tools-tester.lock.yml b/.github/workflows/daily-cli-tools-tester.lock.yml index c1b1f7a9f4c..eb573dbf92a 100644 --- a/.github/workflows/daily-cli-tools-tester.lock.yml +++ b/.github/workflows/daily-cli-tools-tester.lock.yml @@ -932,6 +932,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1169,4 +1177,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index 19d4f55fb15..008958eb20a 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -1044,6 +1044,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1381,6 +1389,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-community-attribution.lock.yml b/.github/workflows/daily-community-attribution.lock.yml index 1c404160b1e..2f625f95462 100644 --- a/.github/workflows/daily-community-attribution.lock.yml +++ b/.github/workflows/daily-community-attribution.lock.yml @@ -942,6 +942,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1295,6 +1303,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/daily-compiler-quality.lock.yml b/.github/workflows/daily-compiler-quality.lock.yml index 2fea563fb80..bb539e064b3 100644 --- a/.github/workflows/daily-compiler-quality.lock.yml +++ b/.github/workflows/daily-compiler-quality.lock.yml @@ -978,6 +978,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1217,6 +1225,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-copilot-token-report.lock.yml b/.github/workflows/daily-copilot-token-report.lock.yml index 028fcafa8c2..652033ecabe 100644 --- a/.github/workflows/daily-copilot-token-report.lock.yml +++ b/.github/workflows/daily-copilot-token-report.lock.yml @@ -1025,6 +1025,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1349,6 +1357,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-doc-healer.lock.yml b/.github/workflows/daily-doc-healer.lock.yml index 5d24f397d2e..65d78f8cfde 100644 --- a/.github/workflows/daily-doc-healer.lock.yml +++ b/.github/workflows/daily-doc-healer.lock.yml @@ -1098,6 +1098,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1470,6 +1478,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index f612d5c3783..db00540ac58 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -1064,6 +1064,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1429,6 +1437,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/daily-fact.lock.yml b/.github/workflows/daily-fact.lock.yml index 37c6734bade..2205e84c9d0 100644 --- a/.github/workflows/daily-fact.lock.yml +++ b/.github/workflows/daily-fact.lock.yml @@ -927,6 +927,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1166,4 +1174,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml index f423f33a98a..6b48fa0cce7 100644 --- a/.github/workflows/daily-file-diet.lock.yml +++ b/.github/workflows/daily-file-diet.lock.yml @@ -952,6 +952,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1237,4 +1245,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml index eee10df5905..54694a9b3e6 100644 --- a/.github/workflows/daily-firewall-report.lock.yml +++ b/.github/workflows/daily-firewall-report.lock.yml @@ -1021,6 +1021,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1258,6 +1266,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-function-namer.lock.yml b/.github/workflows/daily-function-namer.lock.yml index 6be76bf1ac8..a3869bf440d 100644 --- a/.github/workflows/daily-function-namer.lock.yml +++ b/.github/workflows/daily-function-namer.lock.yml @@ -1013,6 +1013,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1266,6 +1274,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-integrity-analysis.lock.yml b/.github/workflows/daily-integrity-analysis.lock.yml index a0877808bbc..58f110489d9 100644 --- a/.github/workflows/daily-integrity-analysis.lock.yml +++ b/.github/workflows/daily-integrity-analysis.lock.yml @@ -1038,6 +1038,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1275,6 +1283,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index abd6a1e2e21..205631b1774 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -1006,6 +1006,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1273,6 +1281,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-malicious-code-scan.lock.yml b/.github/workflows/daily-malicious-code-scan.lock.yml index 5c244794b3c..86d03af7a01 100644 --- a/.github/workflows/daily-malicious-code-scan.lock.yml +++ b/.github/workflows/daily-malicious-code-scan.lock.yml @@ -856,6 +856,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); safe_outputs: needs: agent @@ -947,6 +955,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); upload_code_scanning_sarif: needs: safe_outputs diff --git a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml index e41239b200c..6e908d8a9c7 100644 --- a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml +++ b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml @@ -991,6 +991,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1245,6 +1253,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml index 2e4043d87c4..b4058fb33cb 100644 --- a/.github/workflows/daily-multi-device-docs-tester.lock.yml +++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml @@ -1029,6 +1029,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1282,6 +1290,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); upload_assets: needs: agent diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index b2875b1b455..7e68f4d8dc5 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -1079,6 +1079,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1403,6 +1411,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-observability-report.lock.yml b/.github/workflows/daily-observability-report.lock.yml index 8b15f2b7cfe..4794cf89c83 100644 --- a/.github/workflows/daily-observability-report.lock.yml +++ b/.github/workflows/daily-observability-report.lock.yml @@ -961,6 +961,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1228,4 +1236,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml index 277e605dcb1..183bbae6982 100644 --- a/.github/workflows/daily-performance-summary.lock.yml +++ b/.github/workflows/daily-performance-summary.lock.yml @@ -1433,6 +1433,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1670,6 +1678,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-regulatory.lock.yml b/.github/workflows/daily-regulatory.lock.yml index 8cc3f64fda0..9d560ec1d2d 100644 --- a/.github/workflows/daily-regulatory.lock.yml +++ b/.github/workflows/daily-regulatory.lock.yml @@ -1344,6 +1344,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1581,4 +1589,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-rendering-scripts-verifier.lock.yml b/.github/workflows/daily-rendering-scripts-verifier.lock.yml index d3013b9ea7f..e2043736d0a 100644 --- a/.github/workflows/daily-rendering-scripts-verifier.lock.yml +++ b/.github/workflows/daily-rendering-scripts-verifier.lock.yml @@ -1073,6 +1073,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1402,6 +1410,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml index 0f92d00f5ee..74fe23ed907 100644 --- a/.github/workflows/daily-repo-chronicle.lock.yml +++ b/.github/workflows/daily-repo-chronicle.lock.yml @@ -942,6 +942,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1181,6 +1189,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-safe-output-integrator.lock.yml b/.github/workflows/daily-safe-output-integrator.lock.yml index 6b5a2708c41..d3bd01a5959 100644 --- a/.github/workflows/daily-safe-output-integrator.lock.yml +++ b/.github/workflows/daily-safe-output-integrator.lock.yml @@ -896,6 +896,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1167,6 +1175,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/daily-safe-output-optimizer.lock.yml b/.github/workflows/daily-safe-output-optimizer.lock.yml index b7fc7c31239..ea3d6aa269b 100644 --- a/.github/workflows/daily-safe-output-optimizer.lock.yml +++ b/.github/workflows/daily-safe-output-optimizer.lock.yml @@ -1056,6 +1056,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1353,6 +1361,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/daily-safe-outputs-conformance.lock.yml b/.github/workflows/daily-safe-outputs-conformance.lock.yml index 8dd61390ddb..22937458f91 100644 --- a/.github/workflows/daily-safe-outputs-conformance.lock.yml +++ b/.github/workflows/daily-safe-outputs-conformance.lock.yml @@ -913,6 +913,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1166,4 +1174,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-secrets-analysis.lock.yml b/.github/workflows/daily-secrets-analysis.lock.yml index 90255fe9213..f6f463202bd 100644 --- a/.github/workflows/daily-secrets-analysis.lock.yml +++ b/.github/workflows/daily-secrets-analysis.lock.yml @@ -851,6 +851,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1090,4 +1098,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-security-red-team.lock.yml b/.github/workflows/daily-security-red-team.lock.yml index 80c06d69fc1..5005ec24e9e 100644 --- a/.github/workflows/daily-security-red-team.lock.yml +++ b/.github/workflows/daily-security-red-team.lock.yml @@ -917,6 +917,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1170,4 +1178,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-semgrep-scan.lock.yml b/.github/workflows/daily-semgrep-scan.lock.yml index 1dc79480d95..c708a76d868 100644 --- a/.github/workflows/daily-semgrep-scan.lock.yml +++ b/.github/workflows/daily-semgrep-scan.lock.yml @@ -882,6 +882,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1126,6 +1134,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); upload_code_scanning_sarif: needs: safe_outputs diff --git a/.github/workflows/daily-syntax-error-quality.lock.yml b/.github/workflows/daily-syntax-error-quality.lock.yml index 32894970252..271bd0309cb 100644 --- a/.github/workflows/daily-syntax-error-quality.lock.yml +++ b/.github/workflows/daily-syntax-error-quality.lock.yml @@ -885,6 +885,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1125,4 +1133,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-team-evolution-insights.lock.yml b/.github/workflows/daily-team-evolution-insights.lock.yml index 9f01159d53b..e2a36b355db 100644 --- a/.github/workflows/daily-team-evolution-insights.lock.yml +++ b/.github/workflows/daily-team-evolution-insights.lock.yml @@ -913,6 +913,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1165,4 +1173,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-team-status.lock.yml b/.github/workflows/daily-team-status.lock.yml index 15d6004be07..f495258896f 100644 --- a/.github/workflows/daily-team-status.lock.yml +++ b/.github/workflows/daily-team-status.lock.yml @@ -874,6 +874,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1146,4 +1154,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml index ea2e4bc3d28..06955e61eb3 100644 --- a/.github/workflows/daily-testify-uber-super-expert.lock.yml +++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml @@ -994,6 +994,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1364,4 +1372,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/daily-workflow-updater.lock.yml b/.github/workflows/daily-workflow-updater.lock.yml index 768fd06263c..d313d8c7c99 100644 --- a/.github/workflows/daily-workflow-updater.lock.yml +++ b/.github/workflows/daily-workflow-updater.lock.yml @@ -855,6 +855,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1126,6 +1134,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/dead-code-remover.lock.yml b/.github/workflows/dead-code-remover.lock.yml index f2e95529c16..e0952431c36 100644 --- a/.github/workflows/dead-code-remover.lock.yml +++ b/.github/workflows/dead-code-remover.lock.yml @@ -912,6 +912,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1227,6 +1235,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml index a39a364d3f1..2ab2b5307c7 100644 --- a/.github/workflows/deep-report.lock.yml +++ b/.github/workflows/deep-report.lock.yml @@ -1150,6 +1150,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1489,6 +1497,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/delight.lock.yml b/.github/workflows/delight.lock.yml index fd82f72be38..36c50c93ed9 100644 --- a/.github/workflows/delight.lock.yml +++ b/.github/workflows/delight.lock.yml @@ -953,6 +953,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1280,4 +1288,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/dependabot-burner.lock.yml b/.github/workflows/dependabot-burner.lock.yml index 486ea8e62c6..95cef2d0a9a 100644 --- a/.github/workflows/dependabot-burner.lock.yml +++ b/.github/workflows/dependabot-burner.lock.yml @@ -857,6 +857,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1126,4 +1134,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml index 6ebfcdf4602..e1032d1599b 100644 --- a/.github/workflows/dependabot-go-checker.lock.yml +++ b/.github/workflows/dependabot-go-checker.lock.yml @@ -875,6 +875,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1112,4 +1120,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index e58f0c79141..d9edd66d9af 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -953,6 +953,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1228,4 +1236,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index efa17384dca..cffa45f451e 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -999,6 +999,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1344,4 +1352,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index 91771e6d276..9a1d4a47893 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -1188,6 +1188,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1626,6 +1634,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/dictation-prompt.lock.yml b/.github/workflows/dictation-prompt.lock.yml index 1ac6e6191bd..423ac27615b 100644 --- a/.github/workflows/dictation-prompt.lock.yml +++ b/.github/workflows/dictation-prompt.lock.yml @@ -933,6 +933,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1272,6 +1280,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/discussion-task-miner.lock.yml b/.github/workflows/discussion-task-miner.lock.yml index f7d8f662e41..8ae19fb7aad 100644 --- a/.github/workflows/discussion-task-miner.lock.yml +++ b/.github/workflows/discussion-task-miner.lock.yml @@ -942,6 +942,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1272,4 +1280,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml index d48b31170a4..e60541c521f 100644 --- a/.github/workflows/docs-noob-tester.lock.yml +++ b/.github/workflows/docs-noob-tester.lock.yml @@ -901,6 +901,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1139,6 +1147,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); upload_assets: needs: agent diff --git a/.github/workflows/draft-pr-cleanup.lock.yml b/.github/workflows/draft-pr-cleanup.lock.yml index 1dd513b7c3f..27e21905fd1 100644 --- a/.github/workflows/draft-pr-cleanup.lock.yml +++ b/.github/workflows/draft-pr-cleanup.lock.yml @@ -887,6 +887,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1129,4 +1137,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index b3ea43caaa5..137ab98cc06 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -955,6 +955,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1207,4 +1215,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index 4700d9c2fb2..4a90e84b122 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -981,6 +981,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1232,4 +1240,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/firewall-escape.lock.yml b/.github/workflows/firewall-escape.lock.yml index 71872c4dea0..dc93f07cf38 100644 --- a/.github/workflows/firewall-escape.lock.yml +++ b/.github/workflows/firewall-escape.lock.yml @@ -940,6 +940,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1336,6 +1344,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/functional-pragmatist.lock.yml b/.github/workflows/functional-pragmatist.lock.yml index 8027b9e7159..40047985772 100644 --- a/.github/workflows/functional-pragmatist.lock.yml +++ b/.github/workflows/functional-pragmatist.lock.yml @@ -867,6 +867,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1136,6 +1144,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml index b4eda268243..0c382aceda5 100644 --- a/.github/workflows/github-mcp-structural-analysis.lock.yml +++ b/.github/workflows/github-mcp-structural-analysis.lock.yml @@ -1006,6 +1006,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1257,6 +1265,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index 4034e930183..4c5db2d682a 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -991,6 +991,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1275,6 +1283,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/github-remote-mcp-auth-test.lock.yml b/.github/workflows/github-remote-mcp-auth-test.lock.yml index a3fd4d409a7..1d5f98630f0 100644 --- a/.github/workflows/github-remote-mcp-auth-test.lock.yml +++ b/.github/workflows/github-remote-mcp-auth-test.lock.yml @@ -864,6 +864,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1100,4 +1108,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index f804a295f3a..fd13a1a8dfb 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -1087,6 +1087,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1509,6 +1517,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml index 5bc52d7f61e..d171c4f1406 100644 --- a/.github/workflows/go-fan.lock.yml +++ b/.github/workflows/go-fan.lock.yml @@ -1039,6 +1039,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1291,6 +1299,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml index 54d59f84e66..cca7dc6e74a 100644 --- a/.github/workflows/go-logger.lock.yml +++ b/.github/workflows/go-logger.lock.yml @@ -1151,6 +1151,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1434,6 +1442,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 33f1c565b9d..f3bfb276456 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -979,6 +979,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1231,4 +1239,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/gpclean.lock.yml b/.github/workflows/gpclean.lock.yml index a01bed112b2..eac816803a5 100644 --- a/.github/workflows/gpclean.lock.yml +++ b/.github/workflows/gpclean.lock.yml @@ -895,6 +895,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1132,6 +1140,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index 9fbe804182a..0524d864ce3 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -1009,6 +1009,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1287,6 +1295,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/hourly-ci-cleaner.lock.yml b/.github/workflows/hourly-ci-cleaner.lock.yml index 0c7e82b6234..53fabe66159 100644 --- a/.github/workflows/hourly-ci-cleaner.lock.yml +++ b/.github/workflows/hourly-ci-cleaner.lock.yml @@ -975,6 +975,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1244,6 +1252,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml index 8ad2199d0aa..e7d1db192e6 100644 --- a/.github/workflows/instructions-janitor.lock.yml +++ b/.github/workflows/instructions-janitor.lock.yml @@ -973,6 +973,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1256,6 +1264,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml index 511ee8af186..9fb3a056abc 100644 --- a/.github/workflows/issue-arborist.lock.yml +++ b/.github/workflows/issue-arborist.lock.yml @@ -946,6 +946,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1182,4 +1190,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 44a7226bb55..604e36bc5ed 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -1250,6 +1250,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1934,4 +1942,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/issue-triage-agent.lock.yml b/.github/workflows/issue-triage-agent.lock.yml index 50273d973c0..8585fc2b1c3 100644 --- a/.github/workflows/issue-triage-agent.lock.yml +++ b/.github/workflows/issue-triage-agent.lock.yml @@ -847,6 +847,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1086,4 +1094,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml index 6891dae0316..ec43317ded0 100644 --- a/.github/workflows/jsweep.lock.yml +++ b/.github/workflows/jsweep.lock.yml @@ -972,6 +972,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1241,6 +1249,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/layout-spec-maintainer.lock.yml b/.github/workflows/layout-spec-maintainer.lock.yml index bb55275d1f7..90fb1b7b365 100644 --- a/.github/workflows/layout-spec-maintainer.lock.yml +++ b/.github/workflows/layout-spec-maintainer.lock.yml @@ -899,6 +899,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1168,6 +1176,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index 666310ba467..646981d4f27 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -945,6 +945,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1196,6 +1204,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index 157b6adc5cd..4b736cd7461 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -1400,6 +1400,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1913,6 +1921,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml index 6dea2d5a8b1..dab53dd03a6 100644 --- a/.github/workflows/mergefest.lock.yml +++ b/.github/workflows/mergefest.lock.yml @@ -952,6 +952,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1263,6 +1271,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index 5b00ac160df..42c57671499 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -861,6 +861,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1226,4 +1234,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/org-health-report.lock.yml b/.github/workflows/org-health-report.lock.yml index 024c516cb36..5171916fa41 100644 --- a/.github/workflows/org-health-report.lock.yml +++ b/.github/workflows/org-health-report.lock.yml @@ -950,6 +950,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1186,6 +1194,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 1b12f733f9c..76c33079ce6 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1026,6 +1026,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1310,6 +1318,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index a6a8372f560..4a8899b009b 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -955,6 +955,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1237,4 +1245,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 8da1309cb78..0344a2c8fba 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1315,6 +1315,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1611,6 +1619,14 @@ jobs: const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/create_agent_session.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/portfolio-analyst.lock.yml b/.github/workflows/portfolio-analyst.lock.yml index b0b6e2e5154..99d1f49ab09 100644 --- a/.github/workflows/portfolio-analyst.lock.yml +++ b/.github/workflows/portfolio-analyst.lock.yml @@ -1040,6 +1040,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1277,6 +1285,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml index 51505f76ed9..f7ada2229ca 100644 --- a/.github/workflows/pr-nitpick-reviewer.lock.yml +++ b/.github/workflows/pr-nitpick-reviewer.lock.yml @@ -1023,6 +1023,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1305,6 +1313,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml index 3a479d23c34..45885d2316d 100644 --- a/.github/workflows/pr-triage-agent.lock.yml +++ b/.github/workflows/pr-triage-agent.lock.yml @@ -937,6 +937,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1264,4 +1272,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index d3c53e99384..ce4796f9484 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -1084,6 +1084,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1335,6 +1343,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml index af480ec4935..a6c1b7feed9 100644 --- a/.github/workflows/python-data-charts.lock.yml +++ b/.github/workflows/python-data-charts.lock.yml @@ -1016,6 +1016,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1252,6 +1260,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index da890e2f634..aa775f7844a 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1183,6 +1183,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1499,6 +1507,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/refiner.lock.yml b/.github/workflows/refiner.lock.yml index fad74c6f31c..c6cef4b5f51 100644 --- a/.github/workflows/refiner.lock.yml +++ b/.github/workflows/refiner.lock.yml @@ -916,6 +916,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1223,6 +1231,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index 50f6cc183f7..d3c62939766 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -896,6 +896,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); config: needs: @@ -1504,6 +1512,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); sync_actions: needs: diff --git a/.github/workflows/repo-audit-analyzer.lock.yml b/.github/workflows/repo-audit-analyzer.lock.yml index dbfc6d34049..94c108017f7 100644 --- a/.github/workflows/repo-audit-analyzer.lock.yml +++ b/.github/workflows/repo-audit-analyzer.lock.yml @@ -893,6 +893,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1129,6 +1137,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index ab403ba1775..2b14829e966 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -850,6 +850,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1086,4 +1094,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml index c99a7a0d941..3ad0c2c1c4c 100644 --- a/.github/workflows/repository-quality-improver.lock.yml +++ b/.github/workflows/repository-quality-improver.lock.yml @@ -952,6 +952,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1188,6 +1196,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index d16f3beaad2..d705993f5eb 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -880,6 +880,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1116,4 +1124,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index b892da87435..c56fd4b4c1e 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -1046,6 +1046,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1297,6 +1305,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml index 0af51d57c99..e0a79448ea5 100644 --- a/.github/workflows/schema-consistency-checker.lock.yml +++ b/.github/workflows/schema-consistency-checker.lock.yml @@ -945,6 +945,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1196,6 +1204,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/schema-feature-coverage.lock.yml b/.github/workflows/schema-feature-coverage.lock.yml index 29deadc8ec9..7d03826fbb9 100644 --- a/.github/workflows/schema-feature-coverage.lock.yml +++ b/.github/workflows/schema-feature-coverage.lock.yml @@ -880,6 +880,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1146,6 +1154,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 6d83685df5b..c65a320e687 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1201,6 +1201,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1500,6 +1508,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml index 86011bf3339..0e811d98a6d 100644 --- a/.github/workflows/security-compliance.lock.yml +++ b/.github/workflows/security-compliance.lock.yml @@ -905,6 +905,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1227,4 +1235,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/security-review.lock.yml b/.github/workflows/security-review.lock.yml index 6fbd6a7b355..8dd6a1ad2c7 100644 --- a/.github/workflows/security-review.lock.yml +++ b/.github/workflows/security-review.lock.yml @@ -1067,6 +1067,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1347,6 +1355,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index 925ebf78d03..19fc1dbcfaa 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -1011,6 +1011,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1263,4 +1271,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml index 0f3473bdcfd..2ce7567339f 100644 --- a/.github/workflows/sergo.lock.yml +++ b/.github/workflows/sergo.lock.yml @@ -1028,6 +1028,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1280,6 +1288,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/slide-deck-maintainer.lock.yml b/.github/workflows/slide-deck-maintainer.lock.yml index 894bbff99b2..78397b7e9fd 100644 --- a/.github/workflows/slide-deck-maintainer.lock.yml +++ b/.github/workflows/slide-deck-maintainer.lock.yml @@ -987,6 +987,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1301,6 +1309,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/smoke-agent-all-merged.lock.yml b/.github/workflows/smoke-agent-all-merged.lock.yml index e7e8622c41d..8057899abe7 100644 --- a/.github/workflows/smoke-agent-all-merged.lock.yml +++ b/.github/workflows/smoke-agent-all-merged.lock.yml @@ -908,6 +908,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1181,4 +1189,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/smoke-agent-all-none.lock.yml b/.github/workflows/smoke-agent-all-none.lock.yml index 01a8a1fcc42..e2285a97ba7 100644 --- a/.github/workflows/smoke-agent-all-none.lock.yml +++ b/.github/workflows/smoke-agent-all-none.lock.yml @@ -908,6 +908,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1181,4 +1189,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/smoke-agent-public-approved.lock.yml b/.github/workflows/smoke-agent-public-approved.lock.yml index 26d9b3356a4..41ad4698e28 100644 --- a/.github/workflows/smoke-agent-public-approved.lock.yml +++ b/.github/workflows/smoke-agent-public-approved.lock.yml @@ -943,6 +943,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1237,4 +1245,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/smoke-agent-public-none.lock.yml b/.github/workflows/smoke-agent-public-none.lock.yml index 3d8c74d83ac..05d5b44192d 100644 --- a/.github/workflows/smoke-agent-public-none.lock.yml +++ b/.github/workflows/smoke-agent-public-none.lock.yml @@ -908,6 +908,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1181,4 +1189,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/smoke-agent-scoped-approved.lock.yml b/.github/workflows/smoke-agent-scoped-approved.lock.yml index aee63820592..d543b431ab9 100644 --- a/.github/workflows/smoke-agent-scoped-approved.lock.yml +++ b/.github/workflows/smoke-agent-scoped-approved.lock.yml @@ -918,6 +918,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1191,4 +1199,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/smoke-call-workflow.lock.yml b/.github/workflows/smoke-call-workflow.lock.yml index b53c0a5358d..d9996706389 100644 --- a/.github/workflows/smoke-call-workflow.lock.yml +++ b/.github/workflows/smoke-call-workflow.lock.yml @@ -881,6 +881,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1149,4 +1157,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 5b6bc94ea0e..b306f4f5087 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -2513,6 +2513,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -2842,6 +2850,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index b7cf767e10c..12fa310e207 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -1473,6 +1473,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1829,6 +1837,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/smoke-copilot-arm.lock.yml b/.github/workflows/smoke-copilot-arm.lock.yml index 3cdb9535413..e91eddbc1bc 100644 --- a/.github/workflows/smoke-copilot-arm.lock.yml +++ b/.github/workflows/smoke-copilot-arm.lock.yml @@ -1849,6 +1849,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -2128,6 +2136,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); send_slack_message: needs: diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index daffe564d9c..7a0e924a4fa 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -1901,6 +1901,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -2178,6 +2186,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); send_slack_message: needs: diff --git a/.github/workflows/smoke-create-cross-repo-pr.lock.yml b/.github/workflows/smoke-create-cross-repo-pr.lock.yml index 6e6991ed9cd..b976587743f 100644 --- a/.github/workflows/smoke-create-cross-repo-pr.lock.yml +++ b/.github/workflows/smoke-create-cross-repo-pr.lock.yml @@ -989,6 +989,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1303,6 +1311,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/smoke-gemini.lock.yml b/.github/workflows/smoke-gemini.lock.yml index c99b8fe1c26..0e6dc239842 100644 --- a/.github/workflows/smoke-gemini.lock.yml +++ b/.github/workflows/smoke-gemini.lock.yml @@ -1135,6 +1135,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1422,6 +1430,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/smoke-multi-pr.lock.yml b/.github/workflows/smoke-multi-pr.lock.yml index a98f29959d3..979324ebdc3 100644 --- a/.github/workflows/smoke-multi-pr.lock.yml +++ b/.github/workflows/smoke-multi-pr.lock.yml @@ -974,6 +974,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1281,6 +1289,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/smoke-project.lock.yml b/.github/workflows/smoke-project.lock.yml index f985fd7567a..0761f030259 100644 --- a/.github/workflows/smoke-project.lock.yml +++ b/.github/workflows/smoke-project.lock.yml @@ -1107,6 +1107,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1419,6 +1427,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/smoke-service-ports.lock.yml b/.github/workflows/smoke-service-ports.lock.yml index 68f88b02a60..c13b8101fcd 100644 --- a/.github/workflows/smoke-service-ports.lock.yml +++ b/.github/workflows/smoke-service-ports.lock.yml @@ -882,6 +882,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1154,4 +1162,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/smoke-temporary-id.lock.yml b/.github/workflows/smoke-temporary-id.lock.yml index 6a468ee3ff2..8fc72a33f79 100644 --- a/.github/workflows/smoke-temporary-id.lock.yml +++ b/.github/workflows/smoke-temporary-id.lock.yml @@ -958,6 +958,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1235,4 +1243,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/smoke-test-tools.lock.yml b/.github/workflows/smoke-test-tools.lock.yml index 2085a057bf0..b4ea925c0c3 100644 --- a/.github/workflows/smoke-test-tools.lock.yml +++ b/.github/workflows/smoke-test-tools.lock.yml @@ -919,6 +919,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1194,4 +1202,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/smoke-update-cross-repo-pr.lock.yml b/.github/workflows/smoke-update-cross-repo-pr.lock.yml index 5e9e3e03cc5..6dfa95029a0 100644 --- a/.github/workflows/smoke-update-cross-repo-pr.lock.yml +++ b/.github/workflows/smoke-update-cross-repo-pr.lock.yml @@ -1013,6 +1013,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1326,6 +1334,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/smoke-workflow-call-with-inputs.lock.yml b/.github/workflows/smoke-workflow-call-with-inputs.lock.yml index ff0884963c3..aca80dcc956 100644 --- a/.github/workflows/smoke-workflow-call-with-inputs.lock.yml +++ b/.github/workflows/smoke-workflow-call-with-inputs.lock.yml @@ -918,6 +918,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1188,4 +1196,12 @@ jobs: name: ${{ needs.activation.outputs.artifact_prefix }}safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/smoke-workflow-call.lock.yml b/.github/workflows/smoke-workflow-call.lock.yml index b25f62ff38f..abdb242b659 100644 --- a/.github/workflows/smoke-workflow-call.lock.yml +++ b/.github/workflows/smoke-workflow-call.lock.yml @@ -909,6 +909,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1182,4 +1190,12 @@ jobs: name: ${{ needs.activation.outputs.artifact_prefix }}safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml index 30ad5308879..49cc11d7496 100644 --- a/.github/workflows/stale-repo-identifier.lock.yml +++ b/.github/workflows/stale-repo-identifier.lock.yml @@ -1017,6 +1017,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1255,6 +1263,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index 8975fd3e943..d9dcb51ff32 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -1037,6 +1037,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1288,6 +1296,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/step-name-alignment.lock.yml b/.github/workflows/step-name-alignment.lock.yml index f0ef80ece69..57fbe8997bc 100644 --- a/.github/workflows/step-name-alignment.lock.yml +++ b/.github/workflows/step-name-alignment.lock.yml @@ -959,6 +959,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1211,6 +1219,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/sub-issue-closer.lock.yml b/.github/workflows/sub-issue-closer.lock.yml index f0b61a85939..93fd46e0f93 100644 --- a/.github/workflows/sub-issue-closer.lock.yml +++ b/.github/workflows/sub-issue-closer.lock.yml @@ -891,6 +891,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1130,4 +1138,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml index 359b54b8e62..c721244f848 100644 --- a/.github/workflows/super-linter.lock.yml +++ b/.github/workflows/super-linter.lock.yml @@ -905,6 +905,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1142,6 +1150,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); super_linter: needs: activation diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index e5c885a5120..3a80d9127e0 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -1092,6 +1092,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1518,6 +1526,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/terminal-stylist.lock.yml b/.github/workflows/terminal-stylist.lock.yml index 66b4d96a3d0..fcb6eb20e81 100644 --- a/.github/workflows/terminal-stylist.lock.yml +++ b/.github/workflows/terminal-stylist.lock.yml @@ -914,6 +914,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1150,4 +1158,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/test-create-pr-error-handling.lock.yml b/.github/workflows/test-create-pr-error-handling.lock.yml index 720bd648a43..814cd8cafff 100644 --- a/.github/workflows/test-create-pr-error-handling.lock.yml +++ b/.github/workflows/test-create-pr-error-handling.lock.yml @@ -946,6 +946,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1229,6 +1237,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/test-dispatcher.lock.yml b/.github/workflows/test-dispatcher.lock.yml index c64d1d0cc74..f12d952e618 100644 --- a/.github/workflows/test-dispatcher.lock.yml +++ b/.github/workflows/test-dispatcher.lock.yml @@ -830,6 +830,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1064,4 +1072,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/test-project-url-default.lock.yml b/.github/workflows/test-project-url-default.lock.yml index 6ecdb0e6941..fe6b1951fb8 100644 --- a/.github/workflows/test-project-url-default.lock.yml +++ b/.github/workflows/test-project-url-default.lock.yml @@ -890,6 +890,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1127,4 +1135,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 224cfe39193..8b4037f3002 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -1005,6 +1005,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1319,6 +1327,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/token-logs-fetch.lock.yml b/.github/workflows/token-logs-fetch.lock.yml index 91782040481..a18cf2108e3 100644 --- a/.github/workflows/token-logs-fetch.lock.yml +++ b/.github/workflows/token-logs-fetch.lock.yml @@ -894,6 +894,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1133,6 +1141,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml index 3e3c40e0ae2..40fa217dfe9 100644 --- a/.github/workflows/typist.lock.yml +++ b/.github/workflows/typist.lock.yml @@ -987,6 +987,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1238,4 +1246,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/ubuntu-image-analyzer.lock.yml b/.github/workflows/ubuntu-image-analyzer.lock.yml index 19bf3e37f0c..6f70a78b230 100644 --- a/.github/workflows/ubuntu-image-analyzer.lock.yml +++ b/.github/workflows/ubuntu-image-analyzer.lock.yml @@ -899,6 +899,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1213,6 +1221,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 8e03e4c4cff..8940f6dc9d6 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1260,6 +1260,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1660,6 +1668,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/update-astro.lock.yml b/.github/workflows/update-astro.lock.yml index 34469ec37f9..67eb5d0628a 100644 --- a/.github/workflows/update-astro.lock.yml +++ b/.github/workflows/update-astro.lock.yml @@ -924,6 +924,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1238,6 +1246,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index 8f5474e67b5..773bb5aa68e 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -884,6 +884,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1121,4 +1129,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/weekly-blog-post-writer.lock.yml b/.github/workflows/weekly-blog-post-writer.lock.yml index 880e266f62a..d44753cbe2b 100644 --- a/.github/workflows/weekly-blog-post-writer.lock.yml +++ b/.github/workflows/weekly-blog-post-writer.lock.yml @@ -1063,6 +1063,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1498,6 +1506,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/weekly-editors-health-check.lock.yml b/.github/workflows/weekly-editors-health-check.lock.yml index b650c986bfb..ede2b3f56ed 100644 --- a/.github/workflows/weekly-editors-health-check.lock.yml +++ b/.github/workflows/weekly-editors-health-check.lock.yml @@ -932,6 +932,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1201,6 +1209,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml index 9f51dc26a55..46838bd5325 100644 --- a/.github/workflows/weekly-issue-summary.lock.yml +++ b/.github/workflows/weekly-issue-summary.lock.yml @@ -934,6 +934,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1171,6 +1179,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); update_cache_memory: needs: diff --git a/.github/workflows/weekly-safe-outputs-spec-review.lock.yml b/.github/workflows/weekly-safe-outputs-spec-review.lock.yml index 409165719ac..15d111b7f62 100644 --- a/.github/workflows/weekly-safe-outputs-spec-review.lock.yml +++ b/.github/workflows/weekly-safe-outputs-spec-review.lock.yml @@ -866,6 +866,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1135,6 +1143,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); - name: Restore actions folder if: always() uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml index 00526e52cdc..7da2df8943b 100644 --- a/.github/workflows/workflow-generator.lock.yml +++ b/.github/workflows/workflow-generator.lock.yml @@ -940,6 +940,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1246,6 +1254,14 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); unlock: needs: diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml index ec6656127f5..6d35d609aa7 100644 --- a/.github/workflows/workflow-health-manager.lock.yml +++ b/.github/workflows/workflow-health-manager.lock.yml @@ -973,6 +973,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1331,4 +1339,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/workflow-normalizer.lock.yml b/.github/workflows/workflow-normalizer.lock.yml index 771065ff964..ad1694f361a 100644 --- a/.github/workflows/workflow-normalizer.lock.yml +++ b/.github/workflows/workflow-normalizer.lock.yml @@ -934,6 +934,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1172,4 +1180,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/.github/workflows/workflow-skill-extractor.lock.yml b/.github/workflows/workflow-skill-extractor.lock.yml index 42b29421339..d7e8eea5470 100644 --- a/.github/workflows/workflow-skill-extractor.lock.yml +++ b/.github/workflows/workflow-skill-extractor.lock.yml @@ -905,6 +905,14 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.conclusion"); detection: needs: agent @@ -1143,4 +1151,12 @@ jobs: name: safe-output-items path: /tmp/gh-aw/safe-output-items.jsonl if-no-files-found: ignore + - name: Send OTLP job span + if: always() + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { sendJobConclusionSpan } = require('${{ runner.temp }}/gh-aw/actions/send_otlp_span.cjs'); + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); diff --git a/actions/setup/js/generate_observability_summary.cjs b/actions/setup/js/generate_observability_summary.cjs index 0f28bb12a48..ae45457929e 100644 --- a/actions/setup/js/generate_observability_summary.cjs +++ b/actions/setup/js/generate_observability_summary.cjs @@ -124,6 +124,15 @@ async function main(core) { const markdown = buildObservabilitySummary(data); await core.summary.addRaw(markdown).write(); core.info("Generated observability summary in step summary"); + + // Send an OTLP conclusion span to the configured endpoint, if any. + // Non-fatal: errors are handled inside sendJobConclusionSpan via console.warn. + try { + const { sendJobConclusionSpan } = require("./send_otlp_span.cjs"); + await sendJobConclusionSpan("gh-aw.job.conclusion"); + } catch { + // Silently ignore unexpected require/call failures. + } } module.exports = { diff --git a/actions/setup/js/send_otlp_span.cjs b/actions/setup/js/send_otlp_span.cjs index 1a75a9eb202..70f50db038d 100644 --- a/actions/setup/js/send_otlp_span.cjs +++ b/actions/setup/js/send_otlp_span.cjs @@ -2,6 +2,7 @@ /// const { randomBytes } = require("crypto"); +const fs = require("fs"); /** * send_otlp_span.cjs @@ -73,12 +74,13 @@ function buildAttr(key, value) { /** * @typedef {Object} OTLPSpanOptions - * @property {string} traceId - 32-char hex trace ID - * @property {string} spanId - 16-char hex span ID - * @property {string} spanName - Human-readable span name - * @property {number} startMs - Span start time (ms since epoch) - * @property {number} endMs - Span end time (ms since epoch) - * @property {string} serviceName - Value for the service.name resource attribute + * @property {string} traceId - 32-char hex trace ID + * @property {string} spanId - 16-char hex span ID + * @property {string} spanName - Human-readable span name + * @property {number} startMs - Span start time (ms since epoch) + * @property {number} endMs - Span end time (ms since epoch) + * @property {string} serviceName - Value for the service.name resource attribute + * @property {string} [scopeVersion] - gh-aw version string (e.g. from GH_AW_INFO_VERSION) * @property {Array<{key: string, value: object}>} attributes - Span attributes */ @@ -88,7 +90,7 @@ function buildAttr(key, value) { * @param {OTLPSpanOptions} opts * @returns {object} - Ready to be serialised as JSON and POSTed to `/v1/traces` */ -function buildOTLPPayload({ traceId, spanId, spanName, startMs, endMs, serviceName, attributes }) { +function buildOTLPPayload({ traceId, spanId, spanName, startMs, endMs, serviceName, scopeVersion, attributes }) { return { resourceSpans: [ { @@ -97,7 +99,7 @@ function buildOTLPPayload({ traceId, spanId, spanName, startMs, endMs, serviceNa }, scopeSpans: [ { - scope: { name: "gh-aw.setup", version: "1.0.0" }, + scope: { name: "gh-aw", version: scopeVersion || "unknown" }, spans: [ { traceId, @@ -122,22 +124,47 @@ function buildOTLPPayload({ traceId, spanId, spanName, startMs, endMs, serviceNa // --------------------------------------------------------------------------- /** - * POST an OTLP traces payload to `{endpoint}/v1/traces`. + * POST an OTLP traces payload to `{endpoint}/v1/traces` with automatic retries. * - * @param {string} endpoint - OTLP base URL (e.g. https://traces.example.com:4317) - * @param {object} payload - Serialisable OTLP JSON object + * Failures are surfaced as `console.warn` messages and never thrown; OTLP + * export failures must not break the workflow. Uses exponential back-off + * between attempts (100 ms, 200 ms) so the three total attempts finish in + * well under a second in the typical success case. + * + * @param {string} endpoint - OTLP base URL (e.g. https://traces.example.com:4317) + * @param {object} payload - Serialisable OTLP JSON object + * @param {{ maxRetries?: number, baseDelayMs?: number }} [opts] * @returns {Promise} - * @throws {Error} when the server returns a non-2xx status */ -async function sendOTLPSpan(endpoint, payload) { +async function sendOTLPSpan(endpoint, payload, { maxRetries = 2, baseDelayMs = 100 } = {}) { const url = endpoint.replace(/\/$/, "") + "/v1/traces"; - const response = await fetch(url, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); - if (!response.ok) { - throw new Error(`OTLP export failed: HTTP ${response.status} ${response.statusText}`); + for (let attempt = 0; attempt <= maxRetries; attempt++) { + if (attempt > 0) { + await new Promise(resolve => setTimeout(resolve, baseDelayMs * 2 ** (attempt - 1))); + } + try { + const response = await fetch(url, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + if (response.ok) { + return; + } + const msg = `HTTP ${response.status} ${response.statusText}`; + if (attempt < maxRetries) { + console.warn(`OTLP export attempt ${attempt + 1}/${maxRetries + 1} failed: ${msg}, retrying…`); + } else { + console.warn(`OTLP export failed after ${maxRetries + 1} attempts: ${msg}`); + } + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + if (attempt < maxRetries) { + console.warn(`OTLP export attempt ${attempt + 1}/${maxRetries + 1} error: ${msg}, retrying…`); + } else { + console.warn(`OTLP export error after ${maxRetries + 1} attempts: ${msg}`); + } + } } } @@ -238,6 +265,7 @@ async function sendJobSetupSpan(options = {}) { startMs, endMs, serviceName, + scopeVersion: process.env.GH_AW_INFO_VERSION || "unknown", attributes, }); @@ -245,6 +273,105 @@ async function sendJobSetupSpan(options = {}) { return traceId; } +// --------------------------------------------------------------------------- +// Utilities for conclusion span +// --------------------------------------------------------------------------- + +/** + * Safely read and parse a JSON file. Returns `null` on any error (missing + * file, invalid JSON, permission denied, etc.). + * + * @param {string} filePath - Absolute path to the JSON file + * @returns {object | null} + */ +function readJSONIfExists(filePath) { + try { + return JSON.parse(fs.readFileSync(filePath, "utf8")); + } catch { + return null; + } +} + +// --------------------------------------------------------------------------- +// High-level: job conclusion span +// --------------------------------------------------------------------------- + +/** + * Send a conclusion span for a safe_outputs or conclusion job to the configured + * OTLP endpoint. The span carries workflow metadata read from `aw_info.json` + * and the effective token count from `GH_AW_EFFECTIVE_TOKENS`. + * + * This is a no-op when `OTEL_EXPORTER_OTLP_ENDPOINT` is not set. All errors + * are surfaced as `console.warn` messages and never re-thrown. + * + * Environment variables consumed: + * - `OTEL_EXPORTER_OTLP_ENDPOINT` – collector endpoint + * - `OTEL_SERVICE_NAME` – service name (defaults to "gh-aw") + * - `GH_AW_EFFECTIVE_TOKENS` – total effective token count for the run + * - `GITHUB_RUN_ID` – GitHub Actions run ID + * - `GITHUB_ACTOR` – GitHub Actions actor + * - `GITHUB_REPOSITORY` – `owner/repo` string + * + * Runtime files read: + * - `/tmp/gh-aw/aw_info.json` – workflow/engine metadata written by the agent job + * + * @param {string} spanName - OTLP span name (e.g. `"gh-aw.job.safe-outputs"`) + * @param {{ startMs?: number }} [options] + * @returns {Promise} + */ +async function sendJobConclusionSpan(spanName, options = {}) { + const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || ""; + if (!endpoint) { + return; + } + + const startMs = options.startMs ?? Date.now(); + + // Read workflow metadata from aw_info.json (written by the agent job setup step). + const awInfo = readJSONIfExists("/tmp/gh-aw/aw_info.json") || {}; + + // Effective token count is surfaced by the agent job and passed to downstream jobs + // via the GH_AW_EFFECTIVE_TOKENS environment variable. + const rawET = process.env.GH_AW_EFFECTIVE_TOKENS || ""; + const effectiveTokens = rawET ? parseInt(rawET, 10) : NaN; + + const serviceName = process.env.OTEL_SERVICE_NAME || "gh-aw"; + const version = awInfo.agent_version || awInfo.version || process.env.GH_AW_INFO_VERSION || "unknown"; + + // Use the workflow_call_id from aw_info as the trace ID when available so that + // conclusion spans can be correlated with the activation span. + const awTraceId = typeof awInfo.context?.workflow_call_id === "string" ? awInfo.context.workflow_call_id.replace(/-/g, "") : ""; + const traceId = awTraceId && isValidTraceId(awTraceId) ? awTraceId : generateTraceId(); + + const workflowName = awInfo.workflow_name || ""; + const engineId = awInfo.engine_id || ""; + const model = awInfo.model || ""; + const runId = process.env.GITHUB_RUN_ID || ""; + const actor = process.env.GITHUB_ACTOR || ""; + const repository = process.env.GITHUB_REPOSITORY || ""; + + const attributes = [buildAttr("gh-aw.workflow.name", workflowName), buildAttr("gh-aw.run.id", runId), buildAttr("gh-aw.run.actor", actor), buildAttr("gh-aw.repository", repository)]; + + if (engineId) attributes.push(buildAttr("gh-aw.engine.id", engineId)); + if (model) attributes.push(buildAttr("gh-aw.model", model)); + if (!isNaN(effectiveTokens) && effectiveTokens > 0) { + attributes.push(buildAttr("gh-aw.effective_tokens", effectiveTokens)); + } + + const payload = buildOTLPPayload({ + traceId, + spanId: generateSpanId(), + spanName, + startMs, + endMs: Date.now(), + serviceName, + scopeVersion: version, + attributes, + }); + + await sendOTLPSpan(endpoint, payload); +} + module.exports = { isValidTraceId, generateTraceId, @@ -253,5 +380,7 @@ module.exports = { buildAttr, buildOTLPPayload, sendOTLPSpan, + readJSONIfExists, sendJobSetupSpan, + sendJobConclusionSpan, }; diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index 7853b1f653a..681529e2a93 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -4,7 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; // Module import // --------------------------------------------------------------------------- -const { isValidTraceId, generateTraceId, generateSpanId, toNanoString, buildAttr, buildOTLPPayload, sendOTLPSpan, sendJobSetupSpan } = await import("./send_otlp_span.cjs"); +const { isValidTraceId, generateTraceId, generateSpanId, toNanoString, buildAttr, buildOTLPPayload, sendOTLPSpan, sendJobSetupSpan, sendJobConclusionSpan } = await import("./send_otlp_span.cjs"); // --------------------------------------------------------------------------- // isValidTraceId @@ -128,6 +128,7 @@ describe("buildOTLPPayload", () => { startMs: 1000, endMs: 2000, serviceName: "gh-aw", + scopeVersion: "v1.2.3", attributes: [buildAttr("foo", "bar")], }); @@ -137,9 +138,10 @@ describe("buildOTLPPayload", () => { // Resource expect(rs.resource.attributes).toContainEqual({ key: "service.name", value: { stringValue: "gh-aw" } }); - // Scope + // Scope — name is always "gh-aw"; version comes from scopeVersion expect(rs.scopeSpans).toHaveLength(1); - expect(rs.scopeSpans[0].scope.name).toBe("gh-aw.setup"); + expect(rs.scopeSpans[0].scope.name).toBe("gh-aw"); + expect(rs.scopeSpans[0].scope.version).toBe("v1.2.3"); // Span const span = rs.scopeSpans[0].spans[0]; @@ -152,6 +154,19 @@ describe("buildOTLPPayload", () => { expect(span.status.code).toBe(1); expect(span.attributes).toContainEqual({ key: "foo", value: { stringValue: "bar" } }); }); + + it("uses 'unknown' as scope version when scopeVersion is omitted", () => { + const payload = buildOTLPPayload({ + traceId: "a".repeat(32), + spanId: "b".repeat(16), + spanName: "test", + startMs: 0, + endMs: 1, + serviceName: "gh-aw", + attributes: [], + }); + expect(payload.resourceSpans[0].scopeSpans[0].scope.version).toBe("unknown"); + }); }); // --------------------------------------------------------------------------- @@ -191,11 +206,48 @@ describe("sendOTLPSpan", () => { expect(url).toBe("https://traces.example.com/v1/traces"); }); - it("throws when server returns non-2xx status", async () => { + it("warns (does not throw) when server returns non-2xx status on all retries", async () => { const mockFetch = vi.fn().mockResolvedValue({ ok: false, status: 400, statusText: "Bad Request" }); vi.stubGlobal("fetch", mockFetch); + const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + + // Should not throw + await expect(sendOTLPSpan("https://traces.example.com", {}, { maxRetries: 1, baseDelayMs: 1 })).resolves.toBeUndefined(); - await expect(sendOTLPSpan("https://traces.example.com", {})).rejects.toThrow("OTLP export failed: HTTP 400 Bad Request"); + // Two attempts (1 initial + 1 retry) + expect(mockFetch).toHaveBeenCalledTimes(2); + expect(warnSpy).toHaveBeenCalledTimes(2); + expect(warnSpy.mock.calls[0][0]).toContain("attempt 1/2 failed"); + expect(warnSpy.mock.calls[1][0]).toContain("failed after 2 attempts"); + + warnSpy.mockRestore(); + }); + + it("retries on failure and succeeds on second attempt", async () => { + const mockFetch = vi.fn().mockResolvedValueOnce({ ok: false, status: 503, statusText: "Service Unavailable" }).mockResolvedValueOnce({ ok: true, status: 200, statusText: "OK" }); + vi.stubGlobal("fetch", mockFetch); + const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + + await sendOTLPSpan("https://traces.example.com", {}, { maxRetries: 2, baseDelayMs: 1 }); + + expect(mockFetch).toHaveBeenCalledTimes(2); + expect(warnSpy).toHaveBeenCalledTimes(1); + expect(warnSpy.mock.calls[0][0]).toContain("attempt 1/3 failed"); + + warnSpy.mockRestore(); + }); + + it("warns (does not throw) when fetch rejects on all retries", async () => { + const mockFetch = vi.fn().mockRejectedValue(new Error("network error")); + vi.stubGlobal("fetch", mockFetch); + const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + + await expect(sendOTLPSpan("https://traces.example.com", {}, { maxRetries: 1, baseDelayMs: 1 })).resolves.toBeUndefined(); + + expect(mockFetch).toHaveBeenCalledTimes(2); + expect(warnSpy.mock.calls[1][0]).toContain("error after 2 attempts"); + + warnSpy.mockRestore(); }); }); @@ -393,3 +445,99 @@ describe("sendJobSetupSpan", () => { expect(keys).not.toContain("gh-aw.engine.id"); }); }); + +// --------------------------------------------------------------------------- +// sendJobConclusionSpan +// --------------------------------------------------------------------------- + +describe("sendJobConclusionSpan", () => { + /** @type {Record} */ + const savedEnv = {}; + const envKeys = ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_SERVICE_NAME", "GH_AW_EFFECTIVE_TOKENS", "GH_AW_INFO_VERSION", "GITHUB_RUN_ID", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]; + + beforeEach(() => { + vi.stubGlobal("fetch", vi.fn()); + for (const k of envKeys) { + savedEnv[k] = process.env[k]; + delete process.env[k]; + } + }); + + afterEach(() => { + vi.unstubAllGlobals(); + for (const k of envKeys) { + if (savedEnv[k] !== undefined) { + process.env[k] = savedEnv[k]; + } else { + delete process.env[k]; + } + } + }); + + it("is a no-op when OTEL_EXPORTER_OTLP_ENDPOINT is not set", async () => { + await sendJobConclusionSpan("gh-aw.job.conclusion"); + expect(fetch).not.toHaveBeenCalled(); + }); + + it("sends a span with the given span name", 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_RUN_ID = "111"; + process.env.GITHUB_ACTOR = "octocat"; + process.env.GITHUB_REPOSITORY = "owner/repo"; + + await sendJobConclusionSpan("gh-aw.job.safe-outputs"); + + expect(mockFetch).toHaveBeenCalledOnce(); + const body = JSON.parse(mockFetch.mock.calls[0][1].body); + const span = body.resourceSpans[0].scopeSpans[0].spans[0]; + expect(span.name).toBe("gh-aw.job.safe-outputs"); + expect(span.traceId).toMatch(/^[0-9a-f]{32}$/); + expect(span.spanId).toMatch(/^[0-9a-f]{16}$/); + }); + + 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); + + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "https://traces.example.com"; + process.env.GH_AW_EFFECTIVE_TOKENS = "5000"; + + 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 etAttr = span.attributes.find(a => a.key === "gh-aw.effective_tokens"); + expect(etAttr).toBeDefined(); + expect(etAttr.value.intValue).toBe(5000); + }); + + it("omits effective_tokens attribute when GH_AW_EFFECTIVE_TOKENS 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"; + + 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 keys = span.attributes.map(a => a.key); + expect(keys).not.toContain("gh-aw.effective_tokens"); + }); + + it("uses GH_AW_INFO_VERSION as scope version 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_VERSION = "v2.0.0"; + + await sendJobConclusionSpan("gh-aw.job.conclusion"); + + const body = JSON.parse(mockFetch.mock.calls[0][1].body); + expect(body.resourceSpans[0].scopeSpans[0].scope.version).toBe("v2.0.0"); + }); +}); diff --git a/pkg/workflow/compiler_safe_outputs_job.go b/pkg/workflow/compiler_safe_outputs_job.go index 3f589834a70..7c25971ce48 100644 --- a/pkg/workflow/compiler_safe_outputs_job.go +++ b/pkg/workflow/compiler_safe_outputs_job.go @@ -379,6 +379,9 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa steps = append(steps, buildSafeOutputItemsManifestUploadStep(agentArtifactPrefix)...) } + // Append OTLP conclusion span step (no-op when endpoint is not configured). + steps = append(steps, generateOTLPConclusionSpanStep("gh-aw.job.safe-outputs")) + // In dev mode the setup action is referenced via a local path (./actions/setup), so its files // live in the workspace. When the safe_outputs job contains a checkout step for // create_pull_request or push_to_pull_request_branch, the workspace is replaced with the diff --git a/pkg/workflow/notify_comment.go b/pkg/workflow/notify_comment.go index 67a96f075a5..5d15dc1e1af 100644 --- a/pkg/workflow/notify_comment.go +++ b/pkg/workflow/notify_comment.go @@ -354,6 +354,9 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa steps = append(steps, c.buildGitHubAppTokenInvalidationStep()...) } + // Append OTLP conclusion span step (no-op when endpoint is not configured). + steps = append(steps, generateOTLPConclusionSpanStep("gh-aw.job.conclusion")) + // Build the condition for this job: // 1. always() - run even if agent fails // 2. agent was activated (not skipped) OR lockdown check failed in activation job diff --git a/pkg/workflow/observability_otlp.go b/pkg/workflow/observability_otlp.go index e5eb0a29c32..f0af75dede7 100644 --- a/pkg/workflow/observability_otlp.go +++ b/pkg/workflow/observability_otlp.go @@ -48,8 +48,28 @@ func getOTLPEndpointEnvValue(config *FrontmatterConfig) string { return config.Observability.OTLP.Endpoint } -// injectOTLPConfig modifies workflowData to incorporate any OTLP configuration: +// generateOTLPConclusionSpanStep generates a GitHub Actions step that sends an OTLP +// conclusion span from a downstream job (safe_outputs or conclusion). // +// The step is a no-op when OTEL_EXPORTER_OTLP_ENDPOINT is not set, so it is safe to +// emit unconditionally. It runs with if: always() and continue-on-error: true so OTLP +// failures can never block the job. +// +// Parameters: +// - spanName: the OTLP span name, e.g. "gh-aw.job.safe-outputs" +func generateOTLPConclusionSpanStep(spanName string) string { + var sb strings.Builder + sb.WriteString(" - name: Send OTLP job span\n") + sb.WriteString(" if: always()\n") + sb.WriteString(" continue-on-error: true\n") + fmt.Fprintf(&sb, " uses: %s\n", GetActionPin("actions/github-script")) + sb.WriteString(" with:\n") + sb.WriteString(" script: |\n") + fmt.Fprintf(&sb, " const { sendJobConclusionSpan } = require('%s/send_otlp_span.cjs');\n", SetupActionDestination) + fmt.Fprintf(&sb, " await sendJobConclusionSpan(%q);\n", spanName) + return sb.String() +} + // 1. When the endpoint is a static URL, its hostname is appended to // NetworkPermissions.Allowed so the AWF firewall allows outbound traffic to it. // diff --git a/pkg/workflow/observability_otlp_test.go b/pkg/workflow/observability_otlp_test.go index fbdc52931cc..3e4535a1850 100644 --- a/pkg/workflow/observability_otlp_test.go +++ b/pkg/workflow/observability_otlp_test.go @@ -330,3 +330,22 @@ func TestObservabilityConfigParsing(t *testing.T) { }) } } + +// TestGenerateOTLPConclusionSpanStep verifies the YAML produced for OTLP conclusion span steps. +func TestGenerateOTLPConclusionSpanStep(t *testing.T) { + t.Run("safe-outputs span name", func(t *testing.T) { + step := generateOTLPConclusionSpanStep("gh-aw.job.safe-outputs") + assert.Contains(t, step, "name: Send OTLP job span", "step should have correct name") + assert.Contains(t, step, "if: always()", "step should always run") + assert.Contains(t, step, "continue-on-error: true", "step should be non-fatal") + assert.Contains(t, step, "actions/github-script", "step should use github-script action") + assert.Contains(t, step, "sendJobConclusionSpan", "script should call sendJobConclusionSpan") + assert.Contains(t, step, "gh-aw.job.safe-outputs", "script should use the given span name") + assert.Contains(t, step, "send_otlp_span.cjs", "script should require send_otlp_span.cjs") + }) + + t.Run("conclusion span name", func(t *testing.T) { + step := generateOTLPConclusionSpanStep("gh-aw.job.conclusion") + assert.Contains(t, step, "gh-aw.job.conclusion", "script should use the given span name") + }) +} From 0b4b16eae4269f79ff0787bbb60f6073d0ef18d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 07:45:12 +0000 Subject: [PATCH 09/16] feat: add OTEL_EXPORTER_OTLP_HEADERS support to observability.otlp config Agent-Logs-Url: https://github.com/github/gh-aw/sessions/8f5dc8d2-75f6-4186-affe-e329b01b1aaf Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/send_otlp_span.cjs | 34 +++++++- actions/setup/js/send_otlp_span.test.cjs | 87 +++++++++++++++++++- pkg/parser/schemas/main_workflow_schema.json | 4 + pkg/workflow/frontmatter_types.go | 6 ++ pkg/workflow/observability_otlp.go | 10 +++ pkg/workflow/observability_otlp_test.go | 75 +++++++++++++++++ 6 files changed, 214 insertions(+), 2 deletions(-) diff --git a/actions/setup/js/send_otlp_span.cjs b/actions/setup/js/send_otlp_span.cjs index 70f50db038d..e835ba83229 100644 --- a/actions/setup/js/send_otlp_span.cjs +++ b/actions/setup/js/send_otlp_span.cjs @@ -123,6 +123,32 @@ function buildOTLPPayload({ traceId, spanId, spanName, startMs, endMs, serviceNa // HTTP transport // --------------------------------------------------------------------------- +/** + * Parse an `OTEL_EXPORTER_OTLP_HEADERS` value into a plain object suitable for + * merging into a `Headers` / `fetch` `headers` option. + * + * The value follows the OpenTelemetry specification: + * key=value[,key=value...] + * where individual keys and values may be percent-encoded. + * Empty pairs (from leading/trailing/consecutive commas) are silently skipped. + * + * @param {string} raw - Raw header string (e.g. "Authorization=Bearer tok,X-Tenant=acme") + * @returns {Record} Parsed headers object + */ +function parseOTLPHeaders(raw) { + if (!raw || !raw.trim()) return {}; + /** @type {Record} */ + const result = {}; + for (const pair of raw.split(",")) { + const eqIdx = pair.indexOf("="); + if (eqIdx <= 0) continue; // skip empty keys or malformed pairs + const key = decodeURIComponent(pair.slice(0, eqIdx).trim()); + const value = decodeURIComponent(pair.slice(eqIdx + 1).trim()); + if (key) result[key] = value; + } + return result; +} + /** * POST an OTLP traces payload to `{endpoint}/v1/traces` with automatic retries. * @@ -131,6 +157,9 @@ function buildOTLPPayload({ traceId, spanId, spanName, startMs, endMs, serviceNa * between attempts (100 ms, 200 ms) so the three total attempts finish in * well under a second in the typical success case. * + * Reads `OTEL_EXPORTER_OTLP_HEADERS` from the environment and merges any + * configured headers into every request. + * * @param {string} endpoint - OTLP base URL (e.g. https://traces.example.com:4317) * @param {object} payload - Serialisable OTLP JSON object * @param {{ maxRetries?: number, baseDelayMs?: number }} [opts] @@ -138,6 +167,8 @@ function buildOTLPPayload({ traceId, spanId, spanName, startMs, endMs, serviceNa */ async function sendOTLPSpan(endpoint, payload, { maxRetries = 2, baseDelayMs = 100 } = {}) { const url = endpoint.replace(/\/$/, "") + "/v1/traces"; + const extraHeaders = parseOTLPHeaders(process.env.OTEL_EXPORTER_OTLP_HEADERS || ""); + const headers = { "Content-Type": "application/json", ...extraHeaders }; for (let attempt = 0; attempt <= maxRetries; attempt++) { if (attempt > 0) { await new Promise(resolve => setTimeout(resolve, baseDelayMs * 2 ** (attempt - 1))); @@ -145,7 +176,7 @@ async function sendOTLPSpan(endpoint, payload, { maxRetries = 2, baseDelayMs = 1 try { const response = await fetch(url, { method: "POST", - headers: { "Content-Type": "application/json" }, + headers, body: JSON.stringify(payload), }); if (response.ok) { @@ -379,6 +410,7 @@ module.exports = { toNanoString, buildAttr, buildOTLPPayload, + parseOTLPHeaders, sendOTLPSpan, readJSONIfExists, sendJobSetupSpan, diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index 681529e2a93..cb22f29e54d 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -4,7 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; // Module import // --------------------------------------------------------------------------- -const { isValidTraceId, generateTraceId, generateSpanId, toNanoString, buildAttr, buildOTLPPayload, sendOTLPSpan, sendJobSetupSpan, sendJobConclusionSpan } = await import("./send_otlp_span.cjs"); +const { isValidTraceId, generateTraceId, generateSpanId, toNanoString, buildAttr, buildOTLPPayload, parseOTLPHeaders, sendOTLPSpan, sendJobSetupSpan, sendJobConclusionSpan } = await import("./send_otlp_span.cjs"); // --------------------------------------------------------------------------- // isValidTraceId @@ -251,6 +251,91 @@ describe("sendOTLPSpan", () => { }); }); +// --------------------------------------------------------------------------- +// parseOTLPHeaders +// --------------------------------------------------------------------------- + +describe("parseOTLPHeaders", () => { + it("returns empty object for empty/null/whitespace input", () => { + expect(parseOTLPHeaders("")).toEqual({}); + expect(parseOTLPHeaders(" ")).toEqual({}); + }); + + it("parses a single key=value pair", () => { + expect(parseOTLPHeaders("Authorization=Bearer mytoken")).toEqual({ Authorization: "Bearer mytoken" }); + }); + + it("parses multiple comma-separated key=value pairs", () => { + expect(parseOTLPHeaders("X-Tenant=acme,X-Region=us-east-1")).toEqual({ + "X-Tenant": "acme", + "X-Region": "us-east-1", + }); + }); + + it("handles percent-encoded values", () => { + expect(parseOTLPHeaders("Authorization=Bearer%20tok%3Dvalue")).toEqual({ Authorization: "Bearer tok=value" }); + }); + + it("handles values containing = signs (only first = is delimiter)", () => { + expect(parseOTLPHeaders("Authorization=Bearer base64==")).toEqual({ Authorization: "Bearer base64==" }); + }); + + it("skips malformed pairs with no =", () => { + const result = parseOTLPHeaders("Valid=value,malformedNoEquals"); + expect(result).toEqual({ Valid: "value" }); + }); + + it("skips pairs with empty key", () => { + const result = parseOTLPHeaders("=value,Good=ok"); + expect(result).toEqual({ Good: "ok" }); + }); +}); + +// --------------------------------------------------------------------------- +// sendOTLPSpan headers +// --------------------------------------------------------------------------- + +describe("sendOTLPSpan with OTEL_EXPORTER_OTLP_HEADERS", () => { + const savedHeaders = process.env.OTEL_EXPORTER_OTLP_HEADERS; + + beforeEach(() => { + vi.stubGlobal("fetch", vi.fn()); + delete process.env.OTEL_EXPORTER_OTLP_HEADERS; + }); + + afterEach(() => { + vi.unstubAllGlobals(); + if (savedHeaders !== undefined) { + process.env.OTEL_EXPORTER_OTLP_HEADERS = savedHeaders; + } else { + delete process.env.OTEL_EXPORTER_OTLP_HEADERS; + } + }); + + it("includes custom headers when OTEL_EXPORTER_OTLP_HEADERS is set", async () => { + const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" }); + vi.stubGlobal("fetch", mockFetch); + + process.env.OTEL_EXPORTER_OTLP_HEADERS = "Authorization=Bearer mytoken,X-Tenant=acme"; + await sendOTLPSpan("https://traces.example.com", {}); + + const [, init] = mockFetch.mock.calls[0]; + expect(init.headers["Authorization"]).toBe("Bearer mytoken"); + expect(init.headers["X-Tenant"]).toBe("acme"); + expect(init.headers["Content-Type"]).toBe("application/json"); + }); + + it("does not add extra headers when OTEL_EXPORTER_OTLP_HEADERS is absent", async () => { + const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" }); + vi.stubGlobal("fetch", mockFetch); + + await sendOTLPSpan("https://traces.example.com", {}); + + const [, init] = mockFetch.mock.calls[0]; + expect(Object.keys(init.headers)).toEqual(["Content-Type"]); + }); +}); + // --------------------------------------------------------------------------- // sendJobSetupSpan // --------------------------------------------------------------------------- diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 303db6db17d..e1080e20740 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -8336,6 +8336,10 @@ "endpoint": { "type": "string", "description": "OTLP collector endpoint URL (e.g. 'https://traces.example.com:4317'). Supports GitHub Actions expressions such as ${{ secrets.OTLP_ENDPOINT }}. When a static URL is provided, its hostname is automatically added to the network firewall allowlist." + }, + "headers": { + "type": "string", + "description": "Comma-separated list of key=value HTTP headers to include with every OTLP export request (e.g. 'Authorization=Bearer '). Supports GitHub Actions expressions such as ${{ secrets.OTLP_HEADERS }}. Injected as the OTEL_EXPORTER_OTLP_HEADERS environment variable." } }, "additionalProperties": false diff --git a/pkg/workflow/frontmatter_types.go b/pkg/workflow/frontmatter_types.go index ca8c9afa3c8..e3609f9f7ff 100644 --- a/pkg/workflow/frontmatter_types.go +++ b/pkg/workflow/frontmatter_types.go @@ -124,6 +124,12 @@ type OTLPConfig struct { // When a static URL is provided, its hostname is automatically added to the // network firewall allowlist. Endpoint string `json:"endpoint,omitempty"` + + // Headers is a comma-separated list of key=value HTTP headers to include with + // every OTLP export request (e.g. "Authorization=Bearer "). + // Supports GitHub Actions expressions such as ${{ secrets.OTLP_HEADERS }}. + // Injected as the standard OTEL_EXPORTER_OTLP_HEADERS environment variable. + Headers string `json:"headers,omitempty"` } // ObservabilityConfig represents workflow observability options. diff --git a/pkg/workflow/observability_otlp.go b/pkg/workflow/observability_otlp.go index f0af75dede7..c1463e59cde 100644 --- a/pkg/workflow/observability_otlp.go +++ b/pkg/workflow/observability_otlp.go @@ -77,6 +77,9 @@ func generateOTLPConclusionSpanStep(spanName string) string { // workflow-level env: YAML block (workflowData.Env) so they are available to // every step in the generated GitHub Actions workflow. // +// 3. When headers are configured, OTEL_EXPORTER_OTLP_HEADERS is also appended +// to the workflow-level env: block. +// // When no OTLP endpoint is configured the function is a no-op. func (c *Compiler) injectOTLPConfig(workflowData *WorkflowData) { endpoint := getOTLPEndpointEnvValue(workflowData.ParsedFrontmatter) @@ -97,6 +100,13 @@ func (c *Compiler) injectOTLPConfig(workflowData *WorkflowData) { // 2. Inject OTEL env vars into the workflow-level env: block. otlpEnvLines := fmt.Sprintf(" OTEL_EXPORTER_OTLP_ENDPOINT: %s\n OTEL_SERVICE_NAME: gh-aw", endpoint) + + // 3. Inject OTEL_EXPORTER_OTLP_HEADERS when configured. + if headers := workflowData.ParsedFrontmatter.Observability.OTLP.Headers; headers != "" { + otlpEnvLines += "\n OTEL_EXPORTER_OTLP_HEADERS: " + headers + otlpLog.Printf("Injected OTEL_EXPORTER_OTLP_HEADERS env var") + } + if workflowData.Env == "" { workflowData.Env = "env:\n" + otlpEnvLines } else { diff --git a/pkg/workflow/observability_otlp_test.go b/pkg/workflow/observability_otlp_test.go index 3e4535a1850..4fada70f060 100644 --- a/pkg/workflow/observability_otlp_test.go +++ b/pkg/workflow/observability_otlp_test.go @@ -247,6 +247,51 @@ func TestInjectOTLPConfig(t *testing.T) { c.injectOTLPConfig(wd) assert.Contains(t, wd.Env, "OTEL_SERVICE_NAME: gh-aw", "service name should always be gh-aw") }) + + t.Run("injects OTEL_EXPORTER_OTLP_HEADERS when headers are configured", func(t *testing.T) { + c := newCompiler() + wd := &WorkflowData{ + ParsedFrontmatter: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{ + Endpoint: "https://traces.example.com", + Headers: "Authorization=Bearer tok,X-Tenant=acme", + }, + }, + }, + } + c.injectOTLPConfig(wd) + assert.Contains(t, wd.Env, "OTEL_EXPORTER_OTLP_HEADERS: Authorization=Bearer tok,X-Tenant=acme", "headers var should be injected") + }) + + t.Run("injects OTEL_EXPORTER_OTLP_HEADERS for secret expression", func(t *testing.T) { + c := newCompiler() + wd := &WorkflowData{ + ParsedFrontmatter: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{ + Endpoint: "https://traces.example.com", + Headers: "${{ secrets.OTLP_HEADERS }}", + }, + }, + }, + } + c.injectOTLPConfig(wd) + assert.Contains(t, wd.Env, "OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTLP_HEADERS }}", "headers var should support secret expressions") + }) + + t.Run("does not inject OTEL_EXPORTER_OTLP_HEADERS when headers not configured", func(t *testing.T) { + c := newCompiler() + wd := &WorkflowData{ + ParsedFrontmatter: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{Endpoint: "https://traces.example.com"}, + }, + }, + } + c.injectOTLPConfig(wd) + assert.NotContains(t, wd.Env, "OTEL_EXPORTER_OTLP_HEADERS", "headers var should not appear when unconfigured") + }) } // TestObservabilityConfigParsing verifies that the OTLPConfig is correctly parsed @@ -257,6 +302,7 @@ func TestObservabilityConfigParsing(t *testing.T) { frontmatter map[string]any wantOTLPConfig bool expectedEndpoint string + expectedHeaders string }{ { name: "no observability section", @@ -309,6 +355,34 @@ func TestObservabilityConfigParsing(t *testing.T) { wantOTLPConfig: true, expectedEndpoint: "https://traces.example.com", }, + { + name: "observability with otlp endpoint and headers", + frontmatter: map[string]any{ + "observability": map[string]any{ + "otlp": map[string]any{ + "endpoint": "https://traces.example.com", + "headers": "Authorization=Bearer tok,X-Tenant=acme", + }, + }, + }, + wantOTLPConfig: true, + expectedEndpoint: "https://traces.example.com", + expectedHeaders: "Authorization=Bearer tok,X-Tenant=acme", + }, + { + name: "observability with otlp headers as secret expression", + frontmatter: map[string]any{ + "observability": map[string]any{ + "otlp": map[string]any{ + "endpoint": "https://traces.example.com", + "headers": "${{ secrets.OTLP_HEADERS }}", + }, + }, + }, + wantOTLPConfig: true, + expectedEndpoint: "https://traces.example.com", + expectedHeaders: "${{ secrets.OTLP_HEADERS }}", + }, } for _, tt := range tests { @@ -327,6 +401,7 @@ func TestObservabilityConfigParsing(t *testing.T) { require.NotNil(t, config.Observability, "Observability should not be nil") require.NotNil(t, config.Observability.OTLP, "OTLP should not be nil") assert.Equal(t, tt.expectedEndpoint, config.Observability.OTLP.Endpoint, "Endpoint should match") + assert.Equal(t, tt.expectedHeaders, config.Observability.OTLP.Headers, "Headers should match") }) } } From d3191e0964c101a51f9738692dfd1230a0077641 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 07:46:50 +0000 Subject: [PATCH 10/16] fix: decode before trim in parseOTLPHeaders, clarify eqIdx comment Agent-Logs-Url: https://github.com/github/gh-aw/sessions/8f5dc8d2-75f6-4186-affe-e329b01b1aaf Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/send_otlp_span.cjs | 7 ++++--- actions/setup/js/send_otlp_span.test.cjs | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/actions/setup/js/send_otlp_span.cjs b/actions/setup/js/send_otlp_span.cjs index e835ba83229..bf3f0655bd1 100644 --- a/actions/setup/js/send_otlp_span.cjs +++ b/actions/setup/js/send_otlp_span.cjs @@ -141,9 +141,10 @@ function parseOTLPHeaders(raw) { const result = {}; for (const pair of raw.split(",")) { const eqIdx = pair.indexOf("="); - if (eqIdx <= 0) continue; // skip empty keys or malformed pairs - const key = decodeURIComponent(pair.slice(0, eqIdx).trim()); - const value = decodeURIComponent(pair.slice(eqIdx + 1).trim()); + if (eqIdx <= 0) continue; // skip malformed pairs (no =) or empty keys (= at start) + // Decode before trimming so percent-encoded whitespace (%20) at edges is preserved correctly. + const key = decodeURIComponent(pair.slice(0, eqIdx)).trim(); + const value = decodeURIComponent(pair.slice(eqIdx + 1)).trim(); if (key) result[key] = value; } return result; diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index cb22f29e54d..7cbc771c624 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -276,6 +276,11 @@ describe("parseOTLPHeaders", () => { expect(parseOTLPHeaders("Authorization=Bearer%20tok%3Dvalue")).toEqual({ Authorization: "Bearer tok=value" }); }); + it("decodes before trimming so encoded whitespace at edges is preserved", () => { + // %20 at start/end of value should survive: decode first, then trim removes nothing + expect(parseOTLPHeaders("X-Token=abc%20def")).toEqual({ "X-Token": "abc def" }); + }); + it("handles values containing = signs (only first = is delimiter)", () => { expect(parseOTLPHeaders("Authorization=Bearer base64==")).toEqual({ Authorization: "Bearer base64==" }); }); From 073a79efbb76b2b112bad0d35b14ebeb55856d7c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:26:06 +0000 Subject: [PATCH 11/16] feat: propagate trace/span IDs via GITHUB_ENV for 1-trace-per-run, 1-parent-span-per-job Agent-Logs-Url: https://github.com/github/gh-aw/sessions/95b6d36f-8ffa-4b60-8d50-426464993eec Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/index.js | 21 ++- actions/setup/js/send_otlp_span.cjs | 72 ++++++++--- actions/setup/js/send_otlp_span.test.cjs | 155 ++++++++++++++++++++--- 3 files changed, 205 insertions(+), 43 deletions(-) diff --git a/actions/setup/index.js b/actions/setup/index.js index e71b8f47aff..e9f2de948a8 100644 --- a/actions/setup/index.js +++ b/actions/setup/index.js @@ -39,14 +39,25 @@ if (result.status !== 0) { (async () => { try { const { appendFileSync } = require("fs"); - const { isValidTraceId, sendJobSetupSpan } = require(path.join(__dirname, "js", "send_otlp_span.cjs")); - const traceId = await sendJobSetupSpan({ startMs: setupStartMs }); - // Always expose the trace ID as an action output so downstream jobs can - // reference it via `steps..outputs.trace-id` and pass it to their own - // setup steps to correlate all job spans under a single trace. + const { isValidTraceId, isValidSpanId, sendJobSetupSpan } = require(path.join(__dirname, "js", "send_otlp_span.cjs")); + const { traceId, spanId } = await sendJobSetupSpan({ startMs: setupStartMs }); + // Expose the trace ID as an action output so downstream jobs can reference it + // via `steps..outputs.trace-id` for cross-job trace correlation. if (isValidTraceId(traceId) && process.env.GITHUB_OUTPUT) { appendFileSync(process.env.GITHUB_OUTPUT, `trace-id=${traceId}\n`); } + // Write both the trace ID and setup span ID to GITHUB_ENV so all subsequent + // steps in this job automatically inherit the parent trace context: + // GH_AW_TRACE_ID – shared trace ID (1 trace per run) + // GH_AW_PARENT_SPAN_ID – setup span ID used as parent (1 parent span per job) + if (process.env.GITHUB_ENV) { + if (isValidTraceId(traceId)) { + appendFileSync(process.env.GITHUB_ENV, `GH_AW_TRACE_ID=${traceId}\n`); + } + if (isValidSpanId(spanId)) { + appendFileSync(process.env.GITHUB_ENV, `GH_AW_PARENT_SPAN_ID=${spanId}\n`); + } + } } catch { // Non-fatal: silently ignore any OTLP export or output-write errors. } diff --git a/actions/setup/js/send_otlp_span.cjs b/actions/setup/js/send_otlp_span.cjs index bf3f0655bd1..56e3f90f543 100644 --- a/actions/setup/js/send_otlp_span.cjs +++ b/actions/setup/js/send_otlp_span.cjs @@ -74,13 +74,14 @@ function buildAttr(key, value) { /** * @typedef {Object} OTLPSpanOptions - * @property {string} traceId - 32-char hex trace ID - * @property {string} spanId - 16-char hex span ID - * @property {string} spanName - Human-readable span name - * @property {number} startMs - Span start time (ms since epoch) - * @property {number} endMs - Span end time (ms since epoch) - * @property {string} serviceName - Value for the service.name resource attribute - * @property {string} [scopeVersion] - gh-aw version string (e.g. from GH_AW_INFO_VERSION) + * @property {string} traceId - 32-char hex trace ID + * @property {string} spanId - 16-char hex span ID + * @property {string} [parentSpanId] - 16-char hex parent span ID; omitted for root spans + * @property {string} spanName - Human-readable span name + * @property {number} startMs - Span start time (ms since epoch) + * @property {number} endMs - Span end time (ms since epoch) + * @property {string} serviceName - Value for the service.name resource attribute + * @property {string} [scopeVersion] - gh-aw version string (e.g. from GH_AW_INFO_VERSION) * @property {Array<{key: string, value: object}>} attributes - Span attributes */ @@ -90,7 +91,7 @@ function buildAttr(key, value) { * @param {OTLPSpanOptions} opts * @returns {object} - Ready to be serialised as JSON and POSTed to `/v1/traces` */ -function buildOTLPPayload({ traceId, spanId, spanName, startMs, endMs, serviceName, scopeVersion, attributes }) { +function buildOTLPPayload({ traceId, spanId, parentSpanId, spanName, startMs, endMs, serviceName, scopeVersion, attributes }) { return { resourceSpans: [ { @@ -104,6 +105,7 @@ function buildOTLPPayload({ traceId, spanId, spanName, startMs, endMs, serviceNa { traceId, spanId, + ...(parentSpanId ? { parentSpanId } : {}), name: spanName, kind: 2, // SPAN_KIND_SERVER startTimeUnixNano: toNanoString(startMs), @@ -219,6 +221,21 @@ function isValidTraceId(id) { return TRACE_ID_RE.test(id); } +/** + * Regular expression that matches a valid OTLP span ID: 16 lowercase hex characters. + * @type {RegExp} + */ +const SPAN_ID_RE = /^[0-9a-f]{16}$/; + +/** + * Validate that a string is a well-formed OTLP span ID (16 lowercase hex chars). + * @param {string} id + * @returns {boolean} + */ +function isValidSpanId(id) { + return SPAN_ID_RE.test(id); +} + /** * @typedef {Object} SendJobSetupSpanOptions * @property {number} [startMs] - Override for the span start time (ms). Defaults to `Date.now()`. @@ -233,9 +250,10 @@ function isValidTraceId(id) { * Send a `gh-aw.job.setup` span 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 the trace ID so callers can - * expose it as an action output for cross-job correlation — even when - * `OTEL_EXPORTER_OTLP_ENDPOINT` is not set (no span is sent in that case). + * the setup script completes. It always returns `{ traceId, spanId }` so callers + * can expose the trace ID as an action output and write both values to `$GITHUB_ENV` + * for downstream step correlation — even when `OTEL_EXPORTER_OTLP_ENDPOINT` is not + * set (no span is sent in that case). * Errors are swallowed so the workflow is never broken by tracing failures. * * Environment variables consumed: @@ -250,7 +268,7 @@ function isValidTraceId(id) { * - `GITHUB_REPOSITORY` – `owner/repo` string * * @param {SendJobSetupSpanOptions} [options] - * @returns {Promise} The trace ID used for the span (generated or passed in). + * @returns {Promise<{ traceId: string, spanId: string }>} The trace and span IDs used. */ async function sendJobSetupSpan(options = {}) { // Resolve the trace ID before the early-return so it is always available as @@ -268,9 +286,14 @@ async function sendJobSetupSpan(options = {}) { const traceId = optionsTraceId || inputTraceId || generateTraceId(); + // Always generate a span ID so it can be written to GITHUB_ENV as + // GH_AW_PARENT_SPAN_ID even when OTLP is not configured, allowing downstream + // scripts to establish the correct parent span context. + const spanId = generateSpanId(); + const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || ""; if (!endpoint) { - return traceId; + return { traceId, spanId }; } const startMs = options.startMs ?? Date.now(); @@ -292,7 +315,7 @@ async function sendJobSetupSpan(options = {}) { const payload = buildOTLPPayload({ traceId, - spanId: generateSpanId(), + spanId, spanName: "gh-aw.job.setup", startMs, endMs, @@ -302,7 +325,7 @@ async function sendJobSetupSpan(options = {}) { }); await sendOTLPSpan(endpoint, payload); - return traceId; + return { traceId, spanId }; } // --------------------------------------------------------------------------- @@ -340,6 +363,10 @@ function readJSONIfExists(filePath) { * - `OTEL_EXPORTER_OTLP_ENDPOINT` – collector endpoint * - `OTEL_SERVICE_NAME` – service name (defaults to "gh-aw") * - `GH_AW_EFFECTIVE_TOKENS` – total effective token count for the run + * - `GH_AW_TRACE_ID` – trace ID written to GITHUB_ENV by the setup step; + * enables 1-trace-per-run when present + * - `GH_AW_PARENT_SPAN_ID` – setup span ID written to GITHUB_ENV by the setup step; + * links this span as a child of the job setup span * - `GITHUB_RUN_ID` – GitHub Actions run ID * - `GITHUB_ACTOR` – GitHub Actions actor * - `GITHUB_REPOSITORY` – `owner/repo` string @@ -370,10 +397,17 @@ async function sendJobConclusionSpan(spanName, options = {}) { const serviceName = process.env.OTEL_SERVICE_NAME || "gh-aw"; const version = awInfo.agent_version || awInfo.version || process.env.GH_AW_INFO_VERSION || "unknown"; - // Use the workflow_call_id from aw_info as the trace ID when available so that - // conclusion spans can be correlated with the activation span. + // Prefer GH_AW_TRACE_ID (written to GITHUB_ENV by this job's setup step) so + // all spans in the same job share one trace. Fall back to the workflow_call_id + // from aw_info for cross-job correlation, then generate a fresh ID. + const envTraceId = (process.env.GH_AW_TRACE_ID || "").trim().toLowerCase(); const awTraceId = typeof awInfo.context?.workflow_call_id === "string" ? awInfo.context.workflow_call_id.replace(/-/g, "") : ""; - const traceId = awTraceId && isValidTraceId(awTraceId) ? awTraceId : generateTraceId(); + const traceId = (isValidTraceId(envTraceId) ? envTraceId : null) || (awTraceId && isValidTraceId(awTraceId) ? awTraceId : null) || generateTraceId(); + + // Use GH_AW_PARENT_SPAN_ID (written to GITHUB_ENV by this job's setup step) so + // conclusion spans are linked as children of the setup span (1 parent span per job). + const rawParentSpanId = (process.env.GH_AW_PARENT_SPAN_ID || "").trim().toLowerCase(); + const parentSpanId = isValidSpanId(rawParentSpanId) ? rawParentSpanId : ""; const workflowName = awInfo.workflow_name || ""; const engineId = awInfo.engine_id || ""; @@ -393,6 +427,7 @@ async function sendJobConclusionSpan(spanName, options = {}) { const payload = buildOTLPPayload({ traceId, spanId: generateSpanId(), + ...(parentSpanId ? { parentSpanId } : {}), spanName, startMs, endMs: Date.now(), @@ -406,6 +441,7 @@ async function sendJobConclusionSpan(spanName, options = {}) { module.exports = { isValidTraceId, + isValidSpanId, generateTraceId, generateSpanId, toNanoString, diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index 7cbc771c624..d82ee213f87 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -4,7 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; // Module import // --------------------------------------------------------------------------- -const { isValidTraceId, generateTraceId, generateSpanId, toNanoString, buildAttr, buildOTLPPayload, parseOTLPHeaders, sendOTLPSpan, sendJobSetupSpan, sendJobConclusionSpan } = await import("./send_otlp_span.cjs"); +const { isValidTraceId, isValidSpanId, generateTraceId, generateSpanId, toNanoString, buildAttr, buildOTLPPayload, parseOTLPHeaders, sendOTLPSpan, sendJobSetupSpan, sendJobConclusionSpan } = await import("./send_otlp_span.cjs"); // --------------------------------------------------------------------------- // isValidTraceId @@ -34,6 +34,34 @@ describe("isValidTraceId", () => { }); }); +// --------------------------------------------------------------------------- +// isValidSpanId +// --------------------------------------------------------------------------- + +describe("isValidSpanId", () => { + it("accepts a valid 16-character lowercase hex span ID", () => { + expect(isValidSpanId("b".repeat(16))).toBe(true); + expect(isValidSpanId("0123456789abcdef")).toBe(true); + }); + + it("rejects uppercase hex characters", () => { + expect(isValidSpanId("B".repeat(16))).toBe(false); + }); + + it("rejects strings that are too short or too long", () => { + expect(isValidSpanId("b".repeat(15))).toBe(false); + expect(isValidSpanId("b".repeat(17))).toBe(false); + }); + + it("rejects empty string", () => { + expect(isValidSpanId("")).toBe(false); + }); + + it("rejects non-hex characters", () => { + expect(isValidSpanId("z".repeat(16))).toBe(false); + }); +}); + // --------------------------------------------------------------------------- // generateTraceId // --------------------------------------------------------------------------- @@ -167,6 +195,35 @@ describe("buildOTLPPayload", () => { }); expect(payload.resourceSpans[0].scopeSpans[0].scope.version).toBe("unknown"); }); + + it("includes parentSpanId in span when provided", () => { + const payload = buildOTLPPayload({ + traceId: "a".repeat(32), + spanId: "b".repeat(16), + parentSpanId: "c".repeat(16), + spanName: "test", + startMs: 0, + endMs: 1, + serviceName: "gh-aw", + attributes: [], + }); + const span = payload.resourceSpans[0].scopeSpans[0].spans[0]; + expect(span.parentSpanId).toBe("c".repeat(16)); + }); + + it("omits parentSpanId from span when not provided", () => { + const payload = buildOTLPPayload({ + traceId: "a".repeat(32), + spanId: "b".repeat(16), + spanName: "test", + startMs: 0, + endMs: 1, + serviceName: "gh-aw", + attributes: [], + }); + const span = payload.resourceSpans[0].scopeSpans[0].spans[0]; + expect(span.parentSpanId).toBeUndefined(); + }); }); // --------------------------------------------------------------------------- @@ -383,22 +440,23 @@ describe("sendJobSetupSpan", () => { return undefined; } - it("returns a trace ID even when OTEL_EXPORTER_OTLP_ENDPOINT is not set", async () => { - const traceId = await sendJobSetupSpan(); + it("returns a trace ID and span ID even when OTEL_EXPORTER_OTLP_ENDPOINT is not set", async () => { + const { traceId, spanId } = await sendJobSetupSpan(); expect(traceId).toMatch(/^[0-9a-f]{32}$/); + expect(spanId).toMatch(/^[0-9a-f]{16}$/); expect(fetch).not.toHaveBeenCalled(); }); it("returns the same trace ID when called with INPUT_TRACE_ID and no endpoint", async () => { process.env.INPUT_TRACE_ID = "a".repeat(32); - const traceId = await sendJobSetupSpan(); + const { traceId } = await sendJobSetupSpan(); expect(traceId).toBe("a".repeat(32)); expect(fetch).not.toHaveBeenCalled(); }); it("generates a new trace ID when INPUT_TRACE_ID is invalid", async () => { process.env.INPUT_TRACE_ID = "not-a-valid-trace-id"; - const traceId = await sendJobSetupSpan(); + const { traceId } = await sendJobSetupSpan(); expect(traceId).toMatch(/^[0-9a-f]{32}$/); expect(traceId).not.toBe("not-a-valid-trace-id"); }); @@ -406,17 +464,17 @@ describe("sendJobSetupSpan", () => { it("normalises uppercase INPUT_TRACE_ID to lowercase and accepts it", async () => { // Trace IDs pasted from external tools may be uppercase; we normalise them. process.env.INPUT_TRACE_ID = "A".repeat(32); - const traceId = await sendJobSetupSpan(); + const { traceId } = await sendJobSetupSpan(); expect(traceId).toBe("a".repeat(32)); }); it("rejects an invalid options.traceId and generates a new trace ID", async () => { - const returned = await sendJobSetupSpan({ traceId: "too-short" }); - expect(returned).toMatch(/^[0-9a-f]{32}$/); - expect(returned).not.toBe("too-short"); + const { traceId } = await sendJobSetupSpan({ traceId: "too-short" }); + expect(traceId).toMatch(/^[0-9a-f]{32}$/); + expect(traceId).not.toBe("too-short"); }); - it("sends a span when endpoint is configured and returns the trace ID", async () => { + it("sends a span when endpoint is configured and returns the trace ID and span ID", async () => { const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" }); vi.stubGlobal("fetch", mockFetch); @@ -428,9 +486,10 @@ describe("sendJobSetupSpan", () => { process.env.GITHUB_ACTOR = "octocat"; process.env.GITHUB_REPOSITORY = "owner/repo"; - const traceId = await sendJobSetupSpan(); + const { traceId, spanId } = await sendJobSetupSpan(); expect(traceId).toMatch(/^[0-9a-f]{32}$/); + expect(spanId).toMatch(/^[0-9a-f]{16}$/); expect(mockFetch).toHaveBeenCalledOnce(); const [url, init] = mockFetch.mock.calls[0]; expect(url).toBe("https://traces.example.com/v1/traces"); @@ -439,9 +498,9 @@ describe("sendJobSetupSpan", () => { const body = JSON.parse(init.body); const span = body.resourceSpans[0].scopeSpans[0].spans[0]; expect(span.name).toBe("gh-aw.job.setup"); - // Span traceId must match the returned value (cross-job correlation) + // Span traceId and spanId must match the returned values expect(span.traceId).toBe(traceId); - expect(span.spanId).toMatch(/^[0-9a-f]{16}$/); + expect(span.spanId).toBe(spanId); const attrs = Object.fromEntries(span.attributes.map(a => [a.key, attrValue(a)])); expect(attrs["gh-aw.job.name"]).toBe("agent"); @@ -459,9 +518,9 @@ describe("sendJobSetupSpan", () => { process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "https://traces.example.com"; const correlationTraceId = "b".repeat(32); - const returned = await sendJobSetupSpan({ traceId: correlationTraceId }); + const { traceId } = await sendJobSetupSpan({ traceId: correlationTraceId }); - expect(returned).toBe(correlationTraceId); + expect(traceId).toBe(correlationTraceId); const body = JSON.parse(mockFetch.mock.calls[0][1].body); expect(body.resourceSpans[0].scopeSpans[0].spans[0].traceId).toBe(correlationTraceId); }); @@ -473,9 +532,9 @@ describe("sendJobSetupSpan", () => { process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "https://traces.example.com"; process.env.INPUT_TRACE_ID = "c".repeat(32); - const returned = await sendJobSetupSpan(); + const { traceId } = await sendJobSetupSpan(); - expect(returned).toBe("c".repeat(32)); + expect(traceId).toBe("c".repeat(32)); const body = JSON.parse(mockFetch.mock.calls[0][1].body); expect(body.resourceSpans[0].scopeSpans[0].spans[0].traceId).toBe("c".repeat(32)); }); @@ -487,9 +546,9 @@ describe("sendJobSetupSpan", () => { process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "https://traces.example.com"; process.env.INPUT_TRACE_ID = "d".repeat(32); - const returned = await sendJobSetupSpan({ traceId: "e".repeat(32) }); + const { traceId } = await sendJobSetupSpan({ traceId: "e".repeat(32) }); - expect(returned).toBe("e".repeat(32)); + expect(traceId).toBe("e".repeat(32)); const body = JSON.parse(mockFetch.mock.calls[0][1].body); expect(body.resourceSpans[0].scopeSpans[0].spans[0].traceId).toBe("e".repeat(32)); }); @@ -543,7 +602,7 @@ describe("sendJobSetupSpan", () => { describe("sendJobConclusionSpan", () => { /** @type {Record} */ const savedEnv = {}; - const envKeys = ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_SERVICE_NAME", "GH_AW_EFFECTIVE_TOKENS", "GH_AW_INFO_VERSION", "GITHUB_RUN_ID", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]; + const envKeys = ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_SERVICE_NAME", "GH_AW_EFFECTIVE_TOKENS", "GH_AW_INFO_VERSION", "GH_AW_TRACE_ID", "GH_AW_PARENT_SPAN_ID", "GITHUB_RUN_ID", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]; beforeEach(() => { vi.stubGlobal("fetch", vi.fn()); @@ -630,4 +689,60 @@ describe("sendJobConclusionSpan", () => { const body = JSON.parse(mockFetch.mock.calls[0][1].body); expect(body.resourceSpans[0].scopeSpans[0].scope.version).toBe("v2.0.0"); }); + + it("uses GH_AW_TRACE_ID from env as trace ID (1 trace per run)", 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_TRACE_ID = "f".repeat(32); + + 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]; + expect(span.traceId).toBe("f".repeat(32)); + }); + + it("uses GH_AW_PARENT_SPAN_ID as parentSpanId (1 parent span per job)", 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"; + const parentSpanId = "abcdef1234567890"; + process.env.GH_AW_PARENT_SPAN_ID = parentSpanId; + + 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]; + expect(span.parentSpanId).toBe(parentSpanId); + }); + + it("omits parentSpanId when GH_AW_PARENT_SPAN_ID 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"; + + 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]; + expect(span.parentSpanId).toBeUndefined(); + }); + + it("normalises uppercase GH_AW_TRACE_ID to lowercase", 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_TRACE_ID = "F".repeat(32); // uppercase — should be normalised + + 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]; + expect(span.traceId).toBe("f".repeat(32)); + }); }); From 4f094e4e5476d49831224d319fb0582f25a9ccfb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:30:12 +0000 Subject: [PATCH 12/16] fix: simplify traceId resolution logic, normalize spelling to American English Agent-Logs-Url: https://github.com/github/gh-aw/sessions/95b6d36f-8ffa-4b60-8d50-426464993eec Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/send_otlp_span.cjs | 9 +++++++-- actions/setup/js/send_otlp_span.test.cjs | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/actions/setup/js/send_otlp_span.cjs b/actions/setup/js/send_otlp_span.cjs index 56e3f90f543..af5b195b87a 100644 --- a/actions/setup/js/send_otlp_span.cjs +++ b/actions/setup/js/send_otlp_span.cjs @@ -279,7 +279,7 @@ async function sendJobSetupSpan(options = {}) { // Validate options.traceId if supplied; callers may pass raw user input. const optionsTraceId = options.traceId && isValidTraceId(options.traceId) ? options.traceId : ""; - // Normalise INPUT_TRACE_ID to lowercase before validating: OTLP requires lowercase + // Normalize INPUT_TRACE_ID to lowercase before validating: OTLP requires lowercase // hex, but trace IDs pasted from external tools may use uppercase characters. const rawInputTraceId = (process.env.INPUT_TRACE_ID || "").trim().toLowerCase(); const inputTraceId = isValidTraceId(rawInputTraceId) ? rawInputTraceId : ""; @@ -402,7 +402,12 @@ async function sendJobConclusionSpan(spanName, options = {}) { // from aw_info for cross-job correlation, then generate a fresh ID. const envTraceId = (process.env.GH_AW_TRACE_ID || "").trim().toLowerCase(); const awTraceId = typeof awInfo.context?.workflow_call_id === "string" ? awInfo.context.workflow_call_id.replace(/-/g, "") : ""; - const traceId = (isValidTraceId(envTraceId) ? envTraceId : null) || (awTraceId && isValidTraceId(awTraceId) ? awTraceId : null) || generateTraceId(); + let traceId = generateTraceId(); + if (isValidTraceId(envTraceId)) { + traceId = envTraceId; + } else if (awTraceId && isValidTraceId(awTraceId)) { + traceId = awTraceId; + } // Use GH_AW_PARENT_SPAN_ID (written to GITHUB_ENV by this job's setup step) so // conclusion spans are linked as children of the setup span (1 parent span per job). diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index d82ee213f87..6d9b944246e 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -461,7 +461,7 @@ describe("sendJobSetupSpan", () => { expect(traceId).not.toBe("not-a-valid-trace-id"); }); - it("normalises uppercase INPUT_TRACE_ID to lowercase and accepts it", async () => { + it("normalizes uppercase INPUT_TRACE_ID to lowercase and accepts it", async () => { // Trace IDs pasted from external tools may be uppercase; we normalise them. process.env.INPUT_TRACE_ID = "A".repeat(32); const { traceId } = await sendJobSetupSpan(); @@ -732,7 +732,7 @@ describe("sendJobConclusionSpan", () => { expect(span.parentSpanId).toBeUndefined(); }); - it("normalises uppercase GH_AW_TRACE_ID to lowercase", async () => { + it("normalizes uppercase GH_AW_TRACE_ID to lowercase", async () => { const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" }); vi.stubGlobal("fetch", mockFetch); From e3ae9139f8175f8d6901e84813f32e4b2969f339 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:43:40 +0000 Subject: [PATCH 13/16] refactor: rename GH_AW_TRACE_ID/GH_AW_PARENT_SPAN_ID to GITHUB_AW_OTEL_ prefix Agent-Logs-Url: https://github.com/github/gh-aw/sessions/9fa99f5e-6d91-46ad-8cf4-797227c2651c Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/index.js | 8 ++++---- actions/setup/js/send_otlp_span.cjs | 14 +++++++------- actions/setup/js/send_otlp_span.test.cjs | 16 ++++++++-------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/actions/setup/index.js b/actions/setup/index.js index e9f2de948a8..a0f377a28a9 100644 --- a/actions/setup/index.js +++ b/actions/setup/index.js @@ -48,14 +48,14 @@ if (result.status !== 0) { } // Write both the trace ID and setup span ID to GITHUB_ENV so all subsequent // steps in this job automatically inherit the parent trace context: - // GH_AW_TRACE_ID – shared trace ID (1 trace per run) - // GH_AW_PARENT_SPAN_ID – setup span ID used as parent (1 parent span per job) + // GITHUB_AW_OTEL_TRACE_ID – shared trace ID (1 trace per run) + // GITHUB_AW_OTEL_PARENT_SPAN_ID – setup span ID used as parent (1 parent span per job) if (process.env.GITHUB_ENV) { if (isValidTraceId(traceId)) { - appendFileSync(process.env.GITHUB_ENV, `GH_AW_TRACE_ID=${traceId}\n`); + appendFileSync(process.env.GITHUB_ENV, `GITHUB_AW_OTEL_TRACE_ID=${traceId}\n`); } if (isValidSpanId(spanId)) { - appendFileSync(process.env.GITHUB_ENV, `GH_AW_PARENT_SPAN_ID=${spanId}\n`); + appendFileSync(process.env.GITHUB_ENV, `GITHUB_AW_OTEL_PARENT_SPAN_ID=${spanId}\n`); } } } catch { diff --git a/actions/setup/js/send_otlp_span.cjs b/actions/setup/js/send_otlp_span.cjs index af5b195b87a..d5f9905d976 100644 --- a/actions/setup/js/send_otlp_span.cjs +++ b/actions/setup/js/send_otlp_span.cjs @@ -287,7 +287,7 @@ async function sendJobSetupSpan(options = {}) { const traceId = optionsTraceId || inputTraceId || generateTraceId(); // Always generate a span ID so it can be written to GITHUB_ENV as - // GH_AW_PARENT_SPAN_ID even when OTLP is not configured, allowing downstream + // GITHUB_AW_OTEL_PARENT_SPAN_ID even when OTLP is not configured, allowing downstream // scripts to establish the correct parent span context. const spanId = generateSpanId(); @@ -363,9 +363,9 @@ function readJSONIfExists(filePath) { * - `OTEL_EXPORTER_OTLP_ENDPOINT` – collector endpoint * - `OTEL_SERVICE_NAME` – service name (defaults to "gh-aw") * - `GH_AW_EFFECTIVE_TOKENS` – total effective token count for the run - * - `GH_AW_TRACE_ID` – trace ID written to GITHUB_ENV by the setup step; + * - `GITHUB_AW_OTEL_TRACE_ID` – trace ID written to GITHUB_ENV by the setup step; * enables 1-trace-per-run when present - * - `GH_AW_PARENT_SPAN_ID` – setup span ID written to GITHUB_ENV by the setup step; + * - `GITHUB_AW_OTEL_PARENT_SPAN_ID` – setup span ID written to GITHUB_ENV by the setup step; * links this span as a child of the job setup span * - `GITHUB_RUN_ID` – GitHub Actions run ID * - `GITHUB_ACTOR` – GitHub Actions actor @@ -397,10 +397,10 @@ async function sendJobConclusionSpan(spanName, options = {}) { const serviceName = process.env.OTEL_SERVICE_NAME || "gh-aw"; const version = awInfo.agent_version || awInfo.version || process.env.GH_AW_INFO_VERSION || "unknown"; - // Prefer GH_AW_TRACE_ID (written to GITHUB_ENV by this job's setup step) so + // Prefer GITHUB_AW_OTEL_TRACE_ID (written to GITHUB_ENV by this job's setup step) so // all spans in the same job share one trace. Fall back to the workflow_call_id // from aw_info for cross-job correlation, then generate a fresh ID. - const envTraceId = (process.env.GH_AW_TRACE_ID || "").trim().toLowerCase(); + const envTraceId = (process.env.GITHUB_AW_OTEL_TRACE_ID || "").trim().toLowerCase(); const awTraceId = typeof awInfo.context?.workflow_call_id === "string" ? awInfo.context.workflow_call_id.replace(/-/g, "") : ""; let traceId = generateTraceId(); if (isValidTraceId(envTraceId)) { @@ -409,9 +409,9 @@ async function sendJobConclusionSpan(spanName, options = {}) { traceId = awTraceId; } - // Use GH_AW_PARENT_SPAN_ID (written to GITHUB_ENV by this job's setup step) so + // Use GITHUB_AW_OTEL_PARENT_SPAN_ID (written to GITHUB_ENV by this job's setup step) so // conclusion spans are linked as children of the setup span (1 parent span per job). - const rawParentSpanId = (process.env.GH_AW_PARENT_SPAN_ID || "").trim().toLowerCase(); + const rawParentSpanId = (process.env.GITHUB_AW_OTEL_PARENT_SPAN_ID || "").trim().toLowerCase(); const parentSpanId = isValidSpanId(rawParentSpanId) ? rawParentSpanId : ""; const workflowName = awInfo.workflow_name || ""; diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index 6d9b944246e..d0957047419 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -602,7 +602,7 @@ describe("sendJobSetupSpan", () => { describe("sendJobConclusionSpan", () => { /** @type {Record} */ const savedEnv = {}; - const envKeys = ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_SERVICE_NAME", "GH_AW_EFFECTIVE_TOKENS", "GH_AW_INFO_VERSION", "GH_AW_TRACE_ID", "GH_AW_PARENT_SPAN_ID", "GITHUB_RUN_ID", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]; + const envKeys = ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_SERVICE_NAME", "GH_AW_EFFECTIVE_TOKENS", "GH_AW_INFO_VERSION", "GITHUB_AW_OTEL_TRACE_ID", "GITHUB_AW_OTEL_PARENT_SPAN_ID", "GITHUB_RUN_ID", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]; beforeEach(() => { vi.stubGlobal("fetch", vi.fn()); @@ -690,12 +690,12 @@ describe("sendJobConclusionSpan", () => { expect(body.resourceSpans[0].scopeSpans[0].scope.version).toBe("v2.0.0"); }); - it("uses GH_AW_TRACE_ID from env as trace ID (1 trace per run)", async () => { + it("uses GITHUB_AW_OTEL_TRACE_ID from env as trace ID (1 trace per run)", 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_TRACE_ID = "f".repeat(32); + process.env.GITHUB_AW_OTEL_TRACE_ID = "f".repeat(32); await sendJobConclusionSpan("gh-aw.job.conclusion"); @@ -704,13 +704,13 @@ describe("sendJobConclusionSpan", () => { expect(span.traceId).toBe("f".repeat(32)); }); - it("uses GH_AW_PARENT_SPAN_ID as parentSpanId (1 parent span per job)", async () => { + it("uses GITHUB_AW_OTEL_PARENT_SPAN_ID as parentSpanId (1 parent span per job)", 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"; const parentSpanId = "abcdef1234567890"; - process.env.GH_AW_PARENT_SPAN_ID = parentSpanId; + process.env.GITHUB_AW_OTEL_PARENT_SPAN_ID = parentSpanId; await sendJobConclusionSpan("gh-aw.job.conclusion"); @@ -719,7 +719,7 @@ describe("sendJobConclusionSpan", () => { expect(span.parentSpanId).toBe(parentSpanId); }); - it("omits parentSpanId when GH_AW_PARENT_SPAN_ID is absent", async () => { + it("omits parentSpanId when GITHUB_AW_OTEL_PARENT_SPAN_ID is absent", async () => { const mockFetch = vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" }); vi.stubGlobal("fetch", mockFetch); @@ -732,12 +732,12 @@ describe("sendJobConclusionSpan", () => { expect(span.parentSpanId).toBeUndefined(); }); - it("normalizes uppercase GH_AW_TRACE_ID to lowercase", async () => { + it("normalizes uppercase GITHUB_AW_OTEL_TRACE_ID to lowercase", 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_TRACE_ID = "F".repeat(32); // uppercase — should be normalised + process.env.GITHUB_AW_OTEL_TRACE_ID = "F".repeat(32); // uppercase — should be normalised await sendJobConclusionSpan("gh-aw.job.conclusion"); From a361f5c16ac46b56cdbfcf2bdd76b09cdee714d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:53:53 +0000 Subject: [PATCH 14/16] feat: mirror every OTLP span to /tmp/gh-aw/otel.jsonl for artifact inspection Agent-Logs-Url: https://github.com/github/gh-aw/sessions/c17643f9-a18d-46b3-a0e5-5c43dc2d1298 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/constants.cjs | 8 ++ actions/setup/js/send_otlp_span.cjs | 34 +++++++ actions/setup/js/send_otlp_span.test.cjs | 110 ++++++++++++++++++++++- 3 files changed, 151 insertions(+), 1 deletion(-) diff --git a/actions/setup/js/constants.cjs b/actions/setup/js/constants.cjs index 6adcb33b0d4..5fa4be27b2d 100644 --- a/actions/setup/js/constants.cjs +++ b/actions/setup/js/constants.cjs @@ -80,6 +80,13 @@ const RPC_MESSAGES_PATH = `${TMP_GH_AW_PATH}/mcp-logs/rpc-messages.jsonl`; */ const MANIFEST_FILE_PATH = `${TMP_GH_AW_PATH}/safe-output-items.jsonl`; +/** + * Path to the OTLP telemetry mirror file. + * Every OTLP span payload is appended here as a JSON line for artifact inspection. + * @type {string} + */ +const OTEL_JSONL_PATH = `${TMP_GH_AW_PATH}/otel.jsonl`; + /** * Filename of the threat detection log written by the detection engine via tee. * The detection copilot's stdout (containing THREAT_DETECTION_RESULT) is piped @@ -98,5 +105,6 @@ module.exports = { GATEWAY_JSONL_PATH, RPC_MESSAGES_PATH, MANIFEST_FILE_PATH, + OTEL_JSONL_PATH, DETECTION_LOG_FILENAME, }; diff --git a/actions/setup/js/send_otlp_span.cjs b/actions/setup/js/send_otlp_span.cjs index d5f9905d976..4b88a9e3540 100644 --- a/actions/setup/js/send_otlp_span.cjs +++ b/actions/setup/js/send_otlp_span.cjs @@ -121,6 +121,35 @@ function buildOTLPPayload({ traceId, spanId, parentSpanId, spanName, startMs, en }; } +// --------------------------------------------------------------------------- +// Local JSONL mirror +// --------------------------------------------------------------------------- + +/** + * Path to the OTLP telemetry mirror file. + * Every OTLP span payload is also appended here as a JSON line so that it can + * be inspected via GitHub Actions artifacts without needing a live collector. + * @type {string} + */ +const OTEL_JSONL_PATH = "/tmp/gh-aw/otel.jsonl"; + +/** + * Append an OTLP payload as a single JSON line to the local telemetry mirror + * file. Creates the `/tmp/gh-aw` directory if it does not already exist. + * Errors are silently swallowed — mirror failures must never break the workflow. + * + * @param {object} payload - OTLP traces payload + * @returns {void} + */ +function appendToOTLPJSONL(payload) { + try { + fs.mkdirSync("/tmp/gh-aw", { recursive: true }); + fs.appendFileSync(OTEL_JSONL_PATH, JSON.stringify(payload) + "\n"); + } catch { + // Mirror failures are non-fatal; do not propagate. + } +} + // --------------------------------------------------------------------------- // HTTP transport // --------------------------------------------------------------------------- @@ -169,6 +198,9 @@ function parseOTLPHeaders(raw) { * @returns {Promise} */ async function sendOTLPSpan(endpoint, payload, { maxRetries = 2, baseDelayMs = 100 } = {}) { + // Mirror payload locally so it survives even when the collector is unreachable. + appendToOTLPJSONL(payload); + const url = endpoint.replace(/\/$/, "") + "/v1/traces"; const extraHeaders = parseOTLPHeaders(process.env.OTEL_EXPORTER_OTLP_HEADERS || ""); const headers = { "Content-Type": "application/json", ...extraHeaders }; @@ -457,4 +489,6 @@ module.exports = { readJSONIfExists, sendJobSetupSpan, sendJobConclusionSpan, + OTEL_JSONL_PATH, + appendToOTLPJSONL, }; diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index d0957047419..b0c58cbc4b3 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -1,10 +1,12 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import fs from "fs"; // --------------------------------------------------------------------------- // Module import // --------------------------------------------------------------------------- -const { isValidTraceId, isValidSpanId, generateTraceId, generateSpanId, toNanoString, buildAttr, buildOTLPPayload, parseOTLPHeaders, sendOTLPSpan, sendJobSetupSpan, sendJobConclusionSpan } = await import("./send_otlp_span.cjs"); +const { isValidTraceId, isValidSpanId, generateTraceId, generateSpanId, toNanoString, buildAttr, buildOTLPPayload, parseOTLPHeaders, sendOTLPSpan, sendJobSetupSpan, sendJobConclusionSpan, OTEL_JSONL_PATH, appendToOTLPJSONL } = + await import("./send_otlp_span.cjs"); // --------------------------------------------------------------------------- // isValidTraceId @@ -231,12 +233,18 @@ describe("buildOTLPPayload", () => { // --------------------------------------------------------------------------- describe("sendOTLPSpan", () => { + let mkdirSpy, appendSpy; + beforeEach(() => { vi.stubGlobal("fetch", vi.fn()); + mkdirSpy = vi.spyOn(fs, "mkdirSync").mockImplementation(() => {}); + appendSpy = vi.spyOn(fs, "appendFileSync").mockImplementation(() => {}); }); afterEach(() => { vi.unstubAllGlobals(); + mkdirSpy.mockRestore(); + appendSpy.mockRestore(); }); it("POSTs JSON payload to endpoint/v1/traces", async () => { @@ -308,6 +316,91 @@ describe("sendOTLPSpan", () => { }); }); +// --------------------------------------------------------------------------- +// appendToOTLPJSONL +// --------------------------------------------------------------------------- + +describe("appendToOTLPJSONL", () => { + let mkdirSpy, appendSpy; + + beforeEach(() => { + mkdirSpy = vi.spyOn(fs, "mkdirSync").mockImplementation(() => {}); + appendSpy = vi.spyOn(fs, "appendFileSync").mockImplementation(() => {}); + }); + + afterEach(() => { + mkdirSpy.mockRestore(); + appendSpy.mockRestore(); + }); + + it("writes payload as a JSON line to OTEL_JSONL_PATH", () => { + const payload = { resourceSpans: [{ spans: [] }] }; + appendToOTLPJSONL(payload); + + expect(appendSpy).toHaveBeenCalledOnce(); + const [filePath, content] = appendSpy.mock.calls[0]; + expect(filePath).toBe(OTEL_JSONL_PATH); + expect(content).toBe(JSON.stringify(payload) + "\n"); + }); + + it("ensures /tmp/gh-aw directory exists before writing", () => { + appendToOTLPJSONL({}); + + expect(mkdirSpy).toHaveBeenCalledWith("/tmp/gh-aw", { recursive: true }); + }); + + it("does not throw when appendFileSync fails", () => { + appendSpy.mockImplementation(() => { + throw new Error("disk full"); + }); + + expect(() => appendToOTLPJSONL({ spans: [] })).not.toThrow(); + }); +}); + +// --------------------------------------------------------------------------- +// sendOTLPSpan – JSONL mirror +// --------------------------------------------------------------------------- + +describe("sendOTLPSpan JSONL mirror", () => { + let mkdirSpy, appendSpy; + + beforeEach(() => { + mkdirSpy = vi.spyOn(fs, "mkdirSync").mockImplementation(() => {}); + appendSpy = vi.spyOn(fs, "appendFileSync").mockImplementation(() => {}); + vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, status: 200, statusText: "OK" })); + }); + + afterEach(() => { + mkdirSpy.mockRestore(); + appendSpy.mockRestore(); + vi.unstubAllGlobals(); + }); + + it("mirrors the payload to otel.jsonl even when fetch succeeds", async () => { + const payload = { resourceSpans: [] }; + await sendOTLPSpan("https://traces.example.com", payload); + + expect(appendSpy).toHaveBeenCalledOnce(); + const [filePath, content] = appendSpy.mock.calls[0]; + expect(filePath).toBe(OTEL_JSONL_PATH); + expect(content).toBe(JSON.stringify(payload) + "\n"); + }); + + it("mirrors the payload to otel.jsonl even when fetch fails all retries", async () => { + vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: false, status: 503, statusText: "Unavailable" })); + const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + + const payload = { resourceSpans: [{ note: "retry-test" }] }; + await sendOTLPSpan("https://traces.example.com", payload, { maxRetries: 1, baseDelayMs: 1 }); + + expect(appendSpy).toHaveBeenCalledOnce(); + expect(appendSpy.mock.calls[0][1]).toBe(JSON.stringify(payload) + "\n"); + + warnSpy.mockRestore(); + }); +}); + // --------------------------------------------------------------------------- // parseOTLPHeaders // --------------------------------------------------------------------------- @@ -359,14 +452,19 @@ describe("parseOTLPHeaders", () => { describe("sendOTLPSpan with OTEL_EXPORTER_OTLP_HEADERS", () => { const savedHeaders = process.env.OTEL_EXPORTER_OTLP_HEADERS; + let mkdirSpy, appendSpy; beforeEach(() => { vi.stubGlobal("fetch", vi.fn()); delete process.env.OTEL_EXPORTER_OTLP_HEADERS; + mkdirSpy = vi.spyOn(fs, "mkdirSync").mockImplementation(() => {}); + appendSpy = vi.spyOn(fs, "appendFileSync").mockImplementation(() => {}); }); afterEach(() => { vi.unstubAllGlobals(); + mkdirSpy.mockRestore(); + appendSpy.mockRestore(); if (savedHeaders !== undefined) { process.env.OTEL_EXPORTER_OTLP_HEADERS = savedHeaders; } else { @@ -406,6 +504,7 @@ describe("sendJobSetupSpan", () => { /** @type {Record} */ const savedEnv = {}; const envKeys = ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_SERVICE_NAME", "INPUT_JOB_NAME", "INPUT_TRACE_ID", "GH_AW_INFO_WORKFLOW_NAME", "GH_AW_INFO_ENGINE_ID", "GITHUB_RUN_ID", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]; + let mkdirSpy, appendSpy; beforeEach(() => { vi.stubGlobal("fetch", vi.fn()); @@ -413,6 +512,8 @@ describe("sendJobSetupSpan", () => { savedEnv[k] = process.env[k]; delete process.env[k]; } + mkdirSpy = vi.spyOn(fs, "mkdirSync").mockImplementation(() => {}); + appendSpy = vi.spyOn(fs, "appendFileSync").mockImplementation(() => {}); }); afterEach(() => { @@ -424,6 +525,8 @@ describe("sendJobSetupSpan", () => { delete process.env[k]; } } + mkdirSpy.mockRestore(); + appendSpy.mockRestore(); }); /** @@ -603,6 +706,7 @@ describe("sendJobConclusionSpan", () => { /** @type {Record} */ const savedEnv = {}; const envKeys = ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_SERVICE_NAME", "GH_AW_EFFECTIVE_TOKENS", "GH_AW_INFO_VERSION", "GITHUB_AW_OTEL_TRACE_ID", "GITHUB_AW_OTEL_PARENT_SPAN_ID", "GITHUB_RUN_ID", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]; + let mkdirSpy, appendSpy; beforeEach(() => { vi.stubGlobal("fetch", vi.fn()); @@ -610,6 +714,8 @@ describe("sendJobConclusionSpan", () => { savedEnv[k] = process.env[k]; delete process.env[k]; } + mkdirSpy = vi.spyOn(fs, "mkdirSync").mockImplementation(() => {}); + appendSpy = vi.spyOn(fs, "appendFileSync").mockImplementation(() => {}); }); afterEach(() => { @@ -621,6 +727,8 @@ describe("sendJobConclusionSpan", () => { delete process.env[k]; } } + mkdirSpy.mockRestore(); + appendSpy.mockRestore(); }); it("is a no-op when OTEL_EXPORTER_OTLP_ENDPOINT is not set", async () => { From 1c1b54f8317c02676053d0efe154b9fc7bb29db3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 15:14:08 +0000 Subject: [PATCH 15/16] feat: configure smoke-copilot with OTLP secrets; fix raw frontmatter OTLP injection Agent-Logs-Url: https://github.com/github/gh-aw/sessions/21afe94c-2d9a-4d15-9dab-6859ad6cc23b Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/smoke-copilot-arm.lock.yml | 59 ++++----- .github/workflows/smoke-copilot-arm.md | 4 + .github/workflows/smoke-copilot.lock.yml | 59 ++++----- .github/workflows/smoke-copilot.md | 4 + actions/setup/js/send_otlp_span.test.cjs | 15 +++ pkg/workflow/frontmatter_types.go | 6 +- pkg/workflow/observability_otlp.go | 49 +++++++- pkg/workflow/observability_otlp_test.go | 120 +++++++++++++++++++ 8 files changed, 257 insertions(+), 59 deletions(-) diff --git a/.github/workflows/smoke-copilot-arm.lock.yml b/.github/workflows/smoke-copilot-arm.lock.yml index e91eddbc1bc..74795062670 100644 --- a/.github/workflows/smoke-copilot-arm.lock.yml +++ b/.github/workflows/smoke-copilot-arm.lock.yml @@ -30,7 +30,7 @@ # - shared/mcp/serena.md # - shared/reporting.md # -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"3e2fe69aad7d2801364a57386fae783d35b5afda5c4e976f829c5953a22349a5","agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"e603d70b7bc3dba0312876331b6f358c10fbf40a13be449d18d2a71c98cc8c70","agent_id":"copilot"} name: "Smoke Copilot ARM64" "on": @@ -55,6 +55,11 @@ concurrency: run-name: "Smoke Copilot ARM64" +env: + OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.GH_AW_OTEL_ENDPOINT }} + OTEL_SERVICE_NAME: gh-aw + OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.GH_AW_OTEL_HEADERS }} + jobs: activation: needs: pre_activation @@ -192,9 +197,9 @@ jobs: run: | bash ${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh { - cat << 'GH_AW_PROMPT_5d913d5c763dcde8_EOF' + cat << 'GH_AW_PROMPT_e1fc2cb2cbeaacc4_EOF' - GH_AW_PROMPT_5d913d5c763dcde8_EOF + GH_AW_PROMPT_e1fc2cb2cbeaacc4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" @@ -202,7 +207,7 @@ jobs: cat "${RUNNER_TEMP}/gh-aw/prompts/agentic_workflows_guide.md" cat "${RUNNER_TEMP}/gh-aw/prompts/cache_memory_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_5d913d5c763dcde8_EOF' + cat << 'GH_AW_PROMPT_e1fc2cb2cbeaacc4_EOF' Tools: add_comment(max:2), create_issue, create_discussion, create_pull_request_review_comment(max:5), submit_pull_request_review, add_labels, remove_labels, dispatch_workflow, missing_tool, missing_data, noop, send_slack_message @@ -234,9 +239,9 @@ jobs: {{/if}} - GH_AW_PROMPT_5d913d5c763dcde8_EOF + GH_AW_PROMPT_e1fc2cb2cbeaacc4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_5d913d5c763dcde8_EOF' + cat << 'GH_AW_PROMPT_e1fc2cb2cbeaacc4_EOF' ## Serena Code Analysis @@ -274,7 +279,7 @@ jobs: {{#runtime-import .github/workflows/shared/github-queries-mcp-script.md}} {{#runtime-import .github/workflows/shared/mcp/serena-go.md}} {{#runtime-import .github/workflows/smoke-copilot-arm.md}} - GH_AW_PROMPT_5d913d5c763dcde8_EOF + GH_AW_PROMPT_e1fc2cb2cbeaacc4_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -530,12 +535,12 @@ jobs: mkdir -p ${RUNNER_TEMP}/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_b3b02bc8bedde255_EOF' + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_947e130c0b26d5ea_EOF' {"add_comment":{"allowed_repos":["github/gh-aw"],"hide_older_comments":true,"max":2},"add_labels":{"allowed":["smoke-copilot-arm"],"allowed_repos":["github/gh-aw"]},"create_discussion":{"category":"announcements","close_older_discussions":true,"expires":2,"fallback_to_issue":true,"labels":["ai-generated"],"max":1},"create_issue":{"close_older_issues":true,"close_older_key":"smoke-copilot-arm","expires":2,"group":true,"labels":["automation","testing"],"max":1},"create_pull_request_review_comment":{"max":5,"side":"RIGHT"},"dispatch_workflow":{"max":1,"workflow_files":{"haiku-printer":".yml"},"workflows":["haiku-printer"]},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"remove_labels":{"allowed":["smoke"]},"send-slack-message":{"description":"Send a message to Slack (stub for testing)","inputs":{"message":{"description":"The message to send","required":false,"type":"string"}},"output":"Slack message stub executed!"},"submit_pull_request_review":{"max":1}} - GH_AW_SAFE_OUTPUTS_CONFIG_b3b02bc8bedde255_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_947e130c0b26d5ea_EOF - name: Write Safe Outputs Tools run: | - cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_71157be718379f35_EOF' + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_7a5b953e996d8298_EOF' { "description_suffixes": { "add_comment": " CONSTRAINTS: Maximum 2 comment(s) can be added.", @@ -592,8 +597,8 @@ jobs: } ] } - GH_AW_SAFE_OUTPUTS_TOOLS_META_71157be718379f35_EOF - cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_6b9e2ffc5e628cfb_EOF' + GH_AW_SAFE_OUTPUTS_TOOLS_META_7a5b953e996d8298_EOF + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_cad1a7b1918f47ef_EOF' { "add_comment": { "defaultMax": 1, @@ -823,7 +828,7 @@ jobs: } } } - GH_AW_SAFE_OUTPUTS_VALIDATION_6b9e2ffc5e628cfb_EOF + GH_AW_SAFE_OUTPUTS_VALIDATION_cad1a7b1918f47ef_EOF node ${RUNNER_TEMP}/gh-aw/actions/generate_safe_outputs_tools.cjs - name: Generate Safe Outputs MCP Server Config id: safe-outputs-config @@ -868,7 +873,7 @@ jobs: - name: Setup MCP Scripts Config run: | mkdir -p ${RUNNER_TEMP}/gh-aw/mcp-scripts/logs - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_5f80dec2b93fd240_EOF' + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_1c701d2b9950333f_EOF' { "serverName": "mcpscripts", "version": "1.0.0", @@ -984,8 +989,8 @@ jobs: } ] } - GH_AW_MCP_SCRIPTS_TOOLS_5f80dec2b93fd240_EOF - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_9583ab3a98c30557_EOF' + GH_AW_MCP_SCRIPTS_TOOLS_1c701d2b9950333f_EOF + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_c0a0a7288717268c_EOF' const path = require("path"); const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs"); const configPath = path.join(__dirname, "tools.json"); @@ -999,12 +1004,12 @@ jobs: console.error("Failed to start mcp-scripts HTTP server:", error); process.exit(1); }); - GH_AW_MCP_SCRIPTS_SERVER_9583ab3a98c30557_EOF + GH_AW_MCP_SCRIPTS_SERVER_c0a0a7288717268c_EOF chmod +x ${RUNNER_TEMP}/gh-aw/mcp-scripts/mcp-server.cjs - name: Setup MCP Scripts Tool Files run: | - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/gh.sh << 'GH_AW_MCP_SCRIPTS_SH_GH_487bcc3c1624a635_EOF' + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/gh.sh << 'GH_AW_MCP_SCRIPTS_SH_GH_91488b82431e1a7f_EOF' #!/bin/bash # Auto-generated mcp-script tool: gh # Execute any gh CLI command. This tool is accessible as 'mcpscripts-gh'. Provide the full command after 'gh' (e.g., args: 'pr list --limit 5'). The tool will run: gh . Use single quotes ' for complex args to avoid shell interpretation issues. @@ -1015,9 +1020,9 @@ jobs: echo " token: ${GH_AW_GH_TOKEN:0:6}..." GH_TOKEN="$GH_AW_GH_TOKEN" gh $INPUT_ARGS - GH_AW_MCP_SCRIPTS_SH_GH_487bcc3c1624a635_EOF + GH_AW_MCP_SCRIPTS_SH_GH_91488b82431e1a7f_EOF chmod +x ${RUNNER_TEMP}/gh-aw/mcp-scripts/gh.sh - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-discussion-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_de6480930afd6152_EOF' + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-discussion-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_93e05781f9ca515e_EOF' #!/bin/bash # Auto-generated mcp-script tool: github-discussion-query # Query GitHub discussions with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter. @@ -1152,9 +1157,9 @@ jobs: EOF fi - GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_de6480930afd6152_EOF + GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_93e05781f9ca515e_EOF chmod +x ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-discussion-query.sh - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-issue-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_728c50db158e2b32_EOF' + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-issue-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_c1c61a69acdb8d06_EOF' #!/bin/bash # Auto-generated mcp-script tool: github-issue-query # Query GitHub issues with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter. @@ -1233,9 +1238,9 @@ jobs: fi - GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_728c50db158e2b32_EOF + GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_c1c61a69acdb8d06_EOF chmod +x ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-issue-query.sh - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-pr-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_288f75e4dc98f5a6_EOF' + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-pr-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_f517760b3784f2a2_EOF' #!/bin/bash # Auto-generated mcp-script tool: github-pr-query # Query GitHub pull requests with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter. @@ -1320,7 +1325,7 @@ jobs: fi - GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_288f75e4dc98f5a6_EOF + GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_f517760b3784f2a2_EOF chmod +x ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-pr-query.sh - name: Generate MCP Scripts Server Config @@ -1393,7 +1398,7 @@ jobs: export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e GH_AW_GH_TOKEN -e GH_DEBUG -e GH_TOKEN -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.2.12' mkdir -p /home/runner/.copilot - cat << GH_AW_MCP_CONFIG_bbaab723be3e0d4f_EOF | bash ${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh + cat << GH_AW_MCP_CONFIG_baa1bb4a61e1bc9d_EOF | bash ${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { "agenticworkflows": { @@ -1510,7 +1515,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_bbaab723be3e0d4f_EOF + GH_AW_MCP_CONFIG_baa1bb4a61e1bc9d_EOF - name: Download activation artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: diff --git a/.github/workflows/smoke-copilot-arm.md b/.github/workflows/smoke-copilot-arm.md index 7c539c2d878..35564e5408c 100644 --- a/.github/workflows/smoke-copilot-arm.md +++ b/.github/workflows/smoke-copilot-arm.md @@ -106,6 +106,10 @@ safe-outputs: run-failure: "📰 DEVELOPING STORY: [{workflow_name}]({run_url}) reports {status}. Our correspondents are investigating the incident..." timeout-minutes: 15 strict: false +observability: + otlp: + endpoint: ${{ secrets.GH_AW_OTEL_ENDPOINT }} + headers: ${{ secrets.GH_AW_OTEL_HEADERS }} --- # Smoke Test: Copilot Engine Validation (ARM64) diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 7a0e924a4fa..3ca1ea35850 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -31,7 +31,7 @@ # - shared/mcp/serena.md # - shared/reporting.md # -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"a7b4d7bf4f02611637cd82a7a5f7c06048689e94db970e6f73d90a2673fdecc7","agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"a905032d976816465ac3dca3ab19ecdf3acddb929861a5ff5984faffcab585e4","agent_id":"copilot"} name: "Smoke Copilot" "on": @@ -60,6 +60,11 @@ concurrency: run-name: "Smoke Copilot" +env: + OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.GH_AW_OTEL_ENDPOINT }} + OTEL_SERVICE_NAME: gh-aw + OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.GH_AW_OTEL_HEADERS }} + jobs: activation: needs: pre_activation @@ -199,9 +204,9 @@ jobs: run: | bash ${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh { - cat << 'GH_AW_PROMPT_9df51225daadb559_EOF' + cat << 'GH_AW_PROMPT_62c8abf1cc0f53ad_EOF' - GH_AW_PROMPT_9df51225daadb559_EOF + GH_AW_PROMPT_62c8abf1cc0f53ad_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" @@ -209,7 +214,7 @@ jobs: cat "${RUNNER_TEMP}/gh-aw/prompts/agentic_workflows_guide.md" cat "${RUNNER_TEMP}/gh-aw/prompts/cache_memory_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_9df51225daadb559_EOF' + cat << 'GH_AW_PROMPT_62c8abf1cc0f53ad_EOF' Tools: add_comment(max:2), create_issue, create_discussion, create_pull_request_review_comment(max:5), submit_pull_request_review, reply_to_pull_request_review_comment(max:5), add_labels, remove_labels, set_issue_type, dispatch_workflow, missing_tool, missing_data, noop, send_slack_message @@ -241,9 +246,9 @@ jobs: {{/if}} - GH_AW_PROMPT_9df51225daadb559_EOF + GH_AW_PROMPT_62c8abf1cc0f53ad_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_9df51225daadb559_EOF' + cat << 'GH_AW_PROMPT_62c8abf1cc0f53ad_EOF' ## Serena Code Analysis @@ -282,7 +287,7 @@ jobs: {{#runtime-import .github/workflows/shared/github-queries-mcp-script.md}} {{#runtime-import .github/workflows/shared/mcp/serena-go.md}} {{#runtime-import .github/workflows/smoke-copilot.md}} - GH_AW_PROMPT_9df51225daadb559_EOF + GH_AW_PROMPT_62c8abf1cc0f53ad_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -538,12 +543,12 @@ jobs: mkdir -p ${RUNNER_TEMP}/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_3c8a610621dc5cbf_EOF' + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_996bb8e28da5dfb7_EOF' {"add_comment":{"allowed_repos":["github/gh-aw"],"hide_older_comments":true,"max":2},"add_labels":{"allowed":["smoke-copilot"],"allowed_repos":["github/gh-aw"]},"create_discussion":{"category":"announcements","close_older_discussions":true,"close_older_key":"smoke-copilot","expires":2,"fallback_to_issue":true,"labels":["ai-generated"],"max":1},"create_issue":{"close_older_issues":true,"close_older_key":"smoke-copilot","expires":2,"group":true,"labels":["automation","testing"],"max":1},"create_pull_request_review_comment":{"max":5,"side":"RIGHT"},"dispatch_workflow":{"max":1,"workflow_files":{"haiku-printer":".yml"},"workflows":["haiku-printer"]},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"remove_labels":{"allowed":["smoke"]},"reply_to_pull_request_review_comment":{"max":5},"send-slack-message":{"description":"Send a message to Slack (stub for testing)","inputs":{"message":{"description":"The message to send","required":false,"type":"string"}},"output":"Slack message stub executed!"},"set_issue_type":{},"submit_pull_request_review":{"max":1}} - GH_AW_SAFE_OUTPUTS_CONFIG_3c8a610621dc5cbf_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_996bb8e28da5dfb7_EOF - name: Write Safe Outputs Tools run: | - cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_d67ffee6fad53472_EOF' + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_d4e9fcd31c3eddcb_EOF' { "description_suffixes": { "add_comment": " CONSTRAINTS: Maximum 2 comment(s) can be added.", @@ -601,8 +606,8 @@ jobs: } ] } - GH_AW_SAFE_OUTPUTS_TOOLS_META_d67ffee6fad53472_EOF - cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_d8a24fe9ebf525ab_EOF' + GH_AW_SAFE_OUTPUTS_TOOLS_META_d4e9fcd31c3eddcb_EOF + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_7d41b605be50b9cb_EOF' { "add_comment": { "defaultMax": 1, @@ -872,7 +877,7 @@ jobs: } } } - GH_AW_SAFE_OUTPUTS_VALIDATION_d8a24fe9ebf525ab_EOF + GH_AW_SAFE_OUTPUTS_VALIDATION_7d41b605be50b9cb_EOF node ${RUNNER_TEMP}/gh-aw/actions/generate_safe_outputs_tools.cjs - name: Generate Safe Outputs MCP Server Config id: safe-outputs-config @@ -917,7 +922,7 @@ jobs: - name: Setup MCP Scripts Config run: | mkdir -p ${RUNNER_TEMP}/gh-aw/mcp-scripts/logs - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_3e8f78b47058959a_EOF' + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_1e7561d526f43bda_EOF' { "serverName": "mcpscripts", "version": "1.0.0", @@ -1033,8 +1038,8 @@ jobs: } ] } - GH_AW_MCP_SCRIPTS_TOOLS_3e8f78b47058959a_EOF - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_e7b909a1866941c3_EOF' + GH_AW_MCP_SCRIPTS_TOOLS_1e7561d526f43bda_EOF + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_a5255a3a8e9f133e_EOF' const path = require("path"); const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs"); const configPath = path.join(__dirname, "tools.json"); @@ -1048,12 +1053,12 @@ jobs: console.error("Failed to start mcp-scripts HTTP server:", error); process.exit(1); }); - GH_AW_MCP_SCRIPTS_SERVER_e7b909a1866941c3_EOF + GH_AW_MCP_SCRIPTS_SERVER_a5255a3a8e9f133e_EOF chmod +x ${RUNNER_TEMP}/gh-aw/mcp-scripts/mcp-server.cjs - name: Setup MCP Scripts Tool Files run: | - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/gh.sh << 'GH_AW_MCP_SCRIPTS_SH_GH_263ef6d96de89fb3_EOF' + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/gh.sh << 'GH_AW_MCP_SCRIPTS_SH_GH_ba7386878a21d8cb_EOF' #!/bin/bash # Auto-generated mcp-script tool: gh # Execute any gh CLI command. This tool is accessible as 'mcpscripts-gh'. Provide the full command after 'gh' (e.g., args: 'pr list --limit 5'). The tool will run: gh . Use single quotes ' for complex args to avoid shell interpretation issues. @@ -1064,9 +1069,9 @@ jobs: echo " token: ${GH_AW_GH_TOKEN:0:6}..." GH_TOKEN="$GH_AW_GH_TOKEN" gh $INPUT_ARGS - GH_AW_MCP_SCRIPTS_SH_GH_263ef6d96de89fb3_EOF + GH_AW_MCP_SCRIPTS_SH_GH_ba7386878a21d8cb_EOF chmod +x ${RUNNER_TEMP}/gh-aw/mcp-scripts/gh.sh - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-discussion-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_05d4ab943fe86ce9_EOF' + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-discussion-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_4d7640a4c077e31c_EOF' #!/bin/bash # Auto-generated mcp-script tool: github-discussion-query # Query GitHub discussions with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter. @@ -1201,9 +1206,9 @@ jobs: EOF fi - GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_05d4ab943fe86ce9_EOF + GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_4d7640a4c077e31c_EOF chmod +x ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-discussion-query.sh - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-issue-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_8ef0432b17b9641c_EOF' + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-issue-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_0641c15ff140ff12_EOF' #!/bin/bash # Auto-generated mcp-script tool: github-issue-query # Query GitHub issues with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter. @@ -1282,9 +1287,9 @@ jobs: fi - GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_8ef0432b17b9641c_EOF + GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_0641c15ff140ff12_EOF chmod +x ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-issue-query.sh - cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-pr-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_c889f4807956eb81_EOF' + cat > ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-pr-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_9ddf10ca0deacae4_EOF' #!/bin/bash # Auto-generated mcp-script tool: github-pr-query # Query GitHub pull requests with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter. @@ -1369,7 +1374,7 @@ jobs: fi - GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_c889f4807956eb81_EOF + GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_9ddf10ca0deacae4_EOF chmod +x ${RUNNER_TEMP}/gh-aw/mcp-scripts/github-pr-query.sh - name: Generate MCP Scripts Server Config @@ -1440,7 +1445,7 @@ jobs: export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e GH_AW_GH_TOKEN -e GH_DEBUG -e GH_TOKEN -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.2.12' mkdir -p /home/runner/.copilot - cat << GH_AW_MCP_CONFIG_7d1672508c35ebcf_EOF | bash ${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh + cat << GH_AW_MCP_CONFIG_c260f8029b966df4_EOF | bash ${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { "agenticworkflows": { @@ -1560,7 +1565,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_7d1672508c35ebcf_EOF + GH_AW_MCP_CONFIG_c260f8029b966df4_EOF - name: Download activation artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: diff --git a/.github/workflows/smoke-copilot.md b/.github/workflows/smoke-copilot.md index 93649992299..b476456956e 100644 --- a/.github/workflows/smoke-copilot.md +++ b/.github/workflows/smoke-copilot.md @@ -117,6 +117,10 @@ safe-outputs: run-failure: "📰 DEVELOPING STORY: [{workflow_name}]({run_url}) reports {status}. Our correspondents are investigating the incident..." timeout-minutes: 15 strict: false +observability: + otlp: + endpoint: ${{ secrets.GH_AW_OTEL_ENDPOINT }} + headers: ${{ secrets.GH_AW_OTEL_HEADERS }} --- # Smoke Test: Copilot Engine Validation diff --git a/actions/setup/js/send_otlp_span.test.cjs b/actions/setup/js/send_otlp_span.test.cjs index b0c58cbc4b3..7bbd3b0327e 100644 --- a/actions/setup/js/send_otlp_span.test.cjs +++ b/actions/setup/js/send_otlp_span.test.cjs @@ -435,6 +435,21 @@ describe("parseOTLPHeaders", () => { expect(parseOTLPHeaders("Authorization=Bearer base64==")).toEqual({ Authorization: "Bearer base64==" }); }); + it("parses Sentry OTLP header format (value contains space and embedded = sign)", () => { + // Sentry's OTLP auth header: x-sentry-auth: Sentry sentry_key= + // The value "Sentry sentry_key=abc123" contains both a space and an embedded =. + expect(parseOTLPHeaders("x-sentry-auth=Sentry sentry_key=abc123def456")).toEqual({ + "x-sentry-auth": "Sentry sentry_key=abc123def456", + }); + }); + + it("parses Sentry header combined with another header", () => { + expect(parseOTLPHeaders("x-sentry-auth=Sentry sentry_key=mykey,x-custom=value")).toEqual({ + "x-sentry-auth": "Sentry sentry_key=mykey", + "x-custom": "value", + }); + }); + it("skips malformed pairs with no =", () => { const result = parseOTLPHeaders("Valid=value,malformedNoEquals"); expect(result).toEqual({ Valid: "value" }); diff --git a/pkg/workflow/frontmatter_types.go b/pkg/workflow/frontmatter_types.go index e3609f9f7ff..efd6b52ef27 100644 --- a/pkg/workflow/frontmatter_types.go +++ b/pkg/workflow/frontmatter_types.go @@ -144,7 +144,7 @@ type FrontmatterConfig struct { // Core workflow fields Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` - Engine string `json:"engine,omitempty"` + Engine any `json:"engine,omitempty"` Source string `json:"source,omitempty"` TrackerID string `json:"tracker-id,omitempty"` Version string `json:"version,omitempty"` @@ -272,7 +272,7 @@ func ParseFrontmatterConfig(frontmatter map[string]any) (*FrontmatterConfig, err } } - frontmatterTypesLog.Printf("Successfully parsed frontmatter config: name=%s, engine=%s", config.Name, config.Engine) + frontmatterTypesLog.Printf("Successfully parsed frontmatter config: name=%s, engine=%v", config.Name, config.Engine) return &config, nil } @@ -541,7 +541,7 @@ func (fc *FrontmatterConfig) ToMap() map[string]any { if fc.Description != "" { result["description"] = fc.Description } - if fc.Engine != "" { + if fc.Engine != nil { result["engine"] = fc.Engine } if fc.Source != "" { diff --git a/pkg/workflow/observability_otlp.go b/pkg/workflow/observability_otlp.go index c1463e59cde..bf92fd33a22 100644 --- a/pkg/workflow/observability_otlp.go +++ b/pkg/workflow/observability_otlp.go @@ -48,6 +48,37 @@ func getOTLPEndpointEnvValue(config *FrontmatterConfig) string { return config.Observability.OTLP.Endpoint } +// extractOTLPConfigFromRaw reads OTLP endpoint and headers directly from the raw +// frontmatter map[string]any. This avoids dependence on ParseFrontmatterConfig +// succeeding — that function may fail for workflows with complex tool configurations +// (e.g. engine objects, array-style bash configs), which would leave ParsedFrontmatter +// nil and prevent OTLP injection. +func extractOTLPConfigFromRaw(frontmatter map[string]any) (endpoint, headers string) { + obs, ok := frontmatter["observability"] + if !ok { + return + } + obsMap, ok := obs.(map[string]any) + if !ok { + return + } + otlp, ok := obsMap["otlp"] + if !ok { + return + } + otlpMap, ok := otlp.(map[string]any) + if !ok { + return + } + if ep, ok := otlpMap["endpoint"].(string); ok { + endpoint = ep + } + if h, ok := otlpMap["headers"].(string); ok { + headers = h + } + return +} + // generateOTLPConclusionSpanStep generates a GitHub Actions step that sends an OTLP // conclusion span from a downstream job (safe_outputs or conclusion). // @@ -82,7 +113,15 @@ func generateOTLPConclusionSpanStep(spanName string) string { // // When no OTLP endpoint is configured the function is a no-op. func (c *Compiler) injectOTLPConfig(workflowData *WorkflowData) { - endpoint := getOTLPEndpointEnvValue(workflowData.ParsedFrontmatter) + // Read OTLP config from the raw frontmatter map so that injection works even + // when ParseFrontmatterConfig failed (e.g. due to complex tool configs). + endpoint, headers := extractOTLPConfigFromRaw(workflowData.RawFrontmatter) + + // Fall back to ParsedFrontmatter when the raw map didn't yield an endpoint. + if endpoint == "" { + endpoint = getOTLPEndpointEnvValue(workflowData.ParsedFrontmatter) + } + if endpoint == "" { return } @@ -102,7 +141,13 @@ func (c *Compiler) injectOTLPConfig(workflowData *WorkflowData) { otlpEnvLines := fmt.Sprintf(" OTEL_EXPORTER_OTLP_ENDPOINT: %s\n OTEL_SERVICE_NAME: gh-aw", endpoint) // 3. Inject OTEL_EXPORTER_OTLP_HEADERS when configured. - if headers := workflowData.ParsedFrontmatter.Observability.OTLP.Headers; headers != "" { + // Prefer raw frontmatter value (already read above); fall back to ParsedFrontmatter. + if headers == "" && workflowData.ParsedFrontmatter != nil && + workflowData.ParsedFrontmatter.Observability != nil && + workflowData.ParsedFrontmatter.Observability.OTLP != nil { + headers = workflowData.ParsedFrontmatter.Observability.OTLP.Headers + } + if headers != "" { otlpEnvLines += "\n OTEL_EXPORTER_OTLP_HEADERS: " + headers otlpLog.Printf("Injected OTEL_EXPORTER_OTLP_HEADERS env var") } diff --git a/pkg/workflow/observability_otlp_test.go b/pkg/workflow/observability_otlp_test.go index 4fada70f060..42779eab0ed 100644 --- a/pkg/workflow/observability_otlp_test.go +++ b/pkg/workflow/observability_otlp_test.go @@ -424,3 +424,123 @@ func TestGenerateOTLPConclusionSpanStep(t *testing.T) { assert.Contains(t, step, "gh-aw.job.conclusion", "script should use the given span name") }) } + +// TestExtractOTLPConfigFromRaw verifies direct raw-frontmatter OTLP extraction. +func TestExtractOTLPConfigFromRaw(t *testing.T) { + tests := []struct { + name string + frontmatter map[string]any + wantEndpoint string + wantHeaders string + }{ + { + name: "nil frontmatter", + frontmatter: nil, + }, + { + name: "empty frontmatter", + frontmatter: map[string]any{}, + }, + { + name: "no observability key", + frontmatter: map[string]any{"name": "test"}, + }, + { + name: "observability without otlp", + frontmatter: map[string]any{ + "observability": map[string]any{"job-summary": "on"}, + }, + }, + { + name: "observability.otlp with endpoint", + frontmatter: map[string]any{ + "observability": map[string]any{ + "otlp": map[string]any{"endpoint": "https://traces.example.com:4317"}, + }, + }, + wantEndpoint: "https://traces.example.com:4317", + }, + { + name: "observability.otlp with secret expression endpoint", + frontmatter: map[string]any{ + "observability": map[string]any{ + "otlp": map[string]any{"endpoint": "${{ secrets.GH_AW_OTEL_ENDPOINT }}"}, + }, + }, + wantEndpoint: "${{ secrets.GH_AW_OTEL_ENDPOINT }}", + }, + { + name: "observability.otlp with endpoint and headers", + frontmatter: map[string]any{ + "observability": map[string]any{ + "otlp": map[string]any{ + "endpoint": "https://traces.example.com", + "headers": "${{ secrets.GH_AW_OTEL_HEADERS }}", + }, + }, + }, + wantEndpoint: "https://traces.example.com", + wantHeaders: "${{ secrets.GH_AW_OTEL_HEADERS }}", + }, + { + name: "Sentry-style header with space in value", + frontmatter: map[string]any{ + "observability": map[string]any{ + "otlp": map[string]any{ + "endpoint": "https://sentry.io/api/123/envelope/", + "headers": "x-sentry-auth=Sentry sentry_key=abc123", + }, + }, + }, + wantEndpoint: "https://sentry.io/api/123/envelope/", + wantHeaders: "x-sentry-auth=Sentry sentry_key=abc123", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotEndpoint, gotHeaders := extractOTLPConfigFromRaw(tt.frontmatter) + assert.Equal(t, tt.wantEndpoint, gotEndpoint, "endpoint") + assert.Equal(t, tt.wantHeaders, gotHeaders, "headers") + }) + } +} + +// TestInjectOTLPConfig_RawFrontmatterFallback verifies that injectOTLPConfig works +// when ParsedFrontmatter is nil (e.g. complex engine objects cause ParseFrontmatterConfig +// to fail) but the raw frontmatter contains valid OTLP configuration. +func TestInjectOTLPConfig_RawFrontmatterFallback(t *testing.T) { + c := &Compiler{} + + t.Run("injects OTLP from raw frontmatter when ParsedFrontmatter is nil", func(t *testing.T) { + wd := &WorkflowData{ + ParsedFrontmatter: nil, // simulates ParseFrontmatterConfig failure + RawFrontmatter: map[string]any{ + "observability": map[string]any{ + "otlp": map[string]any{ + "endpoint": "${{ secrets.GH_AW_OTEL_ENDPOINT }}", + "headers": "${{ secrets.GH_AW_OTEL_HEADERS }}", + }, + }, + // Simulate complex engine object that would cause ParseFrontmatterConfig to fail. + "engine": map[string]any{"id": "copilot", "max-continuations": 2}, + }, + } + c.injectOTLPConfig(wd) + + require.NotEmpty(t, wd.Env, "Env should be set even without ParsedFrontmatter") + assert.Contains(t, wd.Env, "OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.GH_AW_OTEL_ENDPOINT }}", "endpoint should be injected from raw") + assert.Contains(t, wd.Env, "OTEL_SERVICE_NAME: gh-aw", "service name should be set") + assert.Contains(t, wd.Env, "OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.GH_AW_OTEL_HEADERS }}", "headers should be injected from raw") + }) + + t.Run("no-op when neither raw nor parsed frontmatter has OTLP", func(t *testing.T) { + wd := &WorkflowData{ + ParsedFrontmatter: nil, + RawFrontmatter: map[string]any{"name": "my-workflow"}, + } + c.injectOTLPConfig(wd) + assert.Empty(t, wd.Env, "Env should remain empty") + assert.Nil(t, wd.NetworkPermissions, "NetworkPermissions should remain nil") + }) +} From 571fef3687eff613381ea575d7c914500c2d5d4b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 15:18:22 +0000 Subject: [PATCH 16/16] fix: add comment to Engine any field; fix em dash in comment Agent-Logs-Url: https://github.com/github/gh-aw/sessions/21afe94c-2d9a-4d15-9dab-6859ad6cc23b Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/frontmatter_types.go | 8 ++++++-- pkg/workflow/observability_otlp.go | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/workflow/frontmatter_types.go b/pkg/workflow/frontmatter_types.go index efd6b52ef27..2ae08be7a61 100644 --- a/pkg/workflow/frontmatter_types.go +++ b/pkg/workflow/frontmatter_types.go @@ -142,8 +142,12 @@ type ObservabilityConfig struct { // This provides compile-time type safety and clearer error messages compared to map[string]any type FrontmatterConfig struct { // Core workflow fields - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + // Engine accepts both a plain string engine name (e.g. "copilot") and an object-style + // configuration (e.g. {id: copilot, max-continuations: 2}). Using any prevents + // JSON unmarshal failures when the engine is an object, which would otherwise cause + // ParseFrontmatterConfig to return nil and break features that depend on it (e.g. OTLP). Engine any `json:"engine,omitempty"` Source string `json:"source,omitempty"` TrackerID string `json:"tracker-id,omitempty"` diff --git a/pkg/workflow/observability_otlp.go b/pkg/workflow/observability_otlp.go index bf92fd33a22..bcc84fa9006 100644 --- a/pkg/workflow/observability_otlp.go +++ b/pkg/workflow/observability_otlp.go @@ -50,7 +50,7 @@ func getOTLPEndpointEnvValue(config *FrontmatterConfig) string { // extractOTLPConfigFromRaw reads OTLP endpoint and headers directly from the raw // frontmatter map[string]any. This avoids dependence on ParseFrontmatterConfig -// succeeding — that function may fail for workflows with complex tool configurations +// succeeding -- that function may fail for workflows with complex tool configurations // (e.g. engine objects, array-style bash configs), which would leave ParsedFrontmatter // nil and prevent OTLP injection. func extractOTLPConfigFromRaw(frontmatter map[string]any) (endpoint, headers string) {