go.fuchsia.dev/jiri@v0.0.0-20240502161911-b66513b29486/cmd/jiri/update.go (about)

     1  // Copyright 2015 The Vanadium Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"time"
    11  
    12  	"go.fuchsia.dev/jiri"
    13  	"go.fuchsia.dev/jiri/cmdline"
    14  	"go.fuchsia.dev/jiri/project"
    15  	"go.fuchsia.dev/jiri/retry"
    16  )
    17  
    18  var (
    19  	gcFlag               bool
    20  	localManifestFlag    bool
    21  	attemptsFlag         uint
    22  	autoupdateFlag       bool
    23  	forceAutoupdateFlag  bool
    24  	rebaseUntrackedFlag  bool
    25  	hookTimeoutFlag      uint
    26  	fetchPkgsTimeoutFlag uint
    27  	rebaseAllFlag        bool
    28  	rebaseCurrentFlag    bool
    29  	rebaseSubmodulesFlag bool
    30  	rebaseTrackedFlag    bool
    31  	runHooksFlag         bool
    32  	fetchPkgsFlag        bool
    33  	overrideOptionalFlag bool
    34  	packagesToSkipFlag   arrayFlag
    35  )
    36  
    37  const (
    38  	MIN_EXECUTION_TIMING_THRESHOLD time.Duration = time.Duration(30) * time.Minute        // 30 min
    39  	MAX_EXECUTION_TIMING_THRESHOLD time.Duration = time.Duration(2) * time.Hour * 24 * 14 // 2 weeks
    40  )
    41  
    42  func init() {
    43  	cmdUpdate.Flags.BoolVar(&gcFlag, "gc", true, "Garbage collect obsolete repositories.")
    44  	cmdUpdate.Flags.BoolVar(&localManifestFlag, "local-manifest", false, "Use local manifest")
    45  	cmdUpdate.Flags.UintVar(&attemptsFlag, "attempts", 3, "Number of attempts before failing.")
    46  	cmdUpdate.Flags.BoolVar(&autoupdateFlag, "autoupdate", true, "Automatically update to the new version.")
    47  	cmdUpdate.Flags.BoolVar(&forceAutoupdateFlag, "force-autoupdate", false, "Always update to the current version.")
    48  	cmdUpdate.Flags.BoolVar(&rebaseUntrackedFlag, "rebase-untracked", false, "Rebase untracked branches onto HEAD.")
    49  	cmdUpdate.Flags.UintVar(&hookTimeoutFlag, "hook-timeout", project.DefaultHookTimeout, "Timeout in minutes for running the hooks operation.")
    50  	cmdUpdate.Flags.UintVar(&fetchPkgsTimeoutFlag, "fetch-packages-timeout", project.DefaultPackageTimeout, "Timeout in minutes for fetching prebuilt packages using cipd.")
    51  	cmdUpdate.Flags.BoolVar(&rebaseAllFlag, "rebase-all", false, "Rebase all tracked branches. Also rebase all untracked branches if -rebase-untracked is passed")
    52  	cmdUpdate.Flags.BoolVar(&rebaseCurrentFlag, "rebase-current", false, "Deprecated. Implies -rebase-tracked. Would be removed in future.")
    53  	cmdUpdate.Flags.BoolVar(&rebaseSubmodulesFlag, "rebase-submodules", false, "Rebase current tracked branches for submodules.")
    54  	cmdUpdate.Flags.BoolVar(&rebaseTrackedFlag, "rebase-tracked", false, "Rebase current tracked branches instead of fast-forwarding them.")
    55  	cmdUpdate.Flags.BoolVar(&runHooksFlag, "run-hooks", true, "Run hooks after updating sources.")
    56  	cmdUpdate.Flags.BoolVar(&fetchPkgsFlag, "fetch-packages", true, "Use cipd to fetch packages.")
    57  	cmdUpdate.Flags.BoolVar(&overrideOptionalFlag, "override-optional", false, "Override existing optional attributes in the snapshot file with current jiri settings")
    58  	cmdUpdate.Flags.Var(&packagesToSkipFlag, "package-to-skip", "Skip fetching this package. Repeatable.")
    59  }
    60  
    61  // cmdUpdate represents the "jiri update" command.
    62  var cmdUpdate = &cmdline.Command{
    63  	Runner: jiri.RunnerFunc(runUpdate),
    64  	Name:   "update",
    65  	Short:  "Update all jiri projects",
    66  	Long: `
    67  Updates all projects. The sequence in which the individual updates happen
    68  guarantees that we end up with a consistent workspace. The set of projects
    69  to update is described in the manifest.
    70  
    71  Run "jiri help manifest" for details on manifests.
    72  `,
    73  	ArgsName: "<file or url>",
    74  	ArgsLong: "<file or url> points to snapshot to checkout.",
    75  }
    76  
    77  func runUpdate(jirix *jiri.X, args []string) error {
    78  	if len(args) > 1 {
    79  		return jirix.UsageErrorf("unexpected number of arguments")
    80  	}
    81  
    82  	if attemptsFlag < 1 {
    83  		return jirix.UsageErrorf("Number of attempts should be >= 1")
    84  	}
    85  	jirix.Attempts = attemptsFlag
    86  
    87  	if autoupdateFlag {
    88  		// Try to update Jiri itself.
    89  		if err := retry.Function(jirix, func() error {
    90  			return jiri.UpdateAndExecute(forceAutoupdateFlag)
    91  		}, fmt.Sprintf("download jiri binary"), retry.AttemptsOpt(jirix.Attempts)); err != nil {
    92  			fmt.Printf("warning: automatic update failed: %v\n", err)
    93  		}
    94  	}
    95  	if rebaseCurrentFlag {
    96  		jirix.Logger.Warningf("Flag -rebase-current has been deprecated, please use -rebase-tracked.\n\n")
    97  		rebaseTrackedFlag = true
    98  	}
    99  
   100  	if len(args) > 0 {
   101  		jirix.OverrideOptional = overrideOptionalFlag
   102  		if err := project.CheckoutSnapshot(jirix, args[0], gcFlag, runHooksFlag, fetchPkgsFlag, hookTimeoutFlag, fetchPkgsTimeoutFlag, packagesToSkipFlag); err != nil {
   103  			return err
   104  		}
   105  	} else {
   106  		lastSnapshot := jirix.UpdateHistoryLatestLink()
   107  		duration := time.Duration(0)
   108  		if info, err := os.Stat(lastSnapshot); err == nil {
   109  			duration = time.Since(info.ModTime())
   110  			if duration < MIN_EXECUTION_TIMING_THRESHOLD || duration > MAX_EXECUTION_TIMING_THRESHOLD {
   111  				duration = time.Duration(0)
   112  			}
   113  		}
   114  
   115  		err := project.UpdateUniverse(jirix, gcFlag, localManifestFlag,
   116  			rebaseTrackedFlag, rebaseUntrackedFlag, rebaseAllFlag, runHooksFlag, fetchPkgsFlag, rebaseSubmodulesFlag, hookTimeoutFlag, fetchPkgsTimeoutFlag, packagesToSkipFlag)
   117  		if err2 := project.WriteUpdateHistorySnapshot(jirix, nil, nil, localManifestFlag); err2 != nil {
   118  			if err != nil {
   119  				return fmt.Errorf("while updating: %s, while writing history: %s", err, err2)
   120  			}
   121  			return fmt.Errorf("while writing history: %s", err2)
   122  		}
   123  		if err != nil {
   124  			return err
   125  		}
   126  
   127  		// Only track on successful update
   128  		if duration.Nanoseconds() > 0 {
   129  			jirix.AnalyticsSession.AddCommandExecutionTiming("update", duration)
   130  		}
   131  	}
   132  
   133  	if jirix.Failures() != 0 {
   134  		return fmt.Errorf("Project update completed with non-fatal errors")
   135  	}
   136  
   137  	if err := project.WriteUpdateHistoryLog(jirix); err != nil {
   138  		jirix.Logger.Errorf("Failed to save jiri logs: %v", err)
   139  	}
   140  	return nil
   141  }