1
0
mirror of https://github.com/golang/go synced 2024-11-17 17:54:48 -07:00

cmd/go: add -retract and -dropretract flags to 'go mod edit'

'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>
This commit is contained in:
Jay Conrod 2020-04-15 14:17:08 -04:00
parent c769f034d7
commit 0bbd386e8b
3 changed files with 208 additions and 25 deletions

View File

@ -1100,9 +1100,14 @@
// 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.
//
@ -1136,6 +1141,15 @@
// 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'.

View 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)

View 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
}