Skip to content

Commit c20683a

Browse files
committed
feat(stovepipe): scaffold pipeline entities and storage extension
Add Commit and Batch as the core domain entities the validation pipeline operates on, each with status types and optimistic-locking Version fields. Add CommitStore and BatchStore extension interfaces scoped to their respective service owners (gateway and orchestrator). Fix missing BUILD.bazel files for the filter and changeingester packages. Flatten ChangeInfo.Author into AuthorName/AuthorEmail fields directly.
1 parent b0a647d commit c20683a

12 files changed

Lines changed: 302 additions & 15 deletions

File tree

stovepipe/core/filter/BUILD.bazel

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 = "filter",
5+
srcs = ["filter.go"],
6+
importpath = "github.com/uber/submitqueue/stovepipe/core/filter",
7+
visibility = ["//visibility:public"],
8+
deps = ["//stovepipe/entity"],
9+
)

stovepipe/entity/BUILD.bazel

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ load("@rules_go//go:def.bzl", "go_library")
22

33
go_library(
44
name = "entity",
5-
srcs = ["entity.go"],
5+
srcs = [
6+
"batch.go",
7+
"commit.go",
8+
"entity.go",
9+
],
610
importpath = "github.com/uber/submitqueue/stovepipe/entity",
711
visibility = ["//visibility:public"],
812
)

stovepipe/entity/batch.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) 2025 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+
// BatchStatus is the state of a validation batch as it moves through the pipeline.
18+
type BatchStatus string
19+
20+
const (
21+
// BatchStatusUnknown is the unreachable default. It should never be seen in the system.
22+
BatchStatusUnknown BatchStatus = ""
23+
// BatchStatusPending means the batch has been created but speculate has not yet run.
24+
BatchStatusPending BatchStatus = "pending"
25+
// BatchStatusBuilding means the batch is moving through the speculate→build→buildsignal→bisect cycle.
26+
BatchStatusBuilding BatchStatus = "building"
27+
// BatchStatusSucceeded means all commits in the batch's range have been validated green.
28+
BatchStatusSucceeded BatchStatus = "succeeded"
29+
// BatchStatusFailed means an offending commit in the range has been isolated and marked failed.
30+
BatchStatusFailed BatchStatus = "failed"
31+
)
32+
33+
// IsBatchStatusTerminal returns true if the batch has reached a final state.
34+
func IsBatchStatusTerminal(s BatchStatus) bool {
35+
return s == BatchStatusSucceeded || s == BatchStatusFailed
36+
}
37+
38+
// Batch is a contiguous range of trunk commits submitted for validation together.
39+
// The range spans from FromSHA (oldest, inclusive) to ToSHA (newest, inclusive)
40+
// and represents all commits since the last known green on the branch.
41+
// Bisection creates sub-range batches from the same type — there is no separate
42+
// bisection entity; the state of the search lives in the ordinary batch results.
43+
type Batch struct {
44+
// ID is the unique identifier for this batch.
45+
ID string
46+
// FromSHA is the oldest commit SHA in the validation range (inclusive).
47+
FromSHA string
48+
// ToSHA is the newest commit SHA in the validation range (inclusive).
49+
ToSHA string
50+
// Repository is the repository this batch validates.
51+
Repository string
52+
// Branch is the branch this batch validates.
53+
Branch string
54+
// Status is the current state of this batch.
55+
Status BatchStatus
56+
// Version is incremented on each update and used for optimistic locking.
57+
// Version arithmetic lives in the controller; the store performs a pure conditional write.
58+
Version int32
59+
// CreatedAt is the time this batch was created, in milliseconds since epoch.
60+
CreatedAt int64
61+
// UpdatedAt is the time this batch was last updated, in milliseconds since epoch.
62+
UpdatedAt int64
63+
}

stovepipe/entity/commit.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) 2025 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+
// CommitStatus is the validation state of a trunk commit as determined by Stovepipe.
18+
type CommitStatus string
19+
20+
const (
21+
// CommitStatusUnknown is the default state when a commit is first ingested.
22+
// The commit has landed on main but has not yet been validated.
23+
CommitStatusUnknown CommitStatus = ""
24+
// CommitStatusSucceeded means the relevant targets build and test successfully at this commit.
25+
CommitStatusSucceeded CommitStatus = "succeeded"
26+
// CommitStatusFailed means a target is broken at this commit; it is the offending change.
27+
CommitStatusFailed CommitStatus = "failed"
28+
)
29+
30+
// IsCommitStatusTerminal returns true if the status is a final, irreversible state.
31+
func IsCommitStatusTerminal(s CommitStatus) bool {
32+
return s == CommitStatusSucceeded || s == CommitStatusFailed
33+
}
34+
35+
// Commit is a trunk commit tracked by Stovepipe. The SHA scoped by Repository and
36+
// Branch is the natural identity and dedup key: a commit announced by both a webhook
37+
// and a poll backfill resolves to the same record and is processed once.
38+
type Commit struct {
39+
// SHA is the full commit hash. Identity key; immutable after creation.
40+
SHA string
41+
// Repository is the repository URI (e.g. "github.com/uber/go-code").
42+
Repository string
43+
// Branch is the target branch (e.g. "main").
44+
Branch string
45+
// CommitterTimeMs is the committer timestamp in milliseconds since epoch.
46+
// Used to order commits within a range and to establish the trunk sequence.
47+
CommitterTimeMs int64
48+
// Status is the current validation state of this commit.
49+
Status CommitStatus
50+
// Version is incremented on each update and used for optimistic locking.
51+
// Version arithmetic lives in the controller; the store performs a pure conditional write.
52+
Version int32
53+
// CreatedAt is the time this commit was first recorded, in milliseconds since epoch.
54+
CreatedAt int64
55+
// UpdatedAt is the time this commit was last updated, in milliseconds since epoch.
56+
UpdatedAt int64
57+
}

stovepipe/entity/entity.go

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,15 @@ package entity
2727
type ChangeInfo struct {
2828
// URI is the canonical VCS identifier for this change.
2929
// Scheme is "git://"; path encodes host, repo, ref, and new revision.
30-
// This mirrors the ChangeInfo.URI pattern used in SubmitQueue.
3130
URI string `json:"uri"`
3231

3332
// PreviousURI is the URI of the prior revision on the same ref, if known.
3433
// Empty string if unavailable.
3534
// Example: "git://github.com/uber/go-code/refs/heads/main/aabbccdd..."
3635
PreviousURI string `json:"previous_uri,omitempty"`
3736

38-
// Author is the identity of the person who authored the change.
39-
Author Author `json:"author"`
40-
}
41-
42-
// Author identifies the person who authored a change.
43-
// Mirrors SubmitQueue's Author to keep the two domains consistent.
44-
type Author struct {
45-
// Name is the display name of the author.
46-
Name string `json:"name"`
47-
// Email is the email address of the author.
48-
Email string `json:"email,omitempty"`
37+
// AuthorName is the display name of the person who authored the change.
38+
AuthorName string `json:"author_name,omitempty"`
39+
// AuthorEmail is the email address of the author.
40+
AuthorEmail string `json:"author_email,omitempty"`
4941
}

stovepipe/extension/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ go_library(
55
srcs = ["extension.go"],
66
importpath = "github.com/uber/submitqueue/stovepipe/extension",
77
visibility = ["//visibility:public"],
8+
deps = ["//stovepipe/entity"],
89
)
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 = "changeingester",
5+
srcs = ["logging.go"],
6+
importpath = "github.com/uber/submitqueue/stovepipe/extension/changeingester",
7+
visibility = ["//visibility:public"],
8+
deps = [
9+
"//stovepipe/entity",
10+
"//stovepipe/extension",
11+
"@org_uber_go_zap//:zap",
12+
],
13+
)

stovepipe/extension/changeingester/logging.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ func (h LoggingHandler) IngestChange(ctx context.Context, info entity.ChangeInfo
3939
h.logger.Info("ingested change",
4040
zap.String("uri", info.URI),
4141
zap.String("previous_uri", info.PreviousURI),
42-
zap.String("author", info.Author.Name),
43-
zap.String("author_email", info.Author.Email),
42+
zap.String("author_name", info.AuthorName),
43+
zap.String("author_email", info.AuthorEmail),
4444
)
4545
return nil
4646
}
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 = "storage",
5+
srcs = [
6+
"batch_store.go",
7+
"commit_store.go",
8+
"storage.go",
9+
],
10+
importpath = "github.com/uber/submitqueue/stovepipe/extension/storage",
11+
visibility = ["//visibility:public"],
12+
deps = ["//stovepipe/entity"],
13+
)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) 2025 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 storage
16+
17+
//go:generate mockgen -source=batch_store.go -destination=mock/batch_store_mock.go -package=mock
18+
19+
import (
20+
"context"
21+
22+
"github.com/uber/submitqueue/stovepipe/entity"
23+
)
24+
25+
// BatchStore is the orchestrator-owned store for in-flight validation batches.
26+
// A batch represents a contiguous range of trunk commits under validation.
27+
// Bisection reuses the same type — sub-range probes are ordinary batches
28+
// driven through the same speculate→build→buildsignal→bisect loop.
29+
type BatchStore interface {
30+
// Get retrieves a batch by ID. Returns ErrNotFound if no record exists.
31+
Get(ctx context.Context, id string) (entity.Batch, error)
32+
33+
// Create records a new batch with status BatchStatusPending.
34+
// Returns ErrAlreadyExists if a batch with the same ID already exists.
35+
Create(ctx context.Context, batch entity.Batch) error
36+
37+
// UpdateStatus updates the batch's status and advances the version from
38+
// oldVersion to newVersion. Returns ErrVersionMismatch if the current
39+
// persisted version does not match oldVersion; the caller must re-read and retry.
40+
// Version arithmetic is owned by the caller (controller); the store performs
41+
// a pure conditional write.
42+
UpdateStatus(ctx context.Context, id string, oldVersion, newVersion int32, status entity.BatchStatus) error
43+
}

0 commit comments

Comments
 (0)