Skip to content

Commit 2727b89

Browse files
committed
feat(stovepipe): Add build analysis contract
1 parent 062742c commit 2727b89

8 files changed

Lines changed: 251 additions & 0 deletions

File tree

‎Makefile‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@ GO_GENERATE_PACKAGES := \
628628
./runway/extension/merger/... \
629629
./stovepipe/controller/... \
630630
./stovepipe/core/requestlog/... \
631+
./stovepipe/extension/buildanalysis/... \
631632
./stovepipe/extension/buildrunner/... \
632633
./stovepipe/extension/projectstatus/... \
633634
./stovepipe/extension/queueconfig/... \

‎stovepipe/entity/BUILD.bazel‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go_library(
44
name = "go_default_library",
55
srcs = [
66
"build.go",
7+
"build_report.go",
78
"ingest.go",
89
"list.go",
910
"project_status.go",

‎stovepipe/entity/build_report.go‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) 2026 Uber Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package entity
16+
17+
// BuildReportState describes the availability of a validation report.
18+
type BuildReportState string
19+
20+
const (
21+
// BuildReportStateUnknown means report availability has not been established.
22+
BuildReportStateUnknown BuildReportState = ""
23+
// BuildReportStatePending means a final report is not available yet.
24+
BuildReportStatePending BuildReportState = "pending"
25+
// BuildReportStateAvailable means a final validation report can be interpreted.
26+
BuildReportStateAvailable BuildReportState = "available"
27+
// BuildReportStateUnavailable means the attempt ended without a usable final report.
28+
BuildReportStateUnavailable BuildReportState = "unavailable"
29+
)
30+
31+
// BuildRetryDisposition describes the automatic retry decision for an attempt of a
32+
// validation task, not manual reruns or the retries used to read its evidence.
33+
type BuildRetryDisposition string
34+
35+
const (
36+
// BuildRetryDispositionUnknown means the automatic retry decision is not known.
37+
BuildRetryDispositionUnknown BuildRetryDisposition = ""
38+
// BuildRetryDispositionPending means an automatic retry is scheduled or running.
39+
BuildRetryDispositionPending BuildRetryDisposition = "pending"
40+
// BuildRetryDispositionExhausted means no automatic retry remains under the applicable policy.
41+
BuildRetryDispositionExhausted BuildRetryDisposition = "exhausted"
42+
)
43+
44+
// BuildReport contains validation evidence from the current attempt of a validation task.
45+
// A validation task is work executed and retried together; a unit is the
46+
// granularity of reported outcomes. All identifiers are opaque.
47+
type BuildReport struct {
48+
// TaskID identifies the logical validation task within the build, stable across attempts.
49+
TaskID string `json:"task_id"`
50+
// AttemptID identifies this execution of the validation task; empty if none exists yet.
51+
AttemptID string `json:"attempt_id"`
52+
// State describes whether this attempt's final validation report is available.
53+
State BuildReportState `json:"state"`
54+
// FailedUnits lists failed validation-unit identifiers when State is available.
55+
// An empty list establishes no failure within the report's coverage, not build success.
56+
FailedUnits []string `json:"failed_units"`
57+
// RetryDisposition describes the automatic retry decision for this attempt.
58+
RetryDisposition BuildRetryDisposition `json:"retry_disposition"`
59+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
load("@rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = ["buildanalysis.go"],
6+
importpath = "github.com/uber/submitqueue/stovepipe/extension/buildanalysis",
7+
visibility = ["//visibility:public"],
8+
deps = ["//stovepipe/entity:go_default_library"],
9+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Build analysis
2+
3+
`Reader` reads validation reports available from an active or completed build. It does not poll build status, wait for completion, schedule redelivery, or persist results. This package defines only the extension contract; it adds no running pipeline stage.
4+
5+
Report values and their availability and retry-disposition enums are defined in the [domain model](../../entity/build_report.go).
6+
7+
A build is the overall validation run. Within a build, a validation task is work executed and retried together, and an attempt is one execution of that task. A validation unit is the granularity at which an implementation reports outcomes within an attempt, not necessarily the smallest executable operation.
8+
9+
A read returns a slice with one report for each relevant task discovered in the build, using its current attempt. Pending or unavailable reports are included rather than silently omitting those tasks. Task identities remain stable across retries within a build; attempt identities distinguish executions but do not establish their ordering. Task, attempt, and validation-unit identifiers are opaque.
10+
11+
Implementations own artifact formats, provider APIs, task composition, validation-unit granularity, and automatic retry-policy interpretation. Queue routing belongs to host wiring through `Factory`.
12+
13+
Report state describes result availability, not whether validation passed or failed:
14+
15+
| State | Meaning |
16+
| --- | --- |
17+
| Unknown | Report availability has not been established; this is the zero value. |
18+
| Pending | The task has not started or its current attempt is still running, so final results are not available yet. |
19+
| Available | A final validation report can be interpreted, whether it contains failed units or no failures. |
20+
| Unavailable | The attempt ended without a usable final report, for example because expected output is missing or malformed. |
21+
22+
Pending or unavailable reports do not invalidate usable reports from other tasks. An empty failure list in an available report means no failures were reported within its coverage; an empty list in a pending or unavailable report establishes nothing about validation success. Dependency failures, such as task-enumeration failures or timeouts while fetching output, return plain errors for the host's classifier rather than an unavailable report; callers discard reports returned with an error.
23+
24+
Build status determines when the caller performs its final inspection. A final snapshot can still contain unavailable reports: analysis can finish without establishing every validation outcome. An empty report slice does not establish build success. Inferring recovery from absence requires an available report from comparable validation covering the unit. A consumer requiring terminal attribution must establish build completion and usable evidence for the relevant scope.
25+
26+
Automatic retry disposition describes whether the current attempt of a task will be retried and is unknown unless the provider can establish it. The latest failed attempt alone does not prove exhaustion. Exhaustion applies to automatic retries under the current policy, not the possibility of a manual rerun. A unit's failure remains evidence of that attempt even when a retry is pending; consumers must not combine successive snapshots into a permanent union of failed units.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) 2026 Uber Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package buildanalysis defines validation reports available from active or
16+
// completed builds, independently of build status polling.
17+
package buildanalysis
18+
19+
//go:generate mockgen -source=buildanalysis.go -destination=mock/buildanalysis_mock.go -package=mock
20+
21+
import (
22+
"context"
23+
24+
"github.com/uber/submitqueue/stovepipe/entity"
25+
)
26+
27+
// Reader inspects every relevant task currently discoverable in a build,
28+
// returning one report per task for its current attempt without waiting.
29+
// Pending or unavailable reports are normal results. Implementations must be
30+
// safe for concurrent use and return plain errors for dependency failures.
31+
// Reports returned with an error must not be used.
32+
type Reader interface {
33+
Read(ctx context.Context, buildID entity.BuildID) ([]entity.BuildReport, error)
34+
}
35+
36+
// Config identifies the queue whose validation-report reader is requested.
37+
type Config struct {
38+
// QueueName identifies the queue.
39+
QueueName string
40+
}
41+
42+
// Factory resolves a queue's reader; implementations and routing belong to host wiring.
43+
type Factory interface {
44+
For(cfg Config) (Reader, error)
45+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
load("@rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = ["buildanalysis_mock.go"],
6+
importpath = "github.com/uber/submitqueue/stovepipe/extension/buildanalysis/mock",
7+
visibility = ["//visibility:public"],
8+
deps = [
9+
"//stovepipe/entity:go_default_library",
10+
"//stovepipe/extension/buildanalysis:go_default_library",
11+
"@org_uber_go_mock//gomock:go_default_library",
12+
],
13+
)

‎stovepipe/extension/buildanalysis/mock/buildanalysis_mock.go‎

Lines changed: 97 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)