Support go.mod replace version pinning (#27)

* support go.mod replace version pinning

* use string replace instead of regex

* Minor cleanups

Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
This commit is contained in:
jpughcs 2020-08-10 13:27:38 -05:00 committed by GitHub
parent 1fec98156c
commit b7fd102f41
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 120 additions and 17 deletions

View file

@ -140,13 +140,34 @@ type Dependency struct {
Version string `json:"version,omitempty"`
}
// ReplacementPath represents an old or new path component
// within a Go module replacement directive.
type ReplacementPath string
// Param reformats a go.mod replace directive to be
// compatible with the `go mod edit` command.
func (r ReplacementPath) Param() string {
return strings.Replace(string(r), " ", "@", 1)
}
func (r ReplacementPath) String() string { return string(r) }
// Replace represents a Go module replacement.
type Replace struct {
// The import path of the module being replaced.
Old string `json:"old,omitempty"`
Old ReplacementPath `json:"old,omitempty"`
// The path to the replacement module.
New string `json:"new,omitempty"`
New ReplacementPath `json:"new,omitempty"`
}
// NewReplace creates a new instance of Replace provided old and
// new Go module paths
func NewReplace(old, new string) Replace {
return Replace{
Old: ReplacementPath(old),
New: ReplacementPath(new),
}
}
// newTempFolder creates a new folder in a temporary location.

88
builder_test.go Normal file
View file

@ -0,0 +1,88 @@
// Copyright 2020 Matthew Holt
//
// 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 xcaddy
import (
"fmt"
"reflect"
"testing"
)
func TestReplacementPath_Param(t *testing.T) {
tests := []struct {
name string
r ReplacementPath
want string
}{
{
"Empty",
ReplacementPath(""),
"",
},
{
"ModulePath",
ReplacementPath("github.com/x/y"),
"github.com/x/y",
},
{
"ModulePath Version Pinned",
ReplacementPath("github.com/x/y v0.0.0-20200101000000-xxxxxxxxxxxx"),
"github.com/x/y@v0.0.0-20200101000000-xxxxxxxxxxxx",
},
{
"FilePath",
ReplacementPath("/x/y/z"),
"/x/y/z",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fmt.Println(tt.r.Param())
if got := tt.r.Param(); got != tt.want {
t.Errorf("ReplacementPath.Param() = %v, want %v", got, tt.want)
}
})
}
}
func TestNewReplace(t *testing.T) {
type args struct {
old string
new string
}
tests := []struct {
name string
args args
want Replace
}{
{
"Empty",
args{"", ""},
Replace{"", ""},
},
{
"Constructor",
args{"a", "b"},
Replace{"a", "b"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewReplace(tt.args.old, tt.args.new); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewReplace() = %v, want %v", got, tt.want)
}
})
}
}

View file

@ -74,10 +74,7 @@ func runBuild(ctx context.Context, args []string) error {
Version: ver,
})
if repl != "" {
replacements = append(replacements, xcaddy.Replace{
Old: mod,
New: repl,
})
replacements = append(replacements, xcaddy.NewReplace(mod, repl))
}
case "--output":
@ -168,10 +165,7 @@ func runDev(ctx context.Context, args []string) error {
// make sure the module being developed is replaced
// so that the local copy is used
replacements := []xcaddy.Replace{
{
Old: currentModule,
New: moduleDir,
},
xcaddy.NewReplace(currentModule, moduleDir),
}
// replace directives only apply to the top-level/main go.mod,
@ -189,10 +183,10 @@ func runDev(ctx context.Context, args []string) error {
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
continue
}
replacements = append(replacements, xcaddy.Replace{
Old: strings.TrimSpace(parts[0]),
New: strings.TrimSpace(parts[1]),
})
replacements = append(replacements, xcaddy.NewReplace(
strings.TrimSpace(parts[0]),
strings.TrimSpace(parts[1]),
))
}
// reconcile remaining path segments; for example if a module foo/a

View file

@ -109,14 +109,14 @@ func (b Builder) newEnvironment(ctx context.Context) (*environment, error) {
// specify module replacements before pinning versions
replaced := make(map[string]string)
for _, r := range b.Replacements {
log.Printf("[INFO] Replace %s => %s", r.Old, r.New)
log.Printf("[INFO] Replace %s => %s", r.Old.String(), r.New.String())
cmd := env.newCommand("go", "mod", "edit",
"-replace", fmt.Sprintf("%s=%s", r.Old, r.New))
"-replace", fmt.Sprintf("%s=%s", r.Old.Param(), r.New.Param()))
err := env.runCommand(ctx, cmd, 10*time.Second)
if err != nil {
return nil, err
}
replaced[r.Old] = r.New
replaced[r.Old.String()] = r.New.String()
}
// check for early abort