Skip to content

Commit b359069

Browse files
feat: auto-reclone git dependencies when pull fails
Signed-off-by: JordanGoasdoue <[email protected]>
1 parent c29cdf5 commit b359069

File tree

1 file changed

+50
-7
lines changed
  • pkg/devspace/dependency/util

1 file changed

+50
-7
lines changed

pkg/devspace/dependency/util/util.go

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ import (
1010
"strings"
1111
"sync"
1212

13-
"github.com/loft-sh/devspace/pkg/util/encoding"
14-
1513
"github.com/loft-sh/devspace/pkg/devspace/config/constants"
1614
"github.com/loft-sh/devspace/pkg/devspace/config/versions/latest"
15+
"github.com/loft-sh/devspace/pkg/util/encoding"
1716
"github.com/loft-sh/devspace/pkg/util/git"
1817
"github.com/loft-sh/devspace/pkg/util/log"
1918
"github.com/mitchellh/go-homedir"
@@ -146,12 +145,18 @@ func DownloadDependency(ctx context.Context, workingDirectory string, source *la
146145

147146
// Git pull
148147
if !source.DisablePull && source.Revision == "" {
149-
err = repo.Pull(ctx)
150-
if err != nil {
151-
log.Warn(err)
152-
}
148+
if err := repo.Pull(ctx); err != nil {
149+
log.Infof("Git pull failed for dependency '%s': %v", gitPath, err)
150+
log.Infof("Attempting automatic recovery by re-cloning")
151+
152+
if err := recloneDependency(ctx, localPath, gitCloneOptions, gitPath, log); err != nil {
153+
return "", errors.Wrap(err, "git pull failed and automatic recovery failed")
154+
}
153155

154-
log.Debugf("Pulled %s", gitPath)
156+
log.Donef("Successfully recovered dependency '%s'", gitPath)
157+
} else {
158+
log.Debugf("Pulled %s", gitPath)
159+
}
155160
}
156161

157162
// Resolve local source
@@ -257,3 +262,41 @@ func GetDependencyID(source *latest.SourceConfig) (string, error) {
257262
func isURL(path string) bool {
258263
return strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://")
259264
}
265+
266+
func recloneDependency(ctx context.Context, localPath string, gitCloneOptions git.CloneOptions, gitPath string, log log.Logger) error {
267+
// Clone to temporary location to avoid losing old version if clone fails
268+
tmpPath := localPath + ".tmp-reclone"
269+
270+
// Clean up any leftover tmp directory from previous failed attempts
271+
_ = os.RemoveAll(tmpPath)
272+
273+
// Create new repository in tmp location
274+
repo, err := git.NewGitCLIRepository(ctx, tmpPath)
275+
if err != nil {
276+
return errors.Wrap(err, "create temporary git repository")
277+
}
278+
279+
// Try to clone
280+
err = repo.Clone(ctx, gitCloneOptions)
281+
if err != nil {
282+
gitCloneOptions.URL = switchURLType(gitPath)
283+
err = repo.Clone(ctx, gitCloneOptions)
284+
if err != nil {
285+
_ = os.RemoveAll(tmpPath)
286+
return errors.Wrap(err, "clone repository")
287+
}
288+
}
289+
290+
// Clone succeeded - now replace old version atomically
291+
if err := os.RemoveAll(localPath); err != nil {
292+
_ = os.RemoveAll(tmpPath)
293+
return errors.Wrap(err, "remove old dependency cache")
294+
}
295+
296+
// Atomic rename (both paths in same directory, so same filesystem)
297+
if err := os.Rename(tmpPath, localPath); err != nil {
298+
return errors.Wrap(err, "move new dependency into place")
299+
}
300+
301+
return nil
302+
}

0 commit comments

Comments
 (0)