go.fuchsia.dev/infra@v0.0.0-20240507153436-9b593402251b/cmd/submodule_update/integration_update.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  // integration_update uses jiri to find package and projects updates.
     5  package main
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"errors"
    11  	"flag"
    12  	"fmt"
    13  	"os"
    14  
    15  	"github.com/google/subcommands"
    16  	"go.fuchsia.dev/infra/cmd/submodule_update/gitutil"
    17  	"go.fuchsia.dev/infra/cmd/submodule_update/submodule"
    18  )
    19  
    20  // integrationUpdateCmd is a subcommand that figure out what has changed in superpoject.
    21  type integrationUpdateCmd struct {
    22  	baseCmd
    23  	updateConfig
    24  	// Skip add and commit changes
    25  	noCommit    bool
    26  	jiriPath    string
    27  	jiriGitPath string
    28  }
    29  
    30  // snapshotPaths is the list of snapshot paths imported from recipe.
    31  type snapshotPaths struct {
    32  	Ensure          string `json:"ensure"`
    33  	InternalEnsure  string `json:"internal_ensure"`
    34  	InternalVersion string `json:"internal_version"`
    35  	Snapshot        string `json:"snapshot"`
    36  	Version         string `json:"version"`
    37  }
    38  
    39  // config imported from json input file.
    40  type updateConfig struct {
    41  	Branch           string        `json:"branch"`
    42  	Author           string        `json:"commit_author_name"`
    43  	Email            string        `json:"commit_author_email"`
    44  	Message          string        `json:"commit_message"`
    45  	Project          string        `json:"project"`
    46  	CurrentRevision  string        `json:"current_revision"`
    47  	PreviousRevision string        `json:"previous_revision"`
    48  	SnapshotPaths    snapshotPaths `json:"snapshot_paths"`
    49  	SuperprojectRoot string        `json:"superproject_path"`
    50  	JiriProjectsPath string        `json:"jiri_projects_path"`
    51  }
    52  
    53  func (*integrationUpdateCmd) Name() string { return "integration_update" }
    54  func (*integrationUpdateCmd) Synopsis() string {
    55  	return "Check integration updates."
    56  }
    57  func (*integrationUpdateCmd) Usage() string {
    58  	return "Find what projects and packages changed"
    59  }
    60  
    61  func (p *integrationUpdateCmd) SetFlags(f *flag.FlagSet) {
    62  	p.baseCmd.SetFlags(f)
    63  	f.StringVar(&p.jiriPath, "jiri_path", "", "location of jiri exe")
    64  	f.StringVar(&p.jiriGitPath, "jiri_git", "", "path to root of jiri checkout")
    65  	f.BoolVar(&p.noCommit, "no-commit", false, "do not commit")
    66  }
    67  
    68  func (p *integrationUpdateCmd) Validate() error {
    69  	if err := p.checkSuperproject(); err != nil {
    70  		return err
    71  	}
    72  	return nil
    73  }
    74  
    75  func (p *integrationUpdateCmd) ParseJSON() error {
    76  	// Read json inputs
    77  	jsonInputFile, err := os.ReadFile(p.jsonInput)
    78  	if err != nil {
    79  		return err
    80  	}
    81  	if err := json.Unmarshal(jsonInputFile, &p.updateConfig); err != nil {
    82  		return err
    83  	}
    84  	if p.CurrentRevision == "" {
    85  		return errors.New("current revision is missing")
    86  	}
    87  	// We don't check previous revision here, since it's not being used.
    88  	if p.updateConfig.Branch == "" {
    89  		return errors.New("commit branch is required")
    90  	}
    91  	if p.updateConfig.Project == "" {
    92  		return errors.New("commit project is required")
    93  	}
    94  	if p.updateConfig.Author == "" {
    95  		return errors.New("commit author is required")
    96  	}
    97  	if p.updateConfig.Email == "" {
    98  		return errors.New("commit author email is required")
    99  	}
   100  	if p.updateConfig.Message == "" {
   101  		return errors.New("commit message is required")
   102  	}
   103  	if p.updateConfig.JiriProjectsPath == "" {
   104  		return errors.New("jiri projects path is required")
   105  	}
   106  	if p.updateConfig.SuperprojectRoot == "" {
   107  		return errors.New("super project path is required")
   108  	}
   109  	return nil
   110  }
   111  
   112  func (p *integrationUpdateCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...any) subcommands.ExitStatus {
   113  	if err := p.Validate(); err != nil {
   114  		fmt.Printf("error: %s\n", err)
   115  		return subcommands.ExitFailure
   116  	}
   117  	if err := p.ParseJSON(); err != nil {
   118  		fmt.Printf("error: %s\n", err)
   119  		return subcommands.ExitFailure
   120  	}
   121  	fmt.Printf("Updating superproject %s to integration.git revision %s on branch %s\n", p.updateConfig.SuperprojectRoot, p.updateConfig.CurrentRevision, p.updateConfig.Branch)
   122  	fmt.Println()
   123  
   124  	scm := gitutil.New(gitutil.UserNameOpt(p.updateConfig.Author), gitutil.UserEmailOpt(p.updateConfig.Email), gitutil.RootDirOpt(p.updateConfig.SuperprojectRoot))
   125  	cipdPaths := map[string]string{
   126  		"ensure":          p.updateConfig.SnapshotPaths.Ensure,
   127  		"version":         p.updateConfig.SnapshotPaths.Version,
   128  		"internalEnsure":  p.updateConfig.SnapshotPaths.InternalEnsure,
   129  		"internalVersion": p.updateConfig.SnapshotPaths.InternalVersion,
   130  	}
   131  
   132  	return submodule.UpdateSuperproject(scm, p.updateConfig.Message, p.updateConfig.JiriProjectsPath, cipdPaths, p.jsonOutput, p.noCommit, p.updateConfig.SuperprojectRoot)
   133  }