]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: add -retract and -dropretract flags to 'go mod edit'
authorJay Conrod <jayconrod@google.com>
Wed, 15 Apr 2020 18:17:08 +0000 (14:17 -0400)
committerJay Conrod <jayconrod@google.com>
Wed, 26 Aug 2020 21:12:55 +0000 (21:12 +0000)
'go mod edit' can now add and remove 'retract' directives from go.mod
files.

Also, retractions are now included in the 'go mod edit -json' output.

For #24031

Change-Id: Ife7915e259fa508626d6ec5f786b5c860b489599
Reviewed-on: https://go-review.googlesource.com/c/go/+/228381
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
src/cmd/go/alldocs.go
src/cmd/go/internal/modcmd/edit.go
src/cmd/go/testdata/script/mod_edit.txt

index f50529c4f2abeb76a0de5b4774620d594014d837..609ede49cd06638a109724a4217e02e4ad51dd57 100644 (file)
 // module path and version pair. If the @v is omitted, a replacement without
 // a version on the left side is dropped.
 //
+// The -retract=version and -dropretract=version flags add and drop a
+// retraction on the given version. The version may be a single version
+// like "v1.2.3" or a closed interval like "[v1.1.0-v1.1.9]". Note that
+// -retract=version is a no-op if that retraction already exists.
+//
 // The -require, -droprequire, -exclude, -dropexclude, -replace,
-// and -dropreplace editing flags may be repeated, and the changes
-// are applied in the order given.
+// -dropreplace, -retract, and -dropretract editing flags may be repeated,
+// and the changes are applied in the order given.
 //
 // The -go=version flag sets the expected Go language version.
 //
 //             New Module
 //     }
 //
+//     type Retract struct {
+//             Low       string
+//             High      string
+//             Rationale string
+//     }
+//
+// Retract entries representing a single version (not an interval) will have
+// the "Low" and "High" fields set to the same value.
+//
 // Note that this only describes the go.mod file itself, not other modules
 // referred to indirectly. For the full set of modules available to a build,
 // use 'go list -m -json all'.
index a81c25270fbae8e225521960001f3c12e0eff185..18bdd34cd07372428dd9445ba8fa27bc6926b8bf 100644 (file)
@@ -68,9 +68,14 @@ The -dropreplace=old[@v] flag drops a replacement of the given
 module path and version pair. If the @v is omitted, a replacement without
 a version on the left side is dropped.
 
+The -retract=version and -dropretract=version flags add and drop a
+retraction on the given version. The version may be a single version
+like "v1.2.3" or a closed interval like "[v1.1.0-v1.1.9]". Note that
+-retract=version is a no-op if that retraction already exists.
+
 The -require, -droprequire, -exclude, -dropexclude, -replace,
-and -dropreplace editing flags may be repeated, and the changes
-are applied in the order given.
+-dropreplace, -retract, and -dropretract editing flags may be repeated,
+and the changes are applied in the order given.
 
 The -go=version flag sets the expected Go language version.
 
@@ -104,6 +109,15 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
                New Module
        }
 
+       type Retract struct {
+               Low       string
+               High      string
+               Rationale string
+       }
+
+Retract entries representing a single version (not an interval) will have
+the "Low" and "High" fields set to the same value.
+
 Note that this only describes the go.mod file itself, not other modules
 referred to indirectly. For the full set of modules available to a build,
 use 'go list -m -json all'.
@@ -137,6 +151,8 @@ func init() {
        cmdEdit.Flag.Var(flagFunc(flagDropReplace), "dropreplace", "")
        cmdEdit.Flag.Var(flagFunc(flagReplace), "replace", "")
        cmdEdit.Flag.Var(flagFunc(flagDropExclude), "dropexclude", "")
+       cmdEdit.Flag.Var(flagFunc(flagRetract), "retract", "")
+       cmdEdit.Flag.Var(flagFunc(flagDropRetract), "dropretract", "")
 
        work.AddModCommonFlags(cmdEdit)
        base.AddBuildFlagsNX(&cmdEdit.Flag)
@@ -252,12 +268,7 @@ func parsePathVersion(flag, arg string) (path, version string) {
                base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
        }
 
-       // We don't call modfile.CheckPathVersion, because that insists
-       // on versions being in semver form, but here we want to allow
-       // versions like "master" or "1234abcdef", which the go command will resolve
-       // the next time it runs (or during -fix).
-       // Even so, we need to make sure the version is a valid token.
-       if modfile.MustQuote(version) {
+       if !allowedVersionArg(version) {
                base.Fatalf("go mod: -%s=%s: invalid version %q", flag, arg, version)
        }
 
@@ -289,12 +300,48 @@ func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version
                        return path, version, fmt.Errorf("invalid %s path: %v", adj, err)
                }
        }
-       if path != arg && modfile.MustQuote(version) {
+       if path != arg && !allowedVersionArg(version) {
                return path, version, fmt.Errorf("invalid %s version: %q", adj, version)
        }
        return path, version, nil
 }
 
+// parseVersionInterval parses a single version like "v1.2.3" or a closed
+// interval like "[v1.2.3,v1.4.5]". Note that a single version has the same
+// representation as an interval with equal upper and lower bounds: both
+// Low and High are set.
+func parseVersionInterval(arg string) (modfile.VersionInterval, error) {
+       if !strings.HasPrefix(arg, "[") {
+               if !allowedVersionArg(arg) {
+                       return modfile.VersionInterval{}, fmt.Errorf("invalid version: %q", arg)
+               }
+               return modfile.VersionInterval{Low: arg, High: arg}, nil
+       }
+       if !strings.HasSuffix(arg, "]") {
+               return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
+       }
+       s := arg[1 : len(arg)-1]
+       i := strings.Index(s, ",")
+       if i < 0 {
+               return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
+       }
+       low := strings.TrimSpace(s[:i])
+       high := strings.TrimSpace(s[i+1:])
+       if !allowedVersionArg(low) || !allowedVersionArg(high) {
+               return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
+       }
+       return modfile.VersionInterval{Low: low, High: high}, nil
+}
+
+// allowedVersionArg returns whether a token may be used as a version in go.mod.
+// We don't call modfile.CheckPathVersion, because that insists on versions
+// being in semver form, but here we want to allow versions like "master" or
+// "1234abcdef", which the go command will resolve the next time it runs (or
+// during -fix).  Even so, we need to make sure the version is a valid token.
+func allowedVersionArg(arg string) bool {
+       return !modfile.MustQuote(arg)
+}
+
 // flagRequire implements the -require flag.
 func flagRequire(arg string) {
        path, version := parsePathVersion("require", arg)
@@ -377,6 +424,32 @@ func flagDropReplace(arg string) {
        })
 }
 
+// flagRetract implements the -retract flag.
+func flagRetract(arg string) {
+       vi, err := parseVersionInterval(arg)
+       if err != nil {
+               base.Fatalf("go mod: -retract=%s: %v", arg, err)
+       }
+       edits = append(edits, func(f *modfile.File) {
+               if err := f.AddRetract(vi, ""); err != nil {
+                       base.Fatalf("go mod: -retract=%s: %v", arg, err)
+               }
+       })
+}
+
+// flagDropRetract implements the -dropretract flag.
+func flagDropRetract(arg string) {
+       vi, err := parseVersionInterval(arg)
+       if err != nil {
+               base.Fatalf("go mod: -dropretract=%s: %v", arg, err)
+       }
+       edits = append(edits, func(f *modfile.File) {
+               if err := f.DropRetract(vi); err != nil {
+                       base.Fatalf("go mod: -dropretract=%s: %v", arg, err)
+               }
+       })
+}
+
 // fileJSON is the -json output data structure.
 type fileJSON struct {
        Module  module.Version
@@ -384,6 +457,7 @@ type fileJSON struct {
        Require []requireJSON
        Exclude []module.Version
        Replace []replaceJSON
+       Retract []retractJSON
 }
 
 type requireJSON struct {
@@ -397,6 +471,12 @@ type replaceJSON struct {
        New module.Version
 }
 
+type retractJSON struct {
+       Low       string `json:",omitempty"`
+       High      string `json:",omitempty"`
+       Rationale string `json:",omitempty"`
+}
+
 // editPrintJSON prints the -json output.
 func editPrintJSON(modFile *modfile.File) {
        var f fileJSON
@@ -415,6 +495,9 @@ func editPrintJSON(modFile *modfile.File) {
        for _, r := range modFile.Replace {
                f.Replace = append(f.Replace, replaceJSON{r.Old, r.New})
        }
+       for _, r := range modFile.Retract {
+               f.Retract = append(f.Retract, retractJSON{r.Low, r.High, r.Rationale})
+       }
        data, err := json.MarshalIndent(&f, "", "\t")
        if err != nil {
                base.Fatalf("go: internal error: %v", err)
index 898d8524acfaff9d1b867763bfd221e4d15097bc..78485eb86ae1d2e1df1df52d171cf7a447136fbf 100644 (file)
@@ -16,15 +16,19 @@ cmpenv go.mod $WORK/go.mod.init
 cmpenv go.mod $WORK/go.mod.init
 
 # go mod edits
-go mod edit -droprequire=x.1 -require=x.1@v1.0.0 -require=x.2@v1.1.0 -droprequire=x.2 -exclude='x.1 @ v1.2.0' -exclude=x.1@v1.2.1 -replace=x.1@v1.3.0=y.1@v1.4.0 -replace='x.1@v1.4.0 = ../z'
+go mod edit -droprequire=x.1 -require=x.1@v1.0.0 -require=x.2@v1.1.0 -droprequire=x.2 -exclude='x.1 @ v1.2.0' -exclude=x.1@v1.2.1 -replace=x.1@v1.3.0=y.1@v1.4.0 -replace='x.1@v1.4.0 = ../z' -retract=v1.6.0 -retract=[v1.1.0,v1.2.0] -retract=[v1.3.0,v1.4.0] -retract=v1.0.0
 cmpenv go.mod $WORK/go.mod.edit1
-go mod edit -droprequire=x.1 -dropexclude=x.1@v1.2.1 -dropreplace=x.1@v1.3.0 -require=x.3@v1.99.0
+go mod edit -droprequire=x.1 -dropexclude=x.1@v1.2.1 -dropreplace=x.1@v1.3.0 -require=x.3@v1.99.0 -dropretract=v1.0.0 -dropretract=[v1.1.0,v1.2.0]
 cmpenv go.mod $WORK/go.mod.edit2
 
 # go mod edit -json
 go mod edit -json
 cmpenv stdout $WORK/go.mod.json
 
+# go mod edit -json (retractions with rationales)
+go mod edit -json $WORK/go.mod.retractrationale
+cmp stdout $WORK/go.mod.retractrationale.json
+
 # go mod edit -json (empty mod file)
 go mod edit -json $WORK/go.mod.empty
 cmp stdout $WORK/go.mod.empty.json
@@ -40,11 +44,11 @@ cmpenv go.mod $WORK/go.mod.edit5
 # go mod edit -fmt
 cp $WORK/go.mod.badfmt go.mod
 go mod edit -fmt -print # -print should avoid writing file
-cmpenv stdout $WORK/go.mod.edit6
+cmpenv stdout $WORK/go.mod.goodfmt
 cmp go.mod $WORK/go.mod.badfmt
 go mod edit -fmt # without -print, should write file (and nothing to stdout)
 ! stdout .
-cmpenv go.mod $WORK/go.mod.edit6
+cmpenv go.mod $WORK/go.mod.goodfmt
 
 # go mod edit -module
 cd $WORK/m
@@ -84,6 +88,13 @@ replace (
        x.1 v1.3.0 => y.1 v1.4.0
        x.1 v1.4.0 => ../z
 )
+
+retract (
+       v1.6.0
+       [v1.3.0, v1.4.0]
+       [v1.1.0, v1.2.0]
+       v1.0.0
+)
 -- $WORK/go.mod.edit2 --
 module x.x/y/z
 
@@ -93,6 +104,11 @@ exclude x.1 v1.2.0
 
 replace x.1 v1.4.0 => ../z
 
+retract (
+       v1.6.0
+       [v1.3.0, v1.4.0]
+)
+
 require x.3 v1.99.0
 -- $WORK/go.mod.json --
 {
@@ -122,6 +138,16 @@ require x.3 v1.99.0
                                "Path": "../z"
                        }
                }
+       ],
+       "Retract": [
+               {
+                       "Low": "v1.6.0",
+                       "High": "v1.6.0"
+               },
+               {
+                       "Low": "v1.3.0",
+                       "High": "v1.4.0"
+               }
        ]
 }
 -- $WORK/go.mod.edit3 --
@@ -136,6 +162,11 @@ replace (
        x.1 v1.4.0 => y.1/v2 v2.3.5
 )
 
+retract (
+       v1.6.0
+       [v1.3.0, v1.4.0]
+)
+
 require x.3 v1.99.0
 -- $WORK/go.mod.edit4 --
 module x.x/y/z
@@ -146,6 +177,11 @@ exclude x.1 v1.2.0
 
 replace x.1 => y.1/v2 v2.3.6
 
+retract (
+       v1.6.0
+       [v1.3.0, v1.4.0]
+)
+
 require x.3 v1.99.0
 -- $WORK/go.mod.edit5 --
 module x.x/y/z
@@ -154,15 +190,10 @@ go $goversion
 
 exclude x.1 v1.2.0
 
-require x.3 v1.99.0
--- $WORK/go.mod.edit6 --
-module x.x/y/z
-
-go 1.10
-
-exclude x.1 v1.2.0
-
-replace x.1 => y.1/v2 v2.3.6
+retract (
+       v1.6.0
+       [v1.3.0, v1.4.0]
+)
 
 require x.3 v1.99.0
 -- $WORK/local/go.mod.edit --
@@ -183,10 +214,64 @@ exclude x.1     v1.2.0
 replace x.1    =>   y.1/v2 v2.3.6
 
 require x.3   v1.99.0
+
+retract [  "v1.8.1" , "v1.8.2" ]
+-- $WORK/go.mod.goodfmt --
+module x.x/y/z
+
+go 1.10
+
+exclude x.1 v1.2.0
+
+replace x.1 => y.1/v2 v2.3.6
+
+require x.3 v1.99.0
+
+retract [v1.8.1, v1.8.2]
 -- $WORK/m/go.mod.edit --
 module x.x/y/z
 
 go $goversion
+-- $WORK/go.mod.retractrationale --
+module x.x/y/z
+
+go 1.15
+
+// a
+retract v1.0.0
+
+// b
+retract (
+  v1.0.1
+  v1.0.2 // c
+)
+-- $WORK/go.mod.retractrationale.json --
+{
+       "Module": {
+               "Path": "x.x/y/z"
+       },
+       "Go": "1.15",
+       "Require": null,
+       "Exclude": null,
+       "Replace": null,
+       "Retract": [
+               {
+                       "Low": "v1.0.0",
+                       "High": "v1.0.0",
+                       "Rationale": "a"
+               },
+               {
+                       "Low": "v1.0.1",
+                       "High": "v1.0.1",
+                       "Rationale": "b"
+               },
+               {
+                       "Low": "v1.0.2",
+                       "High": "v1.0.2",
+                       "Rationale": "c"
+               }
+       ]
+}
 -- $WORK/go.mod.empty --
 -- $WORK/go.mod.empty.json --
 {
@@ -195,5 +280,6 @@ go $goversion
        },
        "Require": null,
        "Exclude": null,
-       "Replace": null
+       "Replace": null,
+       "Retract": null
 }