-
Notifications
You must be signed in to change notification settings - Fork 294
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This adds the capability to update a module version or add a new dependency. The functionality is more limited than that provided by `go get`: - arguments are modules, not packages - downgrade functionality is much more limited (cannot downgrade when that's in conflict with other requirements) - no support for arbitrary dependency constraints such as `<v1.2.3` - only "latest" and version prefixes are supported. Fixes #2867. Signed-off-by: Roger Peppe <[email protected]> Change-Id: I7fc4d8a2f033a48ee0761ae4dbbf5979711f6189 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1177696 Reviewed-by: Daniel Martí <[email protected]> TryBot-Result: CUEcueckoo <[email protected]>
- Loading branch information
Showing
5 changed files
with
405 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
// Copyright 2024 The CUE Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package cmd | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"cuelang.org/go/internal/mod/modload" | ||
) | ||
|
||
func newModGetCmd(c *Command) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "get", | ||
Short: "add and upgrade module dependencies", | ||
Long: `WARNING: THIS COMMAND IS EXPERIMENTAL. | ||
Get updates module dependencies, fetching new dependencies if | ||
needed and changing versions to specified versions. It can downgrade | ||
a version only when a higher version is not required by other | ||
dependencies. | ||
Each argument specifies a module path and optionally a version | ||
suffix. If there is no version suffix, the latest non-prerelease version | ||
of the module will be requested; alternatively a suffix of "@latest" | ||
also specifies the latest version. | ||
A version suffix can contain a major version only (@v1), a major and minor | ||
version (@v1.2) or full version (@v1.2.3). If minor or patch version is omitted, the | ||
latest non-prerelease version will be chosen that has the same major | ||
and minor versions. | ||
If the desired version cannot be chosen (for example because a | ||
dependency already uses a later version than the desired version), | ||
this command will fail. | ||
See "cue help environment" for details on how $CUE_REGISTRY is used to | ||
determine the modules registry. | ||
Note: you must enable the modules experiment with: | ||
export CUE_EXPERIMENT=modules | ||
for this command to work. | ||
`, | ||
RunE: mkRunE(c, runModGet), | ||
Args: cobra.MinimumNArgs(1), | ||
} | ||
|
||
return cmd | ||
} | ||
|
||
func runModGet(cmd *Command, args []string) error { | ||
reg, err := getCachedRegistry() | ||
if err != nil { | ||
return err | ||
} | ||
if reg == nil { | ||
return fmt.Errorf("modules experiment not enabled (enable with CUE_EXPERIMENT=modules)") | ||
} | ||
ctx := context.Background() | ||
modRoot, err := findModuleRoot() | ||
if err != nil { | ||
return err | ||
} | ||
mf, err := modload.UpdateVersions(ctx, os.DirFS(modRoot), ".", reg, args) | ||
if err != nil { | ||
return err | ||
} | ||
// TODO check whether it's changed or not. | ||
data, err := mf.Format() | ||
if err != nil { | ||
return fmt.Errorf("internal error: invalid module.cue file generated: %v", err) | ||
} | ||
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
# Check that cue mod get will upgrade an existing | ||
# dependency. | ||
cp cue.mod/module.cue module-original | ||
|
||
# cue mod get should update to v0.10.2 but not to the | ||
# latest prerelease v0.11.0-alpha | ||
exec cue mod get baz.org | ||
cmp cue.mod/module.cue want-module-1 | ||
exec cue export . | ||
cmp stdout want-stdout-1 | ||
|
||
# Check that the same thing happens when we use an | ||
# explicit @latest suffix. | ||
cp module-original cue.mod/module.cue | ||
exec cue mod get baz.org@latest | ||
cmp cue.mod/module.cue want-module-1 | ||
exec cue export . | ||
cmp stdout want-stdout-1 | ||
|
||
# We can update to the latest prerelease by providing an | ||
# explicit version. | ||
exec cue mod get [email protected] | ||
cmp cue.mod/module.cue want-module-2 | ||
exec cue export . | ||
cmp stdout want-stdout-2 | ||
|
||
# Check that we can also do the above by specifying | ||
# an explicit v0.11 prefix | ||
cp want-module-1 cue.mod/module.cue | ||
exec cue mod get [email protected] | ||
cmp cue.mod/module.cue want-module-2 | ||
exec cue export . | ||
cmp stdout want-stdout-2 | ||
|
||
# If we try it again without an argument, it'll downgrade. | ||
exec cue mod get baz.org | ||
cmp cue.mod/module.cue want-module-1 | ||
exec cue export . | ||
cmp stdout want-stdout-1 | ||
|
||
# If we try to downgrade when some other dependency | ||
# is reliant on a later version, we'll get an error. | ||
! exec cue mod get [email protected] | ||
stderr 'other requirements prevent changing module baz.org@v0 to version v0.5.0 \(actual selected version: v0.10.1\)' | ||
|
||
-- want-module-1 -- | ||
module: "main.org@v0" | ||
deps: { | ||
"bar.com@v0": { | ||
v: "v0.5.0" | ||
} | ||
"baz.org@v0": { | ||
v: "v0.10.2" | ||
} | ||
"example.com@v0": { | ||
v: "v0.0.1" | ||
} | ||
"foo.com/bar/hello@v0": { | ||
v: "v0.2.3" | ||
default: true | ||
} | ||
} | ||
-- want-stdout-1 -- | ||
{ | ||
"main": "main", | ||
"foo.com/bar/hello@v0": "v0.2.3", | ||
"bar.com@v0": "v0.5.0", | ||
"baz.org@v0": "v0.10.2", | ||
"example.com@v0": "v0.0.1" | ||
} | ||
-- want-module-2 -- | ||
module: "main.org@v0" | ||
deps: { | ||
"bar.com@v0": { | ||
v: "v0.5.0" | ||
} | ||
"baz.org@v0": { | ||
v: "v0.11.0-alpha" | ||
} | ||
"example.com@v0": { | ||
v: "v0.0.1" | ||
} | ||
"foo.com/bar/hello@v0": { | ||
v: "v0.2.3" | ||
default: true | ||
} | ||
} | ||
-- want-stdout-2 -- | ||
{ | ||
"main": "main", | ||
"foo.com/bar/hello@v0": "v0.2.3", | ||
"bar.com@v0": "v0.5.0", | ||
"baz.org@v0": "v0.11.0-alpha", | ||
"example.com@v0": "v0.0.1" | ||
} | ||
-- cue.mod/module.cue -- | ||
module: "main.org@v0" | ||
deps: { | ||
"bar.com@v0": { | ||
v: "v0.5.0" | ||
} | ||
"baz.org@v0": { | ||
v: "v0.10.1" | ||
} | ||
"example.com@v0": { | ||
v: "v0.0.1" | ||
} | ||
"foo.com/bar/hello@v0": { | ||
v: "v0.2.3" | ||
default: true | ||
} | ||
} | ||
|
||
-- main.cue -- | ||
package main | ||
import "example.com@v0:main" | ||
|
||
main | ||
|
||
-- _registry/example.com_v0.0.1/cue.mod/module.cue -- | ||
module: "example.com@v0" | ||
deps: { | ||
"foo.com/bar/hello@v0": v: "v0.2.3" | ||
"bar.com@v0": v: "v0.5.0" | ||
} | ||
|
||
-- _registry/example.com_v0.0.1/top.cue -- | ||
package main | ||
|
||
import a "foo.com/bar/hello" | ||
a | ||
main: "main" | ||
"example.com@v0": "v0.0.1" | ||
|
||
-- _registry/example.com_v0.1.2/cue.mod/module.cue -- | ||
module: "example.com@v0" | ||
|
||
-- _registry/example.com_v0.1.2/top.cue -- | ||
package main | ||
"example.com@v0": "v0.1.2" | ||
|
||
// TODO: import without a major version should | ||
// the major version from the module.cue file. | ||
main: "main" | ||
"example.com@v0": "v0.0.1" | ||
|
||
-- _registry/foo.com_bar_hello_v0.2.3/cue.mod/module.cue -- | ||
module: "foo.com/bar/hello@v0" | ||
deps: { | ||
"bar.com@v0": v: "v0.0.2" | ||
"baz.org@v0": v: "v0.10.1" | ||
} | ||
|
||
-- _registry/foo.com_bar_hello_v0.2.3/x.cue -- | ||
package hello | ||
import ( | ||
a "bar.com/bar@v0" | ||
b "baz.org@v0:baz" | ||
) | ||
"foo.com/bar/hello@v0": "v0.2.3" | ||
a | ||
b | ||
|
||
|
||
-- _registry/bar.com_v0.0.2/cue.mod/module.cue -- | ||
module: "bar.com@v0" | ||
deps: "baz.org@v0": v: "v0.0.2" | ||
|
||
-- _registry/bar.com_v0.0.2/bar/x.cue -- | ||
package bar | ||
import a "baz.org@v0:baz" | ||
"bar.com@v0": "v0.0.2" | ||
a | ||
|
||
|
||
-- _registry/bar.com_v0.5.0/cue.mod/module.cue -- | ||
module: "bar.com@v0" | ||
deps: "baz.org@v0": v: "v0.5.0" | ||
|
||
-- _registry/bar.com_v0.5.0/bar/x.cue -- | ||
package bar | ||
import a "baz.org@v0:baz" | ||
"bar.com@v0": "v0.5.0" | ||
a | ||
|
||
|
||
-- _registry/baz.org_v0.0.2/cue.mod/module.cue -- | ||
module: "baz.org@v0" | ||
|
||
-- _registry/baz.org_v0.0.2/baz.cue -- | ||
package baz | ||
"baz.org@v0": "v0.0.2" | ||
|
||
|
||
-- _registry/baz.org_v0.1.2/cue.mod/module.cue -- | ||
module: "baz.org@v0" | ||
|
||
-- _registry/baz.org_v0.1.2/baz.cue -- | ||
package baz | ||
"baz.org@v0": "v0.1.2" | ||
|
||
|
||
-- _registry/baz.org_v0.5.0/cue.mod/module.cue -- | ||
module: "baz.org@v0" | ||
|
||
-- _registry/baz.org_v0.5.0/baz.cue -- | ||
package baz | ||
"baz.org@v0": "v0.5.0" | ||
|
||
|
||
-- _registry/baz.org_v0.10.1/cue.mod/module.cue -- | ||
module: "baz.org@v0" | ||
|
||
-- _registry/baz.org_v0.10.1/baz.cue -- | ||
package baz | ||
"baz.org@v0": "v0.10.1" | ||
|
||
|
||
-- _registry/baz.org_v0.10.2/cue.mod/module.cue -- | ||
module: "baz.org@v0" | ||
|
||
-- _registry/baz.org_v0.10.2/baz.cue -- | ||
package baz | ||
"baz.org@v0": "v0.10.2" | ||
|
||
-- _registry/baz.org_v0.11.0-alpha/cue.mod/module.cue -- | ||
module: "baz.org@v0" | ||
|
||
-- _registry/baz.org_v0.11.0-alpha/baz.cue -- | ||
package baz | ||
"baz.org@v0": "v0.11.0-alpha" |
Oops, something went wrong.