this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cue/load: error when loading packages from inside cue.mod

Running cue commands from inside the cue.mod directory is a common
mistake for new users. Previously, commands like "cue export ." would
silently work, but others could easily fail in confusing ways.

Now, attempting to load packages from inside cue.mod produces a clear
error message: "cannot load packages inside the cue.mod directory"

The legacy directories cue.mod/pkg, cue.mod/usr, and cue.mod/gen
continue to work for backwards compatibility, as they are still
supported for placing package dependencies. Given that CUE packages
in those locations was commonplace, don't break direct loading of those
directories unless we really have to.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
Change-Id: I9defb828d6a0c0b041b21466efce5af9e21efa6a
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1229769
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Reviewed-by: Matthew Sackman <matthew@cue.works>

+65 -20
+4
cmd/cue/cmd/modget.go
··· 23 23 "github.com/spf13/cobra" 24 24 25 25 "cuelang.org/go/internal/mod/modload" 26 + "cuelang.org/go/internal/mod/modpkgload" 26 27 "cuelang.org/go/mod/modfile" 27 28 ) 28 29 ··· 115 116 // TODO this logic is duplicated in multiple places. We should 116 117 // consider deduplicating it. 117 118 dir := rootWorkingDir() 119 + if modpkgload.InsideCueMod(dir) { 120 + return "", fmt.Errorf("cannot run this command inside the cue.mod directory") 121 + } 118 122 for { 119 123 if _, err := os.Stat(filepath.Join(dir, "cue.mod")); err == nil { 120 124 return dir, nil
+22 -20
cmd/cue/cmd/testdata/script/inside_cuemod.txtar
··· 13 13 exec cue export cue.mod/module.cue 14 14 cmp stdout $WORK/export-module.stdout 15 15 16 - # From cue.mod, reaching into the module root. 17 - # TODO: none of these should work; we should guide the user outside of cue.mod. 18 - # Note that some of these did not work on Windows either. 16 + # From cue.mod, reaching into the module root, all commands should fail. 19 17 cd $WORK/cue.mod 20 - [!windows] exec cue export .. 21 - exec cue export ../root.cue 18 + ! exec cue export .. 19 + stderr 'cannot load packages inside the cue.mod directory' 20 + ! exec cue export ../root.cue 21 + stderr 'cannot load packages inside the cue.mod directory' 22 22 23 - # From cue.mod. 24 - # TODO: none of these should work; we should guide the user outside of cue.mod. 25 - cd $WORK/cue.mod 26 - exec cue mod tidy 27 - exec cue export . 28 - exec cue export somefile.cue 29 - exec cue export module.cue 30 - # Interestingly, these fail but with internal or confusing errors. 23 + # From cue.mod, all commands should fail. 24 + ! exec cue export . 25 + stderr 'cannot load packages inside the cue.mod directory' 26 + ! exec cue export somefile.cue 27 + stderr 'cannot load packages inside the cue.mod directory' 28 + ! exec cue export module.cue 29 + stderr 'cannot load packages inside the cue.mod directory' 31 30 ! exec cue export ./subdir 32 - stderr 'internal error:.*non-internal package' 31 + stderr 'cannot load packages inside the cue.mod directory' 33 32 ! exec cue vet ./... 34 - stderr 'pattern not allowed in external package path' 33 + stderr 'cannot load packages inside the cue.mod directory' 34 + ! exec cue mod tidy 35 + stderr 'cannot run this command inside the cue.mod directory' 35 36 36 - # From cue.mod/subdir. 37 - # TODO: none of these should work; we should guide the user outside of cue.mod. 37 + # From cue.mod/subdir, all commands should fail. 38 38 cd $WORK/cue.mod/subdir 39 - exec cue mod tidy 40 - exec cue export . 39 + ! exec cue export . 40 + stderr 'cannot load packages inside the cue.mod directory' 41 + ! exec cue mod tidy 42 + stderr 'cannot run this command inside the cue.mod directory' 41 43 42 - # From cue.mod/pkg/foo.com/bar. 44 + # From cue.mod/pkg/foo.com/bar, loading CUE should work. 43 45 # Arguably this should not work, but historically it has, and placing packages 44 46 # in this directory structure is still supported as the old mechanism 45 47 # to add package dependencies. For now, it continues to work.
+4
cue/load/config.go
··· 29 29 "cuelang.org/go/cue/parser" 30 30 "cuelang.org/go/cue/token" 31 31 "cuelang.org/go/internal" 32 + "cuelang.org/go/internal/mod/modpkgload" 32 33 "cuelang.org/go/mod/modconfig" 33 34 "cuelang.org/go/mod/modfile" 34 35 "cuelang.org/go/mod/module" ··· 360 361 } 361 362 } else if c.Dir, err = filepath.Abs(c.Dir); err != nil { 362 363 return nil, err 364 + } 365 + if modpkgload.InsideCueMod(c.Dir) { 366 + return nil, fmt.Errorf("cannot load packages inside the %s directory", modDir) 363 367 } 364 368 365 369 // TODO: we could populate this already with absolute file paths,
+35
internal/mod/modpkgload/pkgload.go
··· 5 5 "fmt" 6 6 "io/fs" 7 7 "maps" 8 + "path/filepath" 8 9 "runtime" 9 10 "slices" 10 11 "strings" ··· 430 431 firstElem, _, _ := strings.Cut(pkgPath, "/") 431 432 return !strings.Contains(firstElem, ".") 432 433 } 434 + 435 + // InsideCueMod reports whether absDir is inside a cue.mod directory, 436 + // excluding the legacy directories cue.mod/{pkg,usr,gen}, 437 + // which are still supported for placing package dependencies. 438 + // 439 + // For example, /foo/cue.mod and /foo/cue.mod/bar are inside cue.mod, 440 + // but /foo, /foo/cue.modx, and /foo/cue.mod/pkg/example.com are not. 441 + // 442 + // absDir must be an absolute system path that is clean; see [filepath.Clean]. 443 + func InsideCueMod(absDir string) bool { 444 + lastPart := "" 445 + for { 446 + dir, base, found := cutLast(absDir, string(filepath.Separator)) 447 + if base == "cue.mod" { 448 + switch lastPart { 449 + case "pkg", "usr", "gen": 450 + return false 451 + } 452 + return true 453 + } 454 + if !found { 455 + return false 456 + } 457 + absDir = dir 458 + lastPart = base 459 + } 460 + } 461 + 462 + func cutLast(s, sep string) (before, after string, found bool) { 463 + if i := strings.LastIndex(s, sep); i >= 0 { 464 + return s[:i], s[i+len(sep):], true 465 + } 466 + return "", s, false 467 + }