go.fuchsia.dev/infra@v0.0.0-20240507153436-9b593402251b/cmd/lkg/cmd_revision.go (about)

     1  // Copyright 2020 The Fuchsia Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  
    12  	"github.com/maruel/subcommands"
    13  	"go.chromium.org/luci/auth"
    14  	buildbucketpb "go.chromium.org/luci/buildbucket/proto"
    15  	"google.golang.org/genproto/protobuf/field_mask"
    16  )
    17  
    18  func cmdRevision(authOpts auth.Options) *subcommands.Command {
    19  	return &subcommands.Command{
    20  		UsageLine: "revision -builder project1/bucket1/builder1, ... [common options]",
    21  		ShortDesc: "Get the last known good revision across a set of builders.",
    22  		LongDesc:  "Get the last known good revision across a set of builders.",
    23  		CommandRun: func() subcommands.CommandRun {
    24  			c := &revisionRun{}
    25  			c.Init(authOpts)
    26  			return c
    27  		},
    28  	}
    29  }
    30  
    31  type revisionRun struct {
    32  	commonFlags
    33  	builders builders
    34  }
    35  
    36  func (c *revisionRun) Init(defaultAuthOpts auth.Options) {
    37  	c.commonFlags.Init(defaultAuthOpts)
    38  	c.Flags.Var(&c.builders, "builder", "Builder ID as project/bucket/builder. Repeatable")
    39  }
    40  
    41  func (c *revisionRun) checkFlags() error {
    42  	if err := c.commonFlags.checkFlags(); err != nil {
    43  		return err
    44  	}
    45  	if len(c.builders) == 0 {
    46  		return errors.New("at least one -builder is required")
    47  	}
    48  	return nil
    49  }
    50  
    51  func (c *revisionRun) main(a subcommands.Application) error {
    52  	ctx := context.Background()
    53  	client, err := newBuildbucketClient(ctx, c.parsedAuthOpts, c.buildbucketHost)
    54  	if err != nil {
    55  		return fmt.Errorf("failed to initialize client: %w", err)
    56  	}
    57  	// Do not retrieve any fields additional to default input, as we only need revision.
    58  	mask := &field_mask.FieldMask{
    59  		Paths: []string{},
    60  	}
    61  	builds, err := client.GetBuilds(ctx, c.builders, mask, c.searchRange)
    62  	if err != nil {
    63  		return fmt.Errorf("failed to query builds: %w", err)
    64  	}
    65  	// Pass an accessorFunc which returns a build's revision.
    66  	revision, err := getLastKnownGood(builds, c.gitilesRef, func(build *buildbucketpb.Build) any {
    67  		// Skip manually triggered builds, which do not have a gitiles commit.
    68  		if build.Input.GitilesCommit == nil {
    69  			return nil
    70  		}
    71  		return build.Input.GitilesCommit.Id
    72  	})
    73  	if err != nil {
    74  		return fmt.Errorf("failed to get last known good revision: %w", err)
    75  	}
    76  	fmt.Println(revision)
    77  	return nil
    78  }
    79  
    80  func (c *revisionRun) Run(a subcommands.Application, args []string, env subcommands.Env) int {
    81  	if err := c.checkFlags(); err != nil {
    82  		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
    83  		return 1
    84  	}
    85  
    86  	if err := c.main(a); err != nil {
    87  		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
    88  		return 1
    89  	}
    90  	return 0
    91  }