golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/task/buildbucket.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package task 6 7 import ( 8 "context" 9 "fmt" 10 11 pb "go.chromium.org/luci/buildbucket/proto" 12 "google.golang.org/protobuf/types/known/structpb" 13 ) 14 15 type BuildBucketClient interface { 16 // ListBuilders lists all the builders in bucket, keyed by their builder names. 17 ListBuilders(ctx context.Context, bucket string) (map[string]*pb.BuilderConfig, error) 18 // RunBuild runs a builder at commit with properties and returns its ID. 19 RunBuild(ctx context.Context, bucket, builder string, commit *pb.GitilesCommit, properties map[string]*structpb.Value) (int64, error) 20 // Completed reports whether a build has finished, returning an error if 21 // it's failed. It's suitable for use with AwaitCondition. 22 Completed(ctx context.Context, id int64) (string, bool, error) 23 // SearchBuilds searches for builds matching pred and returns their IDs. 24 SearchBuilds(ctx context.Context, pred *pb.BuildPredicate) ([]int64, error) 25 } 26 27 type RealBuildBucketClient struct { 28 BuildersClient pb.BuildersClient 29 BuildsClient pb.BuildsClient 30 } 31 32 func (c *RealBuildBucketClient) ListBuilders(ctx context.Context, bucket string) (map[string]*pb.BuilderConfig, error) { 33 var pageToken string 34 builders := map[string]*pb.BuilderConfig{} 35 nextPage: 36 resp, err := c.BuildersClient.ListBuilders(ctx, &pb.ListBuildersRequest{ 37 Project: "golang", 38 Bucket: bucket, 39 PageSize: 1000, 40 PageToken: pageToken, 41 }) 42 if err != nil { 43 return nil, err 44 } 45 for _, b := range resp.Builders { 46 builders[b.Id.Builder] = b.Config 47 } 48 if resp.NextPageToken != "" { 49 pageToken = resp.NextPageToken 50 goto nextPage 51 } 52 return builders, nil 53 } 54 55 func (c *RealBuildBucketClient) RunBuild(ctx context.Context, bucket, builder string, commit *pb.GitilesCommit, properties map[string]*structpb.Value) (int64, error) { 56 req := &pb.ScheduleBuildRequest{ 57 Builder: &pb.BuilderID{Project: "golang", Bucket: bucket, Builder: builder}, 58 Priority: 20, 59 Properties: &structpb.Struct{ 60 Fields: properties, 61 }, 62 GitilesCommit: commit, 63 } 64 build, err := c.BuildsClient.ScheduleBuild(ctx, req) 65 if err != nil { 66 return 0, err 67 } 68 return build.Id, err 69 } 70 71 func (c *RealBuildBucketClient) Completed(ctx context.Context, id int64) (string, bool, error) { 72 build, err := c.BuildsClient.GetBuildStatus(ctx, &pb.GetBuildStatusRequest{ 73 Id: id, 74 }) 75 if err != nil { 76 return "", false, err 77 } 78 if build.Status&pb.Status_ENDED_MASK == 0 { 79 return "", false, nil 80 } 81 if build.Status&pb.Status_ENDED_MASK != 0 && build.Status != pb.Status_SUCCESS { 82 return "", true, fmt.Errorf("build failed with status %v, see https://ci.chromium.org/b/%v: %v", build.Status, id, build.SummaryMarkdown) 83 } 84 return build.SummaryMarkdown, true, nil 85 } 86 87 func (c *RealBuildBucketClient) SearchBuilds(ctx context.Context, pred *pb.BuildPredicate) ([]int64, error) { 88 resp, err := c.BuildsClient.SearchBuilds(ctx, &pb.SearchBuildsRequest{ 89 Predicate: pred, 90 }) 91 if err != nil { 92 return nil, err 93 } 94 if resp.NextPageToken != "" { 95 return nil, fmt.Errorf("page size to SearchBuilds insufficient") 96 } 97 var results []int64 98 for _, b := range resp.Builds { 99 results = append(results, b.Id) 100 } 101 return results, nil 102 }