@@ -1362,7 +1362,13 @@ func executeBuildPhase(buildctx *buildContext, p *Package, builddir string, bld
13621362
13631363 log .WithField ("phase" , phase ).WithField ("package" , p .FullName ()).WithField ("commands" , bld .Commands [phase ]).Debug ("running commands" )
13641364
1365- err := executeCommandsForPackage (buildctx , p , builddir , cmds )
1365+ var err error
1366+ // Use TestExecutor for test phase if available (enables per-test tracing)
1367+ if phase == PackageBuildPhaseTest && bld .TestExecutor != nil {
1368+ err = bld .TestExecutor (buildctx , p , builddir )
1369+ } else {
1370+ err = executeCommandsForPackage (buildctx , p , builddir , cmds )
1371+ }
13661372 pkgRep .phaseDone [phase ] = time .Now ()
13671373
13681374 return err
@@ -1521,6 +1527,11 @@ type packageBuild struct {
15211527 // It's used for post-build processing that needs to happen regardless of provenance settings,
15221528 // such as Docker image extraction.
15231529 PostProcess func (buildCtx * buildContext , pkg * Package , buildDir string ) error
1530+
1531+ // TestExecutor is an optional function that executes tests with tracing support.
1532+ // If set, it will be used instead of the standard command execution for the test phase.
1533+ // This allows Go packages to create spans for individual tests.
1534+ TestExecutor func (buildctx * buildContext , p * Package , builddir string ) error
15241535}
15251536
15261537type testCoverageFunc func () (coverage , funcsWithoutTest , funcsWithTest int , err error )
@@ -2103,6 +2114,7 @@ func (p *Package) buildGo(buildctx *buildContext, wd, result string) (res *packa
21032114 }
21042115 }
21052116 var reportCoverage testCoverageFunc
2117+ var testExecutor func (buildctx * buildContext , p * Package , builddir string ) error
21062118 if ! cfg .DontTest && ! buildctx .DontTest {
21072119 testCommand := []string {goCommand , "test" }
21082120 if log .IsLevelEnabled (log .DebugLevel ) {
@@ -2122,6 +2134,9 @@ func (p *Package) buildGo(buildctx *buildContext, wd, result string) (res *packa
21222134 testCommand = append (testCommand , "./..." )
21232135
21242136 commands [PackageBuildPhaseTest ] = append (commands [PackageBuildPhaseTest ], testCommand )
2137+
2138+ // Create test executor for tracing individual tests
2139+ testExecutor = createGoTestExecutor (testCommand )
21252140 }
21262141
21272142 var buildCmd []string
@@ -2160,9 +2175,48 @@ func (p *Package) buildGo(buildctx *buildContext, wd, result string) (res *packa
21602175 return & packageBuild {
21612176 Commands : commands ,
21622177 TestCoverage : reportCoverage ,
2178+ TestExecutor : testExecutor ,
21632179 }, nil
21642180}
21652181
2182+ // createGoTestExecutor creates a test executor function that runs go test with JSON output
2183+ // and creates OpenTelemetry spans for each individual test when a TestTracingReporter is available.
2184+ func createGoTestExecutor (testCommand []string ) func (buildctx * buildContext , p * Package , builddir string ) error {
2185+ return func (buildctx * buildContext , p * Package , builddir string ) error {
2186+ // Check if the reporter supports test tracing
2187+ tracingReporter , ok := buildctx .Reporter .(TestTracingReporter )
2188+ if ! ok {
2189+ // Fall back to standard execution without tracing
2190+ return executeCommandsForPackage (buildctx , p , builddir , [][]string {testCommand })
2191+ }
2192+
2193+ // Get the test tracer from the reporter
2194+ tracer := tracingReporter .GetGoTestTracer (p )
2195+ if tracer == nil {
2196+ // Tracer not available, fall back to standard execution
2197+ return executeCommandsForPackage (buildctx , p , builddir , [][]string {testCommand })
2198+ }
2199+
2200+ // Set up environment
2201+ env := append (os .Environ (), p .Environment ... )
2202+ env = append (env , fmt .Sprintf ("%s=%s" , EnvvarWorkspaceRoot , p .C .W .Origin ))
2203+
2204+ // Export SOURCE_DATE_EPOCH for reproducible builds
2205+ mtime , err := p .getDeterministicMtime ()
2206+ if err == nil {
2207+ env = append (env , fmt .Sprintf ("SOURCE_DATE_EPOCH=%d" , mtime ))
2208+ }
2209+ env = append (env , "DOCKER_BUILDKIT=1" )
2210+
2211+ // Create output writer that reports to the build reporter
2212+ outputWriter := & reporterStream {R : buildctx .Reporter , P : p , IsErr : false }
2213+
2214+ // Run go test with JSON output and create spans for each test
2215+ log .WithField ("package" , p .FullName ()).WithField ("command" , strings .Join (testCommand , " " )).Debug ("running go test with tracing" )
2216+ return tracer .RunGoTest (context .Background (), env , builddir , testCommand , outputWriter )
2217+ }
2218+ }
2219+
21662220func collectGoTestCoverage (covfile string ) testCoverageFunc {
21672221 return func () (coverage , funcsWithoutTest , funcsWithTest int , err error ) {
21682222 // We need to collect the coverage for all packages in the module.
0 commit comments