diff --git a/state/project.go b/state/project.go index 843e138e..51c32227 100644 --- a/state/project.go +++ b/state/project.go @@ -428,12 +428,38 @@ func VendorUniverse(ctx context.Context, p string) error { } log.Ctx(ctx).Debug().Str("mod", p).Msg("vendoring universe") - if err := stdlib.Vendor(ctx, p); err != nil { + + // Vendor in a temporary directory + tmp, err := os.MkdirTemp(path.Join(p, "cue.mod", "pkg"), "vendor-*") + if err != nil { + return err + } + if err := stdlib.Vendor(ctx, tmp); err != nil { // FIXME(samalba): disabled install remote stdlib temporarily // if _, err := mod.Install(ctx, p, stdlib.ModuleName, ""); err != nil { return err } + // Semi-atomic swap of the vendor directory + // The following basically does: + // rm -rf cue.mod/pkg/MODULE.old + // mv cue.mod/pkg/MODULE cue.mod/pkg/MODULE.old + // mv VENDOR cue.mod/pkg/MODULE + // rm -rf cue.mod/pkg/MODULE.old + newStdlib := path.Join(p, stdlib.Path) + oldStdlib := newStdlib + ".old" + if err := os.RemoveAll(oldStdlib); err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } + if err := os.Rename(newStdlib, oldStdlib); err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } + defer os.RemoveAll(oldStdlib) + + if err := os.Rename(tmp, newStdlib); err != nil { + return err + } + return nil } diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index f14bb2b3..54f4a1f8 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -20,12 +20,7 @@ var ( Path = path.Join("cue.mod", "pkg", ModuleName) ) -func Vendor(ctx context.Context, mod string) error { - // Remove any existing copy of the universe - if err := os.RemoveAll(path.Join(mod, Path)); err != nil { - return err - } - +func Vendor(ctx context.Context, dest string) error { // Write the current version return fs.WalkDir(FS, ".", func(p string, entry fs.DirEntry, err error) error { if err != nil { @@ -45,7 +40,7 @@ func Vendor(ctx context.Context, mod string) error { return fmt.Errorf("%s: %w", p, err) } - overlayPath := path.Join(mod, Path, p) + overlayPath := path.Join(dest, p) if err := os.MkdirAll(filepath.Dir(overlayPath), 0755); err != nil { return err