go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/client/cmd/gitiles/refs.go (about) 1 // Copyright 2017 The LUCI Authors. 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 main 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "os" 21 22 "github.com/maruel/subcommands" 23 24 "go.chromium.org/luci/auth" 25 "go.chromium.org/luci/common/api/gitiles" 26 "go.chromium.org/luci/common/errors" 27 gitilespb "go.chromium.org/luci/common/proto/gitiles" 28 "go.chromium.org/luci/common/retry" 29 "go.chromium.org/luci/common/retry/transient" 30 "go.chromium.org/luci/grpc/grpcutil" 31 ) 32 33 func cmdRefs(authOpts auth.Options) *subcommands.Command { 34 return &subcommands.Command{ 35 UsageLine: "refs repository refspath", 36 ShortDesc: "resolves each ref in a repo to git revision", 37 LongDesc: `Resolves each ref in a repo to git revision. 38 39 refspath limits which refs to resolve to only those matching {refspath}/*. 40 refspath should start with "refs" and should not include glob '*'. 41 Typically, "refs/heads" should be used.`, 42 CommandRun: func() subcommands.CommandRun { 43 c := refsRun{} 44 c.commonFlags.Init(authOpts) 45 c.Flags.StringVar(&c.jsonOutput, "json-output", "", "Path to write operation results to.") 46 return &c 47 }, 48 } 49 } 50 51 type refsRun struct { 52 commonFlags 53 jsonOutput string 54 } 55 56 func (c *refsRun) Parse(a subcommands.Application, args []string) error { 57 switch err := c.commonFlags.Parse(); { 58 case err != nil: 59 return err 60 case len(args) != 2: 61 return errors.New("exactly 2 position arguments are expected") 62 default: 63 return nil 64 } 65 } 66 67 func (c *refsRun) main(a subcommands.Application, args []string) error { 68 ctx := c.defaultFlags.MakeLoggingContext(os.Stderr) 69 70 host, project, err := gitiles.ParseRepoURL(args[0]) 71 if err != nil { 72 return errors.Annotate(err, "invalid repo URL %q", args[0]).Err() 73 } 74 75 authCl, err := c.createAuthClient() 76 if err != nil { 77 return err 78 } 79 g, err := gitiles.NewRESTClient(authCl, host, true) 80 if err != nil { 81 return err 82 } 83 84 var res *gitilespb.RefsResponse 85 if err := retry.Retry(ctx, transient.Only(retry.Default), func() error { 86 var err error 87 res, err = g.Refs(ctx, &gitilespb.RefsRequest{ 88 Project: project, 89 RefsPath: args[1], 90 }) 91 return grpcutil.WrapIfTransient(err) 92 }, nil); err != nil { 93 return err 94 } 95 96 if c.jsonOutput == "" { 97 for k, v := range res.Revisions { 98 fmt.Printf("%s %s\n", v, k) 99 } 100 return nil 101 } 102 103 out := os.Stdout 104 if c.jsonOutput != "-" { 105 out, err = os.Create(c.jsonOutput) 106 if err != nil { 107 return err 108 } 109 defer out.Close() 110 } 111 112 data, err := json.MarshalIndent(res.Revisions, "", " ") 113 if err != nil { 114 return err 115 } 116 _, err = out.Write(data) 117 return err 118 } 119 120 func (c *refsRun) Run(a subcommands.Application, args []string, _ subcommands.Env) int { 121 if err := c.Parse(a, args); err != nil { 122 fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err) 123 return 1 124 } 125 if err := c.main(a, args); err != nil { 126 fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err) 127 return 1 128 } 129 return 0 130 }