this repo has no description
0
fork

Configure Feed

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

cmd/cue/cmd: require source when running cue mod publish

This is an experimental WIP implementation of the proposal discussed at
https://cuelang.org/discussion/3017.

Still to do in a followup CL: we need to add the git metadata to the
module manifest file.

For #3017.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: Ie7d5b0acda4690ee99d2873eed39183ce909a666
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1192907
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>

+168 -2
+62 -2
cmd/cue/cmd/modpublish.go
··· 16 16 17 17 import ( 18 18 "fmt" 19 + "io" 20 + "io/fs" 19 21 "os" 20 22 "path/filepath" 21 23 22 24 "github.com/spf13/cobra" 23 25 26 + "cuelang.org/go/internal/vcs" 24 27 "cuelang.org/go/mod/modfile" 25 28 "cuelang.org/go/mod/modregistry" 26 29 "cuelang.org/go/mod/module" ··· 50 53 } 51 54 52 55 func runModUpload(cmd *Command, args []string) error { 56 + ctx := cmd.Context() 53 57 resolver, err := getRegistryResolver() 54 58 if err != nil { 55 59 return err ··· 74 78 mv, err := module.NewVersion(mf.Module, args[0]) 75 79 if err != nil { 76 80 return fmt.Errorf("cannot form module version: %v", err) 81 + } 82 + if mf.Source == nil { 83 + // TODO print filename relative to current directory 84 + return fmt.Errorf("no source field found in cue.mod/module.cue") 77 85 } 78 86 zf, err := os.CreateTemp("", "cue-publish-") 79 87 if err != nil { ··· 83 91 defer zf.Close() 84 92 85 93 // TODO verify that all dependencies exist in the registry. 86 - if err := modzip.CreateFromDir(zf, mv, modRoot); err != nil { 87 - return err 94 + 95 + var vcsStatus vcs.Status 96 + 97 + switch mf.Source.Kind { 98 + case "self": 99 + if err := modzip.CreateFromDir(zf, mv, modRoot); err != nil { 100 + return err 101 + } 102 + default: 103 + vcsImpl, err := vcs.New(mf.Source.Kind, modRoot) 104 + if err != nil { 105 + return err 106 + } 107 + status, err := vcsImpl.Status(ctx) 108 + if err != nil { 109 + return err 110 + } 111 + if status.Uncommitted { 112 + // TODO implement --force to bypass this check 113 + return fmt.Errorf("VCS state is not clean") 114 + } 115 + files, err := vcsImpl.ListFiles(ctx, modRoot) 116 + if err != nil { 117 + return err 118 + } 119 + if err := modzip.Create[string](zf, mv, files, osFileIO{ 120 + modRoot: modRoot, 121 + }); err != nil { 122 + return err 123 + } 124 + vcsStatus = status 88 125 } 89 126 info, err := zf.Stat() 90 127 if err != nil { ··· 92 129 } 93 130 94 131 rclient := modregistry.NewClientWithResolver(resolver) 132 + _ = vcsStatus // TODO attach vcsStatus to PutModule metadata 95 133 if err := rclient.PutModule(backgroundContext(), mv, zf, info.Size()); err != nil { 96 134 return fmt.Errorf("cannot put module: %v", err) 97 135 } 98 136 fmt.Printf("published %s\n", mv) 99 137 return nil 100 138 } 139 + 140 + // osFileIO implements [modzip.FileIO] for filepath paths relative to 141 + // the module root directory, as returned by [vcs.VCS.ListFiles]. 142 + type osFileIO struct { 143 + modRoot string 144 + } 145 + 146 + func (osFileIO) Path(f string) string { 147 + return filepath.ToSlash(f) 148 + } 149 + 150 + func (fio osFileIO) Lstat(f string) (fs.FileInfo, error) { 151 + return os.Lstat(fio.absPath(f)) 152 + } 153 + 154 + func (fio osFileIO) Open(f string) (io.ReadCloser, error) { 155 + return os.Open(fio.absPath(f)) 156 + } 157 + 158 + func (fio osFileIO) absPath(f string) string { 159 + return filepath.Join(fio.modRoot, f) 160 + }
+11
cmd/cue/cmd/testdata/script/modpublish_no_source.txtar
··· 1 + # Test that cue mod publish fails when there is no 2 + # explicit source field in module.cue. 3 + 4 + env CUE_EXPERIMENT=modules 5 + ! exec cue mod publish v1.0.0 6 + env-fill want-stderr 7 + cmp stderr want-stderr 8 + -- want-stderr -- 9 + no source field found in cue.mod/module.cue 10 + -- cue.mod/module.cue -- 11 + module: "x.example@v1"
+11
cmd/cue/cmd/testdata/script/modpublish_no_vcs_directory.txtar
··· 1 + # Test that cue mod publish fails when there is 2 + # a VCS source specified but no VCS directory 3 + # can be found. 4 + 5 + env CUE_EXPERIMENT=modules 6 + ! exec cue mod publish v1.0.0 7 + stderr 'git VCS not found in any parent of ".+"' 8 + -- cue.mod/module.cue -- 9 + module: "x.example@v1" 10 + 11 + source: kind: "git"
+4
cmd/cue/cmd/testdata/script/registry_publish.txtar
··· 25 25 "example.com@v0": "v0.0.1" 26 26 -- main/cue.mod/module.cue -- 27 27 module: "main.org@v0" 28 + 29 + source: kind: "self" 30 + 28 31 deps: { 29 32 "bar.com@v0": v: "v0.5.0" 30 33 "baz.org@v0": v: "v0.10.1" ··· 43 46 44 47 -- example/cue.mod/module.cue -- 45 48 module: "example.com@v0" 49 + source: kind: "self" 46 50 deps: { 47 51 "bar.com@v0": v: "v0.5.0" 48 52 "baz.org@v0": v: "v0.10.1"
+2
cmd/cue/cmd/testdata/script/registry_publish_auth.txtar
··· 44 44 -- example/cue.mod/module.cue -- 45 45 module: "example.com@v0" 46 46 47 + source: kind: "self" 48 + 47 49 -- example/top.cue -- 48 50 package main 49 51
+2
cmd/cue/cmd/testdata/script/registry_publish_non_root.txtar
··· 15 15 -- example/cue.mod/module.cue -- 16 16 module: "example.com@v0" 17 17 18 + source: kind: "self" 19 + 18 20 -- example/foo/foo.cue -- 19 21 package foo 20 22
+76
cmd/cue/cmd/testdata/script/registry_publish_with_git.txtar
··· 1 + # Check that we can use the cue mod publish command to publish to a registry 2 + # using the git VCS, and that it doesn't publish files that are git-ignored. 3 + 4 + [!exec:git] skip 'no git command found' 5 + memregistry MEMREGISTRY 6 + env CUE_EXPERIMENT=modules 7 + env CUE_REGISTRY=$MEMREGISTRY 8 + env CUE_CACHE_DIR=$WORK/.tmp/cache 9 + 10 + cd $WORK/example 11 + exec git init . 12 + exec git add . 13 + exec git -c user.name=noone -c user.email=noone@example.com commit -m 'initial commit' 14 + cd cuemodule 15 + exec cue mod publish v0.0.1 16 + cmp stdout $WORK/expect-publish-stdout 17 + cd $WORK/main 18 + exec cue eval . 19 + cmp stdout ../expect-eval-stdout 20 + 21 + # If the git directory is not clean, the publish should fail. We can 22 + # conveniently combine that check with the .gitignore removal. 23 + cd $WORK/example 24 + rm .gitignore 25 + cd cuemodule 26 + ! exec cue mod publish v0.0.2 27 + cmp stderr $WORK/expect-unclean-stderr 28 + 29 + # Sanity check that once the .gitignore file has been removed, the 30 + # published module does actually contain the ignored content. 31 + cd $WORK/example 32 + exec git add . 33 + exec git -c user.name=noone -c user.email=noone@example.com commit -m 'commit with no .gitignore' 34 + cd cuemodule 35 + exec cue mod publish v0.0.2 36 + cd $WORK/main 37 + exec cue mod get x.example/e@v0.0.2 38 + exec cue eval . 39 + cmp stdout $WORK/expect-eval-stdout2 40 + 41 + -- expect-publish-stdout -- 42 + published x.example/e@v0.0.1 43 + -- expect-eval-stdout -- 44 + e: true 45 + -- expect-unclean-stderr -- 46 + VCS state is not clean 47 + -- expect-eval-stdout2 -- 48 + e: true 49 + sensitive: true 50 + -- main/cue.mod/module.cue -- 51 + module: "main.org@v0" 52 + 53 + source: kind: "self" 54 + 55 + deps: "x.example/e@v0": v: "v0.0.1" 56 + 57 + -- main/main.cue -- 58 + package main 59 + import "x.example/e@v0" 60 + 61 + e 62 + 63 + -- example/cuemodule/cue.mod/module.cue -- 64 + module: "x.example/e@v0" 65 + source: kind: "git" 66 + -- example/.gitignore -- 67 + /cuemodule/ignored.cue 68 + -- example/cuemodule/e.cue -- 69 + package e 70 + 71 + e: true 72 + 73 + -- example/cuemodule/ignored.cue -- 74 + package e 75 + 76 + sensitive: true