From f26085535bcd247376ddcb525e6d944ad94ee3da Mon Sep 17 00:00:00 2001 From: Roger Peppe Date: Tue, 19 Dec 2023 11:10:14 +0000 Subject: [PATCH] cmd/cue: allow cue mod tidy in non-module root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The module-root-finding code somewhat duplicates similar code in both `internal/mod/modpkgload` and `cue/load` but the former isn't suitable because it's defined in terms of `io/fs.FS` and reusing the latter would require adding new public API. Also avoid writing the module file when there's no change. Signed-off-by: Roger Peppe Change-Id: I2fb76abedc43f6be41e8eb16762d5e400acfa77d Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1173826 Reviewed-by: Daniel Martí TryBot-Result: CUEcueckoo Unity-Result: CUE porcuepine --- cmd/cue/cmd/modtidy.go | 39 +++++++++++++++++-- .../testdata/script/modtidy_non_root.txtar | 17 ++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 cmd/cue/cmd/testdata/script/modtidy_non_root.txtar diff --git a/cmd/cue/cmd/modtidy.go b/cmd/cue/cmd/modtidy.go index ea292fe141a..3d326439b3e 100644 --- a/cmd/cue/cmd/modtidy.go +++ b/cmd/cue/cmd/modtidy.go @@ -15,6 +15,7 @@ package cmd import ( + "bytes" "context" "fmt" "os" @@ -53,12 +54,11 @@ func runModTidy(cmd *Command, args []string) error { return fmt.Errorf("no registry configured to upload to") } ctx := context.Background() - // TODO don't assume we're running in the module's root directory. - wd, err := os.Getwd() + modRoot, err := findModuleRoot() if err != nil { return err } - mf, err := modload.Load(ctx, os.DirFS(wd), ".", reg) + mf, err := modload.Load(ctx, os.DirFS(modRoot), ".", reg) if err != nil { return err } @@ -67,12 +67,43 @@ func runModTidy(cmd *Command, args []string) error { if err != nil { return fmt.Errorf("internal error: invalid module.cue file generated: %v", err) } - if err := os.WriteFile(filepath.Join("cue.mod", "module.cue"), data, 0o666); err != nil { + modPath := filepath.Join(modRoot, "cue.mod", "module.cue") + oldData, err := os.ReadFile(modPath) + if err != nil { + // Shouldn't happen because modload.Load returns an error + // if it can't load the module file. + return err + } + if bytes.Equal(data, oldData) { + return nil + } + if err := os.WriteFile(modPath, data, 0o666); err != nil { return err } return nil } +func findModuleRoot() (string, error) { + // TODO this logic is duplicated in multiple places. We should + // consider deduplicating it. + dir, err := os.Getwd() + if err != nil { + return "", err + } + for { + if _, err := os.Stat(filepath.Join(dir, "cue.mod")); err == nil { + return dir, nil + } else if !os.IsNotExist(err) { + return "", err + } + dir1 := filepath.Dir(dir) + if dir1 == dir { + return "", fmt.Errorf("module root not found") + } + dir = dir1 + } +} + func modCacheDir() (string, error) { if dir := os.Getenv("CUE_MODCACHE"); dir != "" { return dir, nil diff --git a/cmd/cue/cmd/testdata/script/modtidy_non_root.txtar b/cmd/cue/cmd/testdata/script/modtidy_non_root.txtar new file mode 100644 index 00000000000..1a2fc4aa4e2 --- /dev/null +++ b/cmd/cue/cmd/testdata/script/modtidy_non_root.txtar @@ -0,0 +1,17 @@ +# Check that cue mod tidy works OK even when not +# run in the module's root directory. + +cd x/y +exec cue mod tidy +cd $WORK +cmp cue.mod/module.cue want-module + +-- want-module -- +module: "main.org@v0" +-- cue.mod/module.cue -- +module: "main.org@v0" +-- x/y/z.cue -- +package y +-- _registry/example.com_v0.0.1/cue.mod/module.cue -- +// This file is just here to ensure that CUE_REGISTRY etc is set. +module: "example.com@v0"