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  }