@@ -20,11 +20,14 @@ import (
2020 "strings"
2121
2222 cloudbuild "cloud.google.com/go/cloudbuild/apiv1/v2"
23- cloudbuildpb "cloud.google.com/go/cloudbuild/apiv1/v2/cloudbuildpb"
24-
23+ logging "cloud.google.com/go/logging/apiv2"
2524 build "google.golang.org/api/cloudbuild/v1"
2625 "google.golang.org/api/iterator"
26+ "google.golang.org/protobuf/encoding/protojson"
2727 "google.golang.org/protobuf/types/known/timestamppb"
28+
29+ cloudbuildpb "cloud.google.com/go/cloudbuild/apiv1/v2/cloudbuildpb"
30+ loggingpb "cloud.google.com/go/logging/apiv2/loggingpb"
2831)
2932
3033// contextKey is a private type to use as a key for context values.
@@ -51,6 +54,14 @@ type CloudBuildClient interface {
5154 GetLatestBuildForTrigger (ctx context.Context , projectID , location , triggerID string ) (* cloudbuildpb.Build , error )
5255 ListBuildTriggers (ctx context.Context , projectID , location string ) ([]* cloudbuildpb.BuildTrigger , error )
5356 RunBuildTrigger (ctx context.Context , projectID , location , triggerID , branch , tag , commitSha string ) (* cloudbuild.RunBuildTriggerOperation , error )
57+ ListBuilds (ctx context.Context , projectID , location string ) ([]* cloudbuildpb.Build , error )
58+ GetBuildInfo (ctx context.Context , projectID , location , buildID string ) (BuildInfo , error )
59+ StartBuild (ctx context.Context , projectID , location string , source * cloudbuildpb.Source ) (* cloudbuild.CreateBuildOperation , error )
60+ }
61+
62+ type BuildInfo struct {
63+ BuildDetails * cloudbuildpb.Build
64+ Logs string
5465}
5566
5667// NewCloudBuildClient creates a new Cloud Build client.
@@ -65,13 +76,23 @@ func NewCloudBuildClient(ctx context.Context) (CloudBuildClient, error) {
6576 return nil , fmt .Errorf ("failed to create Cloud Build service: %v" , err )
6677 }
6778
68- return & CloudBuildClientImpl {v1client : c , legacyClient : c2 }, nil
79+ loggingClient , err := logging .NewClient (ctx )
80+ if err != nil {
81+ return nil , fmt .Errorf ("failed to create Logging client: %v" , err )
82+ }
83+
84+ return & CloudBuildClientImpl {
85+ v1client : c ,
86+ legacyClient : c2 ,
87+ loggingClient : loggingClient ,
88+ }, nil
6989}
7090
7191// CloudBuildClientImpl is an implementation of the CloudBuildClient interface.
7292type CloudBuildClientImpl struct {
7393 v1client * cloudbuild.Client
7494 legacyClient * build.Service
95+ loggingClient * logging.Client
7596}
7697
7798// CreateCloudBuildTrigger creates a new build trigger.
@@ -181,3 +202,80 @@ func (c *CloudBuildClientImpl) RunBuildTrigger(ctx context.Context, projectID, l
181202 }
182203 return op , nil
183204}
205+
206+
207+ func (c * CloudBuildClientImpl ) ListBuilds (ctx context.Context , projectID , location string ) ([]* cloudbuildpb.Build , error ) {
208+ req := & cloudbuildpb.ListBuildsRequest {
209+ Parent : fmt .Sprintf ("projects/%s/locations/%s" , projectID , location ),
210+ }
211+ it := c .v1client .ListBuilds (ctx , req )
212+ var builds []* cloudbuildpb.Build
213+ for {
214+ build , err := it .Next ()
215+ if err == iterator .Done {
216+ break
217+ }
218+ if err != nil {
219+ return nil , fmt .Errorf ("failed to list builds: %w" , err )
220+ }
221+ builds = append (builds , build )
222+ }
223+ return builds , nil
224+ }
225+
226+ func (c * CloudBuildClientImpl ) GetBuildInfo (ctx context.Context , projectID , location , buildID string ) (BuildInfo , error ) {
227+ req := & cloudbuildpb.GetBuildRequest {
228+ Name : fmt .Sprintf ("projects/%s/locations/%s/builds/%s" , projectID , location , buildID ),
229+ }
230+ build , err := c .v1client .GetBuild (ctx , req )
231+ if err != nil {
232+ return BuildInfo {}, fmt .Errorf ("failed to get build info: %w" , err )
233+ }
234+ info := BuildInfo {BuildDetails : build }
235+ logReq := & loggingpb.ListLogEntriesRequest {
236+ ResourceNames : []string {fmt .Sprintf ("projects/%s" , projectID )},
237+ Filter : fmt .Sprintf (`resource.type="build" AND resource.labels.build_id="%s" AND logName="projects/%s/logs/cloudbuild"` , buildID , projectID ),
238+ }
239+ it := c .loggingClient .ListLogEntries (ctx , logReq )
240+ var logs []string
241+ for {
242+ entry , err := it .Next ()
243+ if err == iterator .Done {
244+ break
245+ }
246+ if err != nil {
247+ return BuildInfo {}, fmt .Errorf ("failed to list log entries: %w" , err )
248+ }
249+ var logMessage string
250+ switch payload := entry .Payload .(type ) {
251+ case * loggingpb.LogEntry_TextPayload :
252+ logMessage = payload .TextPayload
253+ case * loggingpb.LogEntry_JsonPayload :
254+ jsonBytes , err := protojson .Marshal (payload .JsonPayload )
255+ if err != nil {
256+ logMessage = fmt .Sprintf ("failed to marshal json payload to string: %v" , err )
257+ } else {
258+ logMessage = string (jsonBytes )
259+ }
260+ case * loggingpb.LogEntry_ProtoPayload :
261+ logMessage = fmt .Sprintf ("%v" , payload .ProtoPayload )
262+ default :
263+ return BuildInfo {}, fmt .Errorf ("unknown log entry payload type" )
264+ }
265+ logs = append (logs , logMessage )
266+ }
267+ info .Logs = strings .Join (logs , "\n " )
268+ return info , nil
269+ }
270+
271+ func (c * CloudBuildClientImpl ) StartBuild (ctx context.Context , projectID , location string , source * cloudbuildpb.Source ) (* cloudbuild.CreateBuildOperation , error ) {
272+ req := & cloudbuildpb.CreateBuildRequest {
273+ Parent : fmt .Sprintf ("projects/%s/locations/%s" , projectID , location ),
274+ Build : & cloudbuildpb.Build {Source : source },
275+ }
276+ ops , err := c .v1client .CreateBuild (ctx , req )
277+ if err != nil {
278+ return nil , fmt .Errorf ("failed to start build: %w" , err )
279+ }
280+ return ops , nil
281+ }
0 commit comments