this repo has no description
0
fork

Configure Feed

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

cmd/cue: suggest `cue mod fix` when `language.version` is missing

That is, if a user tries to use `mod tidy`, `mod get`, or `mod publish`
on a module without a `language.version` field, which is now mandatory,
suggest what the user should run to resolve that issue.

Note that we don't apply this change to non-mod commands such as
`cue export` because, as the TODOs explain, those scenarios should work
for the sake of backwards compatibility as a downstream user.

We also don't try to make this be consistent for all `cue mod` commands
as it's not clear that all of them should require `language.version`.
For example, `mod init` creates a module, `mod fix` can add the field,
and we should arguably teach `mod edit` to add or set the field too.
For now, add the suggestion to the three `cue mod` commands which are
most likely to be used when developing or publishing a CUE module.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
Change-Id: I11cef96900b2a5984ad6c07abbd705108c2c2759
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1195784
Reviewed-by: Paul Jolly <paul@myitcv.io>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>

+27 -18
+1 -1
cmd/cue/cmd/modget.go
··· 77 77 } 78 78 mf, err := modload.UpdateVersions(ctx, os.DirFS(modRoot), ".", reg, args) 79 79 if err != nil { 80 - return err 80 + return suggestModCommand(err) 81 81 } 82 82 // TODO check whether it's changed or not. 83 83 data, err := mf.Format()
+1 -1
cmd/cue/cmd/modpublish.go
··· 122 122 return err 123 123 } 124 124 if err := modload.CheckTidy(ctx, os.DirFS(modRoot), ".", reg); err != nil { 125 - return suggestModTidy(err) 125 + return suggestModCommand(err) 126 126 } 127 127 128 128 modPath := filepath.Join(modRoot, "cue.mod/module.cue")
+12 -6
cmd/cue/cmd/modtidy.go
··· 24 24 "github.com/spf13/cobra" 25 25 26 26 "cuelang.org/go/internal/mod/modload" 27 + "cuelang.org/go/mod/modfile" 27 28 ) 28 29 29 30 func newModTidyCmd(c *Command) *cobra.Command { ··· 67 68 } 68 69 if flagCheck.Bool(cmd) { 69 70 err := modload.CheckTidy(ctx, os.DirFS(modRoot), ".", reg) 70 - return suggestModTidy(err) 71 + return suggestModCommand(err) 71 72 } 72 73 mf, err := modload.Tidy(ctx, os.DirFS(modRoot), ".", reg) 73 74 if err != nil { 74 - return err 75 + return suggestModCommand(err) 75 76 } 76 77 data, err := mf.Format() 77 78 if err != nil { ··· 93 94 return nil 94 95 } 95 96 96 - // suggestModTidy rewrites [modload.ErrModuleNotTidy] errors 97 - // so that they suggest running `cue mod tidy` to the user. 98 - func suggestModTidy(err error) error { 97 + // suggestModCommand rewrites a non-nil error to suggest to the user 98 + // what command they could use to fix a problem. 99 + // [modload.ErrModuleNotTidy] suggests running `cue mod tidy`, 100 + // and [modfile.ErrNoLanguageVersion] suggests running `cue mod fix`. 101 + func suggestModCommand(err error) error { 99 102 notTidyErr := new(modload.ErrModuleNotTidy) 100 - if errors.As(err, &notTidyErr) { 103 + switch { 104 + case errors.Is(err, modfile.ErrNoLanguageVersion): 105 + err = fmt.Errorf("%w; run 'cue mod fix'", err) 106 + case errors.As(err, &notTidyErr): 101 107 if notTidyErr.Reason == "" { 102 108 err = fmt.Errorf("module is not tidy, use 'cue mod tidy'") 103 109 } else {
+1 -1
cmd/cue/cmd/testdata/script/modpublish_no_source.txtar
··· 49 49 language: version: "v0.5.0" 50 50 51 51 -- none/both.stderr -- 52 - no language version declared in module.cue 52 + no language version declared in module.cue; run 'cue mod fix' 53 53 -- none/cue.mod/module.cue -- 54 54 module: "test.example/none@v1"
+1 -1
cmd/cue/cmd/testdata/script/modtidy_with_version.txtar
··· 5 5 cmp stderr want-stderr 6 6 7 7 -- want-stderr -- 8 - no language version declared in module.cue 8 + no language version declared in module.cue; run 'cue mod fix' 9 9 -- cue.mod/module.cue -- 10 10 module: "main.org@v0" 11 11
+3 -3
cmd/cue/cmd/testdata/script/module_compatibility_backwards.txtar
··· 37 37 # Upstream development works once `cue mod fix` adds a language.version field. 38 38 # TODO(mvdan): we don't have anything like `cue mod edit -langversion` if the user wants an older version. 39 39 ! exec cue mod tidy --check 40 - stderr '^no language version declared in module.cue$' 40 + stderr '^no language version declared in module.cue; run ''cue mod fix''$' 41 41 ! exec cue mod get some.dependency 42 - stderr '^no language version declared in module.cue$' 42 + stderr '^no language version declared in module.cue; run ''cue mod fix''$' 43 43 ! exec cue mod publish v0.0.2 44 - stderr '^no language version declared in module.cue$' 44 + stderr '^no language version declared in module.cue; run ''cue mod fix''$' 45 45 exec cue mod fix 46 46 cmp cue.mod/module.cue ${WORK}/premodules-module.cue.fixed 47 47 exec cue export
+8 -5
mod/modfile/modfile.go
··· 220 220 return limits 221 221 }) 222 222 223 - // Parse verifies that the module file has correct syntax. 223 + // Parse verifies that the module file has correct syntax 224 + // and follows the schema following the required language.version field. 224 225 // The file name is used for error messages. 225 226 // All dependencies must be specified correctly: with major 226 - // versions in the module paths and canonical dependency 227 - // versions. 227 + // versions in the module paths and canonical dependency versions. 228 228 func Parse(modfile []byte, filename string) (*File, error) { 229 229 return parse(modfile, filename, true) 230 230 } ··· 349 349 return nil, errors.Wrapf(err, token.NoPos, "cannot determine language version") 350 350 } 351 351 if base.Language.Version == "" { 352 - // TODO is something different we could do here? 353 - return nil, fmt.Errorf("no language version declared in module.cue") 352 + return nil, ErrNoLanguageVersion 354 353 } 355 354 if !semver.IsValid(base.Language.Version) { 356 355 return nil, fmt.Errorf("language version %q in module.cue is not valid semantic version", base.Language.Version) ··· 465 464 module.Sort(mf.versions) 466 465 return mf, nil 467 466 } 467 + 468 + // ErrNoLanguageVersion is returned by [Parse] and [ParseNonStrict] 469 + // when a cue.mod/module.cue file lacks the `language.version` field. 470 + var ErrNoLanguageVersion = fmt.Errorf("no language version declared in module.cue") 468 471 469 472 func parseDataOnlyCUE(ctx *cue.Context, cueData []byte, filename string) (*ast.File, error) { 470 473 dec := encoding.NewDecoder(ctx, &build.File{