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'.
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)
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)
}
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)
})
}
+// 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
Require []requireJSON
Exclude []module.Version
Replace []replaceJSON
+ Retract []retractJSON
}
type requireJSON 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
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)
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
# 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
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
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 --
{
"Path": "../z"
}
}
+ ],
+ "Retract": [
+ {
+ "Low": "v1.6.0",
+ "High": "v1.6.0"
+ },
+ {
+ "Low": "v1.3.0",
+ "High": "v1.4.0"
+ }
]
}
-- $WORK/go.mod.edit3 --
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
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
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 --
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 --
{
},
"Require": null,
"Exclude": null,
- "Replace": null
+ "Replace": null,
+ "Retract": null
}