From edd9918fe20836b3644869f81f4998c460f1f54b Mon Sep 17 00:00:00 2001 From: Fritz Durchardt Date: Mon, 7 Aug 2023 08:57:07 +0200 Subject: [PATCH] feat: added lazy sync functionality to only do a sync, if the config has changed since the last sync. Added extra field to lock file in order to keep track of config changes. --- pkg/vendir/cmd/sync.go | 50 +++++++++++++++++++++++++++-- pkg/vendir/config/lock_config.go | 7 ++++ pkg/vendir/config/lock_directory.go | 1 + pkg/vendir/directory/directory.go | 15 ++++++++- 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/pkg/vendir/cmd/sync.go b/pkg/vendir/cmd/sync.go index c60882b9..62259e92 100644 --- a/pkg/vendir/cmd/sync.go +++ b/pkg/vendir/cmd/sync.go @@ -4,7 +4,10 @@ package cmd import ( + "crypto/sha256" + "encoding/hex" "fmt" + "gopkg.in/yaml.v3" "os" "path/filepath" "strings" @@ -30,6 +33,7 @@ type SyncOptions struct { Directories []string Locked bool + Lazy bool Chdir string AllowAllSymlinkDestinations bool @@ -50,6 +54,7 @@ func NewSyncCmd(o *SyncOptions) *cobra.Command { cmd.Flags().StringSliceVarP(&o.Directories, "directory", "d", nil, "Sync specific directory (format: dir/sub-dir[=local-dir])") cmd.Flags().BoolVarP(&o.Locked, "locked", "l", false, "Consult lock file to pull exact references (e.g. use git sha instead of branch name)") + cmd.Flags().BoolVar(&o.Lazy, "lazy", false, "Only fetch remote if vendir.yaml diverges from lock file") cmd.Flags().StringVar(&o.Chdir, "chdir", "", "Set current directory for process") cmd.Flags().BoolVar(&o.AllowAllSymlinkDestinations, "dangerous-allow-all-symlink-destinations", false, "Symlinks to all destinations are allowed") @@ -57,6 +62,37 @@ func NewSyncCmd(o *SyncOptions) *cobra.Command { return cmd } +func configUnchanged(vendirConfig ctlconf.Config, lockConfig ctlconf.LockConfig) bool { + for _, dir := range vendirConfig.Directories { + for _, content := range dir.Contents { + if !matchesLockConfig(dir.Path, content, lockConfig) { + return false + } + } + } + return true +} + +func matchesLockConfig(dir string, content ctlconf.DirectoryContents, lockConfig ctlconf.LockConfig) bool { + for _, lockDir := range lockConfig.Directories { + for _, lockContent := range lockDir.Contents { + if lockDir.Path == dir && lockContent.Path == content.Path { + yaml, err := yaml.Marshal(content) + if err != nil { + return false + } + hash := sha256.Sum256(yaml) + hashStr := hex.EncodeToString(hash[:]) + + if hashStr == lockContent.Hash { + return true + } + } + } + } + return false +} + func (o *SyncOptions) Run() error { if len(o.Chdir) > 0 { err := os.Chdir(o.Chdir) @@ -95,13 +131,21 @@ func (o *SyncOptions) Run() error { o.ui.PrintBlock(configBs) } - // If syncing against a lock file, apply lock information - // on top of existing config - if o.Locked { + if o.Lazy && ctlconf.LockFileExists(o.LockFile) { existingLockConfig, err := ctlconf.NewLockConfigFromFile(o.LockFile) if err != nil { return err } + if configUnchanged(conf, existingLockConfig) { + o.ui.PrintLinef("No changes in vendir.yaml since last sync. No need to sync.") + return nil + } + } + + // If syncing against a lock file, apply lock information + // on top of existing config + if o.Locked { + existingLockConfig, err := ctlconf.NewLockConfigFromFile(o.LockFile) err = conf.Lock(existingLockConfig) if err != nil { diff --git a/pkg/vendir/config/lock_config.go b/pkg/vendir/config/lock_config.go index d2a0c2cb..e50e517e 100644 --- a/pkg/vendir/config/lock_config.go +++ b/pkg/vendir/config/lock_config.go @@ -28,6 +28,13 @@ func NewLockConfig() LockConfig { } } +func LockFileExists(path string) bool { + if _, err := os.Stat(path); err != nil { + return false + } + return true +} + func NewLockConfigFromFile(path string) (LockConfig, error) { bs, err := os.ReadFile(path) if err != nil { diff --git a/pkg/vendir/config/lock_directory.go b/pkg/vendir/config/lock_directory.go index 41651e47..52811206 100644 --- a/pkg/vendir/config/lock_directory.go +++ b/pkg/vendir/config/lock_directory.go @@ -10,6 +10,7 @@ type LockDirectory struct { type LockDirectoryContents struct { Path string `json:"path"` + Hash string `json:"hash"` Git *LockDirectoryContentsGit `json:"git,omitempty"` Hg *LockDirectoryContentsHg `json:"hg,omitempty"` diff --git a/pkg/vendir/directory/directory.go b/pkg/vendir/directory/directory.go index 8baf5b07..5ea59daf 100644 --- a/pkg/vendir/directory/directory.go +++ b/pkg/vendir/directory/directory.go @@ -4,7 +4,10 @@ package directory import ( + "crypto/sha256" + "encoding/hex" "fmt" + "gopkg.in/yaml.v3" "os" "path/filepath" @@ -57,7 +60,17 @@ func (d *Directory) Sync(syncOpts SyncOpts) (ctlconf.LockDirectory, error) { return lockConfig, err } - lockDirContents := ctlconf.LockDirectoryContents{Path: contents.Path} + yaml, err := yaml.Marshal(contents) + if err != nil { + return lockConfig, err + } + hash := sha256.Sum256(yaml) + hashStr := hex.EncodeToString(hash[:]) + + lockDirContents := ctlconf.LockDirectoryContents{ + Path: contents.Path, + Hash: hashStr, + } skipFileFilter := false skipNewRootPath := false