go.fuchsia.dev/infra@v0.0.0-20240507153436-9b593402251b/cmd/roller-configurator/resolve.go (about) 1 // Copyright 2023 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 "encoding/json" 10 "fmt" 11 "io" 12 "log" 13 "os" 14 "path/filepath" 15 16 "github.com/maruel/subcommands" 17 "go.fuchsia.dev/infra/cmd/roller-configurator/proto" 18 ) 19 20 func cmdResolve() *subcommands.Command { 21 return &subcommands.Command{ 22 UsageLine: "resolve [-config <config-path>]", 23 ShortDesc: "Resolve a rollers.textproto file.", 24 LongDesc: "Resolve a rollers.textproto file.", 25 CommandRun: func() subcommands.CommandRun { 26 c := &resolveRun{} 27 c.Init() 28 return c 29 }, 30 } 31 } 32 33 type resolveRun struct { 34 subcommands.CommandRunBase 35 configPath string 36 } 37 38 func (c *resolveRun) Init() { 39 c.Flags.StringVar(&c.configPath, "config", "rollers.textproto", "Path to the config file to validate.") 40 } 41 42 func (c *resolveRun) Parse() error { 43 return nil 44 } 45 46 func (c *resolveRun) Run(a subcommands.Application, args []string, env subcommands.Env) int { 47 if err := c.Parse(); err != nil { 48 fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err) 49 return 1 50 } 51 config, err := readConfig(c.configPath) 52 if err != nil { 53 fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err) 54 return 1 55 } 56 // rollers.textproto files must be located in the repository root. 57 repoRoot := filepath.Dir(c.configPath) 58 if err := resolve(context.Background(), repoRoot, config, os.Stdout); err != nil { 59 fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err) 60 return 1 61 } 62 return 0 63 } 64 65 func resolve(ctx context.Context, repoRoot string, config *proto.Config, output io.Writer) error { 66 out := []map[string]any{} 67 for i, roller := range config.GetRollers() { 68 toRollDesc := roller.ProtoReflect().Descriptor().Oneofs().ByName("to_roll") 69 field := roller.ProtoReflect().WhichOneof(toRollDesc) 70 if field == nil { 71 return fmt.Errorf("entry %d is missing an entity to roll", i) 72 } 73 74 var toResolve interface { 75 Resolve(ctx context.Context, params proto.ResolveParams) (map[string]any, error) 76 } 77 switch field.Name() { // TODO(olivernewman): Deduplicate this logic with validate.go. 78 case "submodule": 79 toResolve = roller.GetSubmodule() 80 case "cipd_ensure_file": 81 toResolve = roller.GetCipdEnsureFile() 82 case "jiri_project": 83 toResolve = roller.GetJiriProject() 84 case "jiri_packages": 85 toResolve = roller.GetJiriPackages() 86 default: 87 log.Panicf("unknown to_roll type: %q", field.Name()) 88 } 89 90 resolved, err := proto.ProtoToMap(roller) 91 if err != nil { 92 return err 93 } 94 delete(resolved, string(field.Name())) 95 addl, err := toResolve.Resolve(ctx, proto.ResolveParams{ 96 RepoRoot: repoRoot, 97 DefaultCheckoutJiriManifest: config.DefaultCheckoutJiriManifest, 98 }) 99 if err != nil { 100 return err 101 } 102 for k, v := range addl { 103 resolved[k] = v 104 } 105 out = append(out, resolved) 106 } 107 enc := json.NewEncoder(output) 108 enc.SetIndent("", " ") 109 return enc.Encode(out) 110 }