diff --git a/cmd/cue/cmd/flags.go b/cmd/cue/cmd/flags.go index 2ebc90195f5..42d3c601e76 100644 --- a/cmd/cue/cmd/flags.go +++ b/cmd/cue/cmd/flags.go @@ -20,38 +20,39 @@ import ( // Common flags const ( - flagAll flagName = "all" - flagAllErrors flagName = "all-errors" - flagCheck flagName = "check" - flagDiff flagName = "diff" - flagDryRun flagName = "dry-run" - flagEscape flagName = "escape" - flagExpression flagName = "expression" - flagExt flagName = "ext" - flagFiles flagName = "files" - flagForce flagName = "force" - flagGlob flagName = "name" - flagIgnore flagName = "ignore" - flagInject flagName = "inject" - flagInjectVars flagName = "inject-vars" - flagInlineImports flagName = "inline-imports" - flagJSON flagName = "json" - flagList flagName = "list" - flagMerge flagName = "merge" - flagOut flagName = "out" - flagOutFile flagName = "outfile" - flagPackage flagName = "package" - flagPath flagName = "path" - flagProtoEnum flagName = "proto_enum" - flagProtoPath flagName = "proto_path" - flagRecursive flagName = "recursive" - flagSchema flagName = "schema" - flagSimplify flagName = "simplify" - flagSource flagName = "source" - flagStrict flagName = "strict" - flagTrace flagName = "trace" - flagVerbose flagName = "verbose" - flagWithContext flagName = "with-context" + flagAll flagName = "all" + flagAllErrors flagName = "all-errors" + flagCheck flagName = "check" + flagDiff flagName = "diff" + flagDryRun flagName = "dry-run" + flagEscape flagName = "escape" + flagExpression flagName = "expression" + flagExt flagName = "ext" + flagFiles flagName = "files" + flagForce flagName = "force" + flagGlob flagName = "name" + flagIgnore flagName = "ignore" + flagInject flagName = "inject" + flagInjectVars flagName = "inject-vars" + flagInlineImports flagName = "inline-imports" + flagJSON flagName = "json" + flagLanguageVersion flagName = "language-version" + flagList flagName = "list" + flagMerge flagName = "merge" + flagOut flagName = "out" + flagOutFile flagName = "outfile" + flagPackage flagName = "package" + flagPath flagName = "path" + flagProtoEnum flagName = "proto_enum" + flagProtoPath flagName = "proto_path" + flagRecursive flagName = "recursive" + flagSchema flagName = "schema" + flagSimplify flagName = "simplify" + flagSource flagName = "source" + flagStrict flagName = "strict" + flagTrace flagName = "trace" + flagVerbose flagName = "verbose" + flagWithContext flagName = "with-context" // Hidden flags. flagCpuProfile flagName = "cpuprofile" diff --git a/cmd/cue/cmd/modedit.go b/cmd/cue/cmd/modedit.go index 1844d3a1336..b6d95fd1b39 100644 --- a/cmd/cue/cmd/modedit.go +++ b/cmd/cue/cmd/modedit.go @@ -21,6 +21,8 @@ import ( "path/filepath" "strconv" + "cuelang.org/go/internal/cueversion" + "cuelang.org/go/internal/mod/semver" "cuelang.org/go/mod/modfile" "cuelang.org/go/mod/module" "github.com/spf13/cobra" @@ -54,8 +56,9 @@ Note that this command is not yet stable and may be changed. RunE: mkRunE(c, editCmd.run), Args: cobra.ExactArgs(0), } - addFlagVar(cmd, flagFunc(editCmd.flagSource), "source", "set the source field") + addFlagVar(cmd, flagFunc(editCmd.flagSource), string(flagSource), "set the source field") addFlagVar(cmd, boolFlagFunc(editCmd.flagDropSource), "drop-source", "remove the source field") + addFlagVar(cmd, flagFunc(editCmd.flagLanguageVersion), string(flagLanguageVersion), "set language.version ('current' means current language version)") addFlagVar(cmd, flagFunc(editCmd.flagModule), "module", "set the module path") addFlagVar(cmd, flagFunc(editCmd.flagRequire), "require", "add a required module@version") addFlagVar(cmd, flagFunc(editCmd.flagDropRequire), "drop-require", "remove a requirement") @@ -88,7 +91,7 @@ func (c *modEditCmd) run(cmd *Command, args []string) error { } newData, err := mf.Format() if err != nil { - return fmt.Errorf("internal error: invalid module.cue file generated: %v", err) + return fmt.Errorf("invalid resulting module.cue file after edits: %v", err) } if bytes.Equal(newData, data) { return nil @@ -128,6 +131,41 @@ func (c *modEditCmd) flagDropSource(arg bool) error { return nil } +func (c *modEditCmd) flagLanguageVersion(arg string) error { + editFunc, err := addLanguageVersion(arg) + if err != nil { + return err + } + c.addEdit(editFunc) + return nil +} + +func addLanguageVersion(v string) (func(*modfile.File) error, error) { + if v == "current" { + v = cueversion.LanguageVersion() + } else { + if semver.Canonical(v) != v { + return nil, fmt.Errorf("language version %q is not canonical (must include major, minor and patch versions)", v) + } + + if min := modfile.EarliestClosedSchemaVersion(); semver.Compare(v, min) < 0 { + // TODO(rogpeppe) We might want to relax this to allow people to + // declare an earlier language version (see https://cuelang.org/issue/3145). + return nil, fmt.Errorf("language version %q is too early for module.cue schema (earliest allowed is %s)", v, min) + } + if max := cueversion.LanguageVersion(); semver.Compare(v, max) > 0 { + return nil, fmt.Errorf("language version %q may not be after current language version %s", v, max) + } + } + return func(f *modfile.File) error { + if f.Language == nil { + f.Language = &modfile.Language{} + } + f.Language.Version = v + return nil + }, nil +} + func (c *modEditCmd) flagModule(arg string) error { if err := module.CheckPath(arg); err != nil { return err diff --git a/cmd/cue/cmd/modinit.go b/cmd/cue/cmd/modinit.go index fe219314389..51ad8383332 100644 --- a/cmd/cue/cmd/modinit.go +++ b/cmd/cue/cmd/modinit.go @@ -22,7 +22,6 @@ import ( "github.com/spf13/cobra" - "cuelang.org/go/internal/cueversion" "cuelang.org/go/mod/modfile" "cuelang.org/go/mod/module" ) @@ -45,6 +44,7 @@ in the module. cmd.Flags().BoolP(string(flagForce), "f", false, "force moving old-style cue.mod file") cmd.Flags().String(string(flagSource), "", "set the source field") + cmd.Flags().String(string(flagLanguageVersion), "current", "set the language version ('current' means current language version)") return cmd } @@ -84,8 +84,12 @@ func runModInit(cmd *Command, args []string) (err error) { return err } } - mf.Language = &modfile.Language{ - Version: cueversion.LanguageVersion(), + editFunc, err := addLanguageVersion(flagLanguageVersion.String(cmd)) + if err != nil { + return err + } + if err := editFunc(mf); err != nil { + return err } err = os.Mkdir(mod, 0755) diff --git a/cmd/cue/cmd/testdata/script/modedit_initial.txtar b/cmd/cue/cmd/testdata/script/modedit_initial.txtar index 3f1d7360c43..f9536345ea4 100644 --- a/cmd/cue/cmd/testdata/script/modedit_initial.txtar +++ b/cmd/cue/cmd/testdata/script/modedit_initial.txtar @@ -19,6 +19,27 @@ cmp cue.mod/module.cue want-module-4 exec cue mod edit --module othermain.org@v1 cmp cue.mod/module.cue want-module-5 +# Set specific version. +exec cue mod edit --language-version v0.9.2 +cmp cue.mod/module.cue want-module-6 + +# Set latest version. +exec cue mod edit --language-version current +cmpenv cue.mod/module.cue want-module-7 + +# Set version earlier than earliest module schema version. +! exec cue mod edit --language-version v0.4.3 +cmp stderr want-stderr-8 + +# Set version too new. +! exec cue mod edit --language-version v2.3.4 +cmpenv stderr want-stderr-9 + +# Check that it's an error to set the version earlier than +# allowed by some of the fields already present. +exec cue mod edit --source self +! exec cue mod edit --language-version v0.8.0 +cmp stderr want-stderr-10 -- cue.mod/module.cue -- module: "main.org@v0" @@ -66,3 +87,19 @@ module: "othermain.org@v1" language: { version: "v0.9.0-alpha.0" } +-- want-module-6 -- +module: "othermain.org@v1" +language: { + version: "v0.9.2" +} +-- want-module-7 -- +module: "othermain.org@v1" +language: { + version: "$CUE_LANGUAGE_VERSION" +} +-- want-stderr-8 -- +invalid argument "v0.4.3" for "--language-version" flag: language version "v0.4.3" is too early for module.cue schema (earliest allowed is v0.8.0-alpha.0) +-- want-stderr-9 -- +invalid argument "v2.3.4" for "--language-version" flag: language version "v2.3.4" may not be after current language version $CUE_LANGUAGE_VERSION +-- want-stderr-10 -- +invalid resulting module.cue file after edits: cannot parse result: invalid module.cue file: source field is not allowed at this language version; need at least v0.9.0-alpha.0 diff --git a/cmd/cue/cmd/testdata/script/modinit_with_explicit_version.txtar b/cmd/cue/cmd/testdata/script/modinit_with_explicit_version.txtar new file mode 100644 index 00000000000..30851beae6a --- /dev/null +++ b/cmd/cue/cmd/testdata/script/modinit_with_explicit_version.txtar @@ -0,0 +1,43 @@ +# Check we can initialize the module with an explicit version. + +# Set current version. +exec cue mod init --language-version current foo.example +cmpenv cue.mod/module.cue want-module-1 +rm cue.mod + +# Set specific version. +exec cue mod init --language-version v0.9.2 foo.example +cmp cue.mod/module.cue want-module-2 +rm cue.mod + +# Set version earlier than earliest module schema version. +! exec cue mod init --language-version v0.4.3 foo.example +cmp stderr want-stderr-3 +rm cue.mod + +# Set version too new. +! exec cue mod init --language-version v2.3.4 foo.example +cmp stderr want-stderr-4 +rm cue.mod + +# Set version that's incompatible with the source field. +! exec cue mod init --language-version v0.8.0 --source self foo.example +cmp stderr want-stderr-5 +rm cue.mod + +-- want-module-1 -- +module: "foo.example" +language: { + version: "$CUE_LANGUAGE_VERSION" +} +-- want-module-2 -- +module: "foo.example" +language: { + version: "v0.9.2" +} +-- want-stderr-3 -- +language version "v0.4.3" is too early for module.cue schema (earliest allowed is v0.8.0-alpha.0) +-- want-stderr-4 -- +language version "v2.3.4" may not be after current language version v0.10.0 +-- want-stderr-5 -- +cannot parse result: invalid module.cue file: source field is not allowed at this language version; need at least v0.9.0-alpha.0 diff --git a/cmd/cue/cmd/testdata/script/modinit_without_version.txtar b/cmd/cue/cmd/testdata/script/modinit_without_version.txtar index d4a9e36668d..a4e63ec96f1 100644 --- a/cmd/cue/cmd/testdata/script/modinit_without_version.txtar +++ b/cmd/cue/cmd/testdata/script/modinit_without_version.txtar @@ -1,13 +1,12 @@ # Check that cue mod init is independent of the module version; # even though CUE's current module version will often be a v0 pseudo-version # or a pre-release, we will always use the current language version in init. -env-fill want-module exec cue mod init foo.example -cmp cue.mod/module.cue want-module +cmpenv cue.mod/module.cue want-module # cue mod tidy should be a no-op after cue mod init exec cue mod tidy -cmp cue.mod/module.cue want-module +cmpenv cue.mod/module.cue want-module -- want-module -- module: "foo.example" diff --git a/mod/modfile/modfile.go b/mod/modfile/modfile.go index 3c06a848fd6..3991af3590c 100644 --- a/mod/modfile/modfile.go +++ b/mod/modfile/modfile.go @@ -156,7 +156,7 @@ func (f *File) Format() ([]byte, error) { // before formatting the output. f1, err := ParseNonStrict(data, "-") if err != nil { - return nil, fmt.Errorf("cannot round-trip module file: %v", strings.TrimSuffix(errors.Details(err, nil), "\n")) + return nil, fmt.Errorf("cannot parse result: %v", strings.TrimSuffix(errors.Details(err, nil), "\n")) } if f.Language != nil && f1.actualSchemaVersion == "v0.0.0" { // It's not a legacy module file (because the language field is present) @@ -363,7 +363,7 @@ func FixLegacy(modfile []byte, filename string) (*File, error) { } f, err = ParseNonStrict(data, "fixed-"+filename) if err != nil { - return nil, fmt.Errorf("cannot round-trip fixed module file %q: %v", data, err) + return nil, fmt.Errorf("cannot parse resulting module file %q: %v", data, err) } return f, nil } diff --git a/mod/modfile/modfile_test.go b/mod/modfile/modfile_test.go index 6629d84e93f..9e187e488b0 100644 --- a/mod/modfile/modfile_test.go +++ b/mod/modfile/modfile_test.go @@ -500,7 +500,7 @@ language: { Version: "v0.4.3", }, }, - wantError: `cannot round-trip module file: cannot find schema suitable for reading module file with language version "v0.4.3"`, + wantError: `cannot parse result: cannot find schema suitable for reading module file with language version "v0.4.3"`, }, { name: "WithInvalidModuleVersion", file: &File{ @@ -509,7 +509,7 @@ language: { Version: "badversion--", }, }, - wantError: `cannot round-trip module file: language version "badversion--" in module.cue is not valid semantic version`, + wantError: `cannot parse result: language version "badversion--" in module.cue is not valid semantic version`, }, { name: "WithNonNilEmptyDeps", file: &File{