mirror of
https://github.com/hashicorp/vault.git
synced 2026-02-03 20:40:45 -05:00
* [VAULT-40043]: pipeline: add `go diff mod` command Add a `pipeline go diff mod` command that is capable of comparing two go.mod files at a directive level. We also support strict or lax comparisons of several directives to flexible diff comparisons. This is especially useful when you want to compare two go.mod files that have some different dependencies (CE vs. Ent) but still want to compare versions of like dependencies. This command is not currently used in the pipeline but was useful in developing the diff library that is used. Subsequent work will use the library and be integrated into CI. * review feedback * one more comment fix --------- Signed-off-by: Ryan Cragun <me@ryan.ec> Co-authored-by: Ryan Cragun <me@ryan.ec>
This commit is contained in:
parent
0c6c13dd38
commit
7a4d71f95a
26 changed files with 1952 additions and 21 deletions
|
|
@ -2,6 +2,9 @@ module github.com/hashicorp/vault/tools/pipeline
|
|||
|
||||
go 1.24.0
|
||||
|
||||
// We have test modules in here but they ought to be completely ignored
|
||||
ignore internal/pkg/golang/fixtures
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/avast/retry-go/v4 v4.6.1
|
||||
|
|
@ -9,11 +12,13 @@ require (
|
|||
github.com/hashicorp/hcl/v2 v2.24.0
|
||||
github.com/hashicorp/releases-api v0.2.3
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.8
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
|
||||
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/veqryn/slog-context v0.8.0
|
||||
github.com/zclconf/go-cty v1.16.4
|
||||
golang.org/x/mod v0.29.0
|
||||
golang.org/x/oauth2 v0.31.0
|
||||
)
|
||||
|
||||
|
|
@ -57,7 +62,6 @@ require (
|
|||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/oklog/ulid/v2 v2.1.1 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect
|
||||
github.com/spf13/pflag v1.0.7 // indirect
|
||||
|
|
@ -66,12 +70,11 @@ require (
|
|||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -207,30 +207,30 @@ go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mx
|
|||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
|
||||
golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
|
|
|
|||
18
tools/pipeline/internal/cmd/go.go
Normal file
18
tools/pipeline/internal/cmd/go.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package cmd
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
func newGoCmd() *cobra.Command {
|
||||
goCmd := &cobra.Command{
|
||||
Use: "go",
|
||||
Short: "Go commands",
|
||||
Long: "Go commands",
|
||||
}
|
||||
|
||||
goCmd.AddCommand(newGoDiffCmd())
|
||||
|
||||
return goCmd
|
||||
}
|
||||
20
tools/pipeline/internal/cmd/go_diff.go
Normal file
20
tools/pipeline/internal/cmd/go_diff.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newGoDiffCmd() *cobra.Command {
|
||||
goModCmd := &cobra.Command{
|
||||
Use: "diff",
|
||||
Short: "Go diff commands",
|
||||
Long: "Go diff commands",
|
||||
}
|
||||
|
||||
goModCmd.AddCommand(newGoDiffModCmd())
|
||||
|
||||
return goModCmd
|
||||
}
|
||||
130
tools/pipeline/internal/cmd/go_diff_mod.go
Normal file
130
tools/pipeline/internal/cmd/go_diff_mod.go
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/vault/tools/pipeline/internal/pkg/golang"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var goModDiffReq = &golang.DiffModReq{
|
||||
A: &golang.ModSource{},
|
||||
B: &golang.ModSource{},
|
||||
Opts: golang.DefaultDiffOpts(),
|
||||
}
|
||||
|
||||
func newGoDiffModCmd() *cobra.Command {
|
||||
goModDiffCmd := &cobra.Command{
|
||||
Use: "mod </path/a/go.mod> </path/b/go.mod> [ARGS]",
|
||||
Short: "Diff two local go.mod files",
|
||||
Long: "Diff two local go.mod files",
|
||||
RunE: runGoModDiffCmd,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
switch len(args) {
|
||||
case 2:
|
||||
err := setUpGoModSourceFromPath(args[0], goModDiffReq.A)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setUpGoModSourceFromPath(args[1], goModDiffReq.B)
|
||||
case 0, 1:
|
||||
return errors.New("invalid arguments: you must provide two local file paths")
|
||||
default:
|
||||
return fmt.Errorf("invalid arguments: expected two paths as arguments, received %d arguments", len(args))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.ParseLax, "lax", false, "Parse the modules in lax mode to ignore newer and unknown directives")
|
||||
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.Module, "module", true, "Compare the module directives in both files")
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.Go, "go", true, "Compare the go directives in both files")
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.Toolchain, "toolchain", true, "Compare the toolchain directives in both files")
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.Godebug, "godebug", true, "Compare the godebug directives in both files")
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.Require, "require", true, "Compare the require directives in both files")
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.Replace, "replace", true, "Compare the replace directives in both files")
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.Retract, "retract", true, "Compare the retract directives in both files")
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.Tool, "tool", true, "Compare the tool directives in both files")
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.Ignore, "ignore", true, "Compare the ignore directives in both files")
|
||||
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.StrictDiffRequire, "strict-require", true, "Strictly compare the requires directives in both files. When true all requires are compared, otherwise only shared requires are compared")
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.StrictDiffExclude, "strict-exclude", true, "Strictly compare the excludes directives in both files. When true all excludes are compared, otherwise only shared excludes are compared")
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.StrictDiffReplace, "strict-replace", true, "Strictly compare the replace directives in both files. When true all replaces are compared, otherwise only shared replaces are compared")
|
||||
goModDiffCmd.PersistentFlags().BoolVar(&goModDiffReq.Opts.StrictDiffRetract, "strict-retract", true, "Strictly compare the retract directives in both files. When true all retracts are compared, otherwise only shared retract directives are compared")
|
||||
|
||||
return goModDiffCmd
|
||||
}
|
||||
|
||||
func runGoModDiffCmd(cmd *cobra.Command, args []string) error {
|
||||
cmd.SilenceUsage = true // Don't spam the usage on failure
|
||||
|
||||
res, err := goModDiffReq.Run(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch rootCfg.format {
|
||||
case "json":
|
||||
b, err1 := res.ToJSON()
|
||||
if err1 != nil {
|
||||
err = errors.Join(err, err1)
|
||||
} else {
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
case "markdown":
|
||||
tbl, err1 := res.ToTable(err)
|
||||
if err1 != nil {
|
||||
err = errors.Join(err, err1)
|
||||
} else {
|
||||
tbl.SetTitle("Go Mod Diff")
|
||||
fmt.Println(tbl.RenderMarkdown())
|
||||
}
|
||||
default:
|
||||
tbl, err1 := res.ToTable(err)
|
||||
if err1 != nil {
|
||||
err = errors.Join(err, err1)
|
||||
} else {
|
||||
if text := tbl.Render(); text != "" {
|
||||
fmt.Println(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if l := len(res.ModDiff); l > 0 {
|
||||
err = errors.Join(fmt.Errorf("%d differences were found", l), err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func setUpGoModSourceFromPath(path string, source *golang.ModSource) (err error) {
|
||||
if source == nil {
|
||||
return errors.New("you must provide a mod source")
|
||||
}
|
||||
|
||||
aPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Open(aPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Join(f.Close())
|
||||
}()
|
||||
|
||||
source.Name = path
|
||||
source.Data, err = io.ReadAll(f)
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ func newRootCmd() *cobra.Command {
|
|||
|
||||
rootCmd.AddCommand(newGenerateCmd())
|
||||
rootCmd.AddCommand(newGithubCmd())
|
||||
rootCmd.AddCommand(newGoCmd())
|
||||
rootCmd.AddCommand(newHCPCmd())
|
||||
rootCmd.AddCommand(newReleasesCmd())
|
||||
|
||||
|
|
|
|||
102
tools/pipeline/internal/pkg/golang/diff_exclude.go
Normal file
102
tools/pipeline/internal/pkg/golang/diff_exclude.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
// diffExclude compares the exclude directives in two module files and returns
|
||||
// a slice of *Diff's. When strict parsing is set to true, exclude directives
|
||||
// that are ommitted from one or the other modfile will also be included.
|
||||
func diffExclude(a *modfile.File, b *modfile.File, strictDiff bool) []*Diff {
|
||||
if (a == nil && b == nil) || (len(a.Exclude) == 0 && len(b.Exclude) == 0) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var diffs []*Diff
|
||||
if strictDiff {
|
||||
diffs = append(diffExcludeFindMissing(a, b), diffExcludeFindMissing(b, a)...)
|
||||
}
|
||||
versionDiffsA := diffExcludeFindDifferent(a, b)
|
||||
versionDiffsB := diffExcludeFindDifferent(b, a)
|
||||
maps.Copy(versionDiffsB, versionDiffsA)
|
||||
|
||||
return slices.DeleteFunc(
|
||||
append(diffs, slices.Collect(maps.Values(versionDiffsB))...),
|
||||
func(d *Diff) bool { return d == nil },
|
||||
)
|
||||
}
|
||||
|
||||
func diffExcludeFindMissing(a, b *modfile.File) []*Diff {
|
||||
diffs := []*Diff{}
|
||||
for _, needle := range a.Exclude {
|
||||
if needle == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// See if there's a matching require
|
||||
idx := slices.IndexFunc(b.Exclude, func(hay *modfile.Exclude) bool {
|
||||
if hay == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return needle.Mod.Path == hay.Mod.Path
|
||||
})
|
||||
|
||||
if idx >= 0 {
|
||||
// We have a matching require
|
||||
continue
|
||||
}
|
||||
|
||||
diff := newDiffFromModFiles(a, b, DirectiveExclude)
|
||||
if needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ")}
|
||||
}
|
||||
diffs = append(diffs, diff)
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
|
||||
func diffExcludeFindDifferent(a, b *modfile.File) map[string]*Diff {
|
||||
diffs := map[string]*Diff{}
|
||||
for _, needle := range a.Exclude {
|
||||
if needle == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// See if there's a matching require
|
||||
idx := slices.IndexFunc(b.Exclude, func(hay *modfile.Exclude) bool {
|
||||
if hay == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return needle.Mod.Path == hay.Mod.Path
|
||||
})
|
||||
|
||||
if idx < 0 {
|
||||
// We don't have a matching require
|
||||
continue
|
||||
}
|
||||
|
||||
hay := b.Exclude[idx]
|
||||
if needle.Mod.Version != hay.Mod.Version {
|
||||
diff := newDiffFromModFiles(a, b, DirectiveExclude)
|
||||
if needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ") + "\n"}
|
||||
}
|
||||
if hay.Syntax != nil {
|
||||
diff.Diff.B = []string{strings.Join(hay.Syntax.Token, " ")}
|
||||
}
|
||||
diffs[needle.Mod.Path] = diff
|
||||
}
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
31
tools/pipeline/internal/pkg/golang/diff_go.go
Normal file
31
tools/pipeline/internal/pkg/golang/diff_go.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
// diffGo compares the go directives in two modules files and returns
|
||||
// a slice of *Diff's.
|
||||
func diffGo(a *modfile.File, b *modfile.File) *Diff {
|
||||
if (a == nil && b == nil) ||
|
||||
(a.Go == nil && b.Go == nil) ||
|
||||
((a.Go != nil && b.Go != nil) && (a.Go.Version == b.Go.Version)) {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
diff := newDiffFromModFiles(a, b, DirectiveGo)
|
||||
if a != nil && a.Go != nil && a.Go.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(a.Go.Syntax.Token, " ") + "\n"}
|
||||
}
|
||||
if b != nil && b.Go != nil && b.Go.Syntax != nil {
|
||||
diff.Diff.B = []string{strings.Join(b.Go.Syntax.Token, " ")}
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
92
tools/pipeline/internal/pkg/golang/diff_godebug.go
Normal file
92
tools/pipeline/internal/pkg/golang/diff_godebug.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
// diffGodebug compares the godebug directives in two modules files and returns
|
||||
// a slice of *Diff's. When strict parsing is set to true, godebug directives
|
||||
// that are ommitted from one or the other modfile will also be included.
|
||||
func diffGodebug(a *modfile.File, b *modfile.File) []*Diff {
|
||||
if (a == nil && b == nil) || (len(a.Godebug) == 0 && len(b.Godebug) == 0) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return slices.DeleteFunc(
|
||||
append(diffGodebugFindDiffs(a, b), diffGodebugFindDiffs(b, a)...),
|
||||
func(d *Diff) bool { return d == nil },
|
||||
)
|
||||
}
|
||||
|
||||
func diffGodebugFindDiffs(a, b *modfile.File) []*Diff {
|
||||
diffs := []*Diff{}
|
||||
for _, needle := range a.Godebug {
|
||||
idx := slices.IndexFunc(b.Godebug, func(hay *modfile.Godebug) bool {
|
||||
return godebugMatchingKey(needle, hay)
|
||||
})
|
||||
|
||||
if idx < 0 {
|
||||
// We don't have matching godebug with the same key, create a single
|
||||
// sided diff
|
||||
diff := newDiffFromModFiles(a, b, DirectiveGodebug)
|
||||
if needle != nil && needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ")}
|
||||
}
|
||||
diffs = append(diffs, diff)
|
||||
continue
|
||||
}
|
||||
|
||||
// We have a matching key
|
||||
hay := b.Godebug[idx]
|
||||
if godebugEqual(needle, hay) {
|
||||
continue
|
||||
}
|
||||
|
||||
// We have differing values. Create a double sided diff
|
||||
diff := newDiffFromModFiles(a, b, DirectiveGodebug)
|
||||
if needle != nil && needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ") + "\n"}
|
||||
}
|
||||
if hay != nil && hay.Syntax != nil {
|
||||
diff.Diff.B = []string{strings.Join(hay.Syntax.Token, " ")}
|
||||
}
|
||||
|
||||
diffs = append(diffs, diff)
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
|
||||
func godebugEqual(a *modfile.Godebug, b *modfile.Godebug) bool {
|
||||
if a == nil && b == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if (a == nil && b != nil) || (a != nil && b == nil) {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.Key != b.Key {
|
||||
return false
|
||||
}
|
||||
|
||||
return a.Value == b.Value
|
||||
}
|
||||
|
||||
func godebugMatchingKey(a *modfile.Godebug, b *modfile.Godebug) bool {
|
||||
if a == nil && b == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if (a == nil && b != nil) || (a != nil && b == nil) {
|
||||
return false
|
||||
}
|
||||
|
||||
return a.Key == b.Key
|
||||
}
|
||||
56
tools/pipeline/internal/pkg/golang/diff_ignore.go
Normal file
56
tools/pipeline/internal/pkg/golang/diff_ignore.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
// diffIgnore compares the ignore directives in two module files and returns
|
||||
// a slice of *Diff's. When strict parsing is set to true, ignore directives
|
||||
// that are ommitted from one or the other modfile will also be included.
|
||||
func diffIgnore(a *modfile.File, b *modfile.File) []*Diff {
|
||||
if (a == nil && b == nil) || (len(a.Ignore) == 0 && len(b.Ignore) == 0) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return slices.DeleteFunc(
|
||||
append(diffIgnoreFindDiffs(a, b), diffIgnoreFindDiffs(b, a)...),
|
||||
func(d *Diff) bool { return d == nil },
|
||||
)
|
||||
}
|
||||
|
||||
func diffIgnoreFindDiffs(a, b *modfile.File) []*Diff {
|
||||
diffs := []*Diff{}
|
||||
for _, needle := range a.Ignore {
|
||||
if needle == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// See if there's a matching ignore
|
||||
idx := slices.IndexFunc(b.Ignore, func(hay *modfile.Ignore) bool {
|
||||
if hay == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return needle.Path == hay.Path
|
||||
})
|
||||
|
||||
if idx >= 0 {
|
||||
// We have a matching ignore
|
||||
continue
|
||||
}
|
||||
|
||||
diff := newDiffFromModFiles(a, b, DirectiveIgnore)
|
||||
if needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ")}
|
||||
}
|
||||
diffs = append(diffs, diff)
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
79
tools/pipeline/internal/pkg/golang/diff_mod_request.go
Normal file
79
tools/pipeline/internal/pkg/golang/diff_mod_request.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
)
|
||||
|
||||
type DiffModReq struct {
|
||||
A *ModSource `json:"a,omitempty"`
|
||||
B *ModSource `json:"b,omitempty"`
|
||||
Opts *DiffOpts `json:"opts,omitempty"`
|
||||
}
|
||||
|
||||
func (r *DiffModReq) Run(ctx context.Context) (*DiffModRes, error) {
|
||||
res := &DiffModRes{}
|
||||
var err error
|
||||
|
||||
res.ModDiff, err = DiffModFiles(r.A, r.B, r.Opts)
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
type DiffModRes struct {
|
||||
ModDiff
|
||||
}
|
||||
|
||||
func (r *DiffModRes) ToJSON() ([]byte, error) {
|
||||
b, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshaling latest HCP image response to JSON: %w", err)
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// ToTable marshals the response to a text table.
|
||||
func (r *DiffModRes) ToTable(err error) (table.Writer, error) {
|
||||
t := table.NewWriter()
|
||||
t.Style().Options.DrawBorder = false
|
||||
t.Style().Options.SeparateColumns = false
|
||||
t.Style().Options.SeparateFooter = false
|
||||
t.Style().Options.SeparateHeader = false
|
||||
t.Style().Options.SeparateRows = false
|
||||
|
||||
if r == nil || r.ModDiff == nil || len(r.ModDiff) == 0 || err != nil {
|
||||
if err != nil {
|
||||
t.AppendHeader(table.Row{"error"})
|
||||
t.AppendRow(table.Row{err.Error()})
|
||||
}
|
||||
return t, err
|
||||
}
|
||||
|
||||
t.AppendHeader(table.Row{"explanation", "diff"})
|
||||
for _, diff := range r.ModDiff {
|
||||
if diff == nil {
|
||||
continue
|
||||
}
|
||||
if diff.Diff == nil {
|
||||
return nil, fmt.Errorf("missing unified diff: %v", diff)
|
||||
}
|
||||
|
||||
diffText, err := difflib.GetUnifiedDiffString(*diff.Diff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.AppendRow(table.Row{diff.Directive.Explanation(), diffText})
|
||||
}
|
||||
t.SuppressEmptyColumns()
|
||||
t.SuppressTrailingSpaces()
|
||||
|
||||
return t, nil
|
||||
}
|
||||
31
tools/pipeline/internal/pkg/golang/diff_module.go
Normal file
31
tools/pipeline/internal/pkg/golang/diff_module.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
// diffModule compares the module directives in two modules files and returns
|
||||
// a slice of *Diff's.
|
||||
func diffModule(a *modfile.File, b *modfile.File) *Diff {
|
||||
if (a == nil && b == nil) ||
|
||||
(a.Module == nil && b.Module == nil) ||
|
||||
(a.Module.Mod.String() == b.Module.Mod.String()) {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
diff := newDiffFromModFiles(a, b, DirectiveModule)
|
||||
if a != nil && a.Module != nil && a.Module.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(a.Module.Syntax.Token, " ") + "\n"}
|
||||
}
|
||||
if b != nil && b.Module != nil && b.Module.Syntax != nil {
|
||||
diff.Diff.B = []string{strings.Join(b.Module.Syntax.Token, " ")}
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
125
tools/pipeline/internal/pkg/golang/diff_replace.go
Normal file
125
tools/pipeline/internal/pkg/golang/diff_replace.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
func diffReplace(a *modfile.File, b *modfile.File, strictDiff bool) []*Diff {
|
||||
if (a == nil && b == nil) || (len(a.Replace) == 0 && len(b.Replace) == 0) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var diffs []*Diff
|
||||
if strictDiff {
|
||||
diffs = append(diffReplaceFindMissing(a, b), diffReplaceFindMissing(b, a)...)
|
||||
}
|
||||
versionDiffsA := diffReplaceFindDifferent(a, b)
|
||||
versionDiffsB := diffReplaceFindDifferent(b, a)
|
||||
maps.Copy(versionDiffsB, versionDiffsA)
|
||||
|
||||
return slices.DeleteFunc(
|
||||
append(diffs, slices.Collect(maps.Values(versionDiffsB))...),
|
||||
func(d *Diff) bool { return d == nil },
|
||||
)
|
||||
}
|
||||
|
||||
func diffReplaceFindMissing(a, b *modfile.File) []*Diff {
|
||||
diffs := []*Diff{}
|
||||
for _, needle := range a.Replace {
|
||||
if needle == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// See if there's a matching old exclude
|
||||
idx := slices.IndexFunc(b.Replace, func(hay *modfile.Replace) bool {
|
||||
if hay == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return needle.Old.Path == hay.Old.Path
|
||||
})
|
||||
|
||||
if idx >= 0 {
|
||||
// We have a matching old exclude, we'll handle this in the version diff check
|
||||
continue
|
||||
}
|
||||
|
||||
diff := newDiffFromModFiles(a, b, DirectiveReplace)
|
||||
if needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ")}
|
||||
}
|
||||
diffs = append(diffs, diff)
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
|
||||
func diffReplaceFindDifferent(a, b *modfile.File) map[string]*Diff {
|
||||
diffs := map[string]*Diff{}
|
||||
for _, needle := range a.Replace {
|
||||
if needle == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// See if there's a matching exclude
|
||||
idx := slices.IndexFunc(b.Replace, func(hay *modfile.Replace) bool {
|
||||
if hay == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return needle.Old.Path == hay.Old.Path
|
||||
})
|
||||
|
||||
if idx < 0 {
|
||||
// We don't have a matching exclude
|
||||
continue
|
||||
}
|
||||
|
||||
hay := b.Replace[idx]
|
||||
if replaceEqual(needle, hay) {
|
||||
continue
|
||||
}
|
||||
|
||||
diff := newDiffFromModFiles(a, b, DirectiveReplace)
|
||||
if needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ") + "\n"}
|
||||
}
|
||||
if hay.Syntax != nil {
|
||||
diff.Diff.B = []string{strings.Join(hay.Syntax.Token, " ")}
|
||||
}
|
||||
diffs[needle.Old.Path] = diff
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
|
||||
func replaceEqual(a *modfile.Replace, b *modfile.Replace) bool {
|
||||
if a == nil && b == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if (a == nil && b != nil) || (a != nil && b == nil) {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.Old.Path != b.Old.Path {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.Old.Version != b.Old.Version {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.New.Path != b.New.Path {
|
||||
return false
|
||||
}
|
||||
|
||||
return a.New.Version == b.New.Version
|
||||
}
|
||||
99
tools/pipeline/internal/pkg/golang/diff_require.go
Normal file
99
tools/pipeline/internal/pkg/golang/diff_require.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
func diffRequire(a *modfile.File, b *modfile.File, strictDiff bool) []*Diff {
|
||||
if (a == nil && b == nil) || (len(a.Require) == 0 && len(b.Require) == 0) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var diffs []*Diff
|
||||
if strictDiff {
|
||||
diffs = append(diffRequireFindMissing(a, b), diffRequireFindMissing(b, a)...)
|
||||
}
|
||||
versionDiffsA := diffRequireFindDifferent(a, b)
|
||||
versionDiffsB := diffRequireFindDifferent(b, a)
|
||||
maps.Copy(versionDiffsB, versionDiffsA)
|
||||
|
||||
return slices.DeleteFunc(
|
||||
append(diffs, slices.Collect(maps.Values(versionDiffsB))...),
|
||||
func(d *Diff) bool { return d == nil },
|
||||
)
|
||||
}
|
||||
|
||||
func diffRequireFindMissing(a, b *modfile.File) []*Diff {
|
||||
diffs := []*Diff{}
|
||||
for _, needle := range a.Require {
|
||||
if needle == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// See if there's a matching require
|
||||
idx := slices.IndexFunc(b.Require, func(hay *modfile.Require) bool {
|
||||
if hay == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return needle.Mod.Path == hay.Mod.Path
|
||||
})
|
||||
|
||||
if idx >= 0 {
|
||||
// We have a matching require
|
||||
continue
|
||||
}
|
||||
|
||||
diff := newDiffFromModFiles(a, b, DirectiveRequire)
|
||||
if needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ")}
|
||||
}
|
||||
diffs = append(diffs, diff)
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
|
||||
func diffRequireFindDifferent(a, b *modfile.File) map[string]*Diff {
|
||||
diffs := map[string]*Diff{}
|
||||
for _, needle := range a.Require {
|
||||
if needle == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// See if there's a matching require
|
||||
idx := slices.IndexFunc(b.Require, func(hay *modfile.Require) bool {
|
||||
if hay == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return needle.Mod.Path == hay.Mod.Path
|
||||
})
|
||||
|
||||
if idx < 0 {
|
||||
// We don't have a matching require
|
||||
continue
|
||||
}
|
||||
|
||||
hay := b.Require[idx]
|
||||
if needle.Mod.Version != hay.Mod.Version {
|
||||
diff := newDiffFromModFiles(a, b, DirectiveRequire)
|
||||
if needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ") + "\n"}
|
||||
}
|
||||
if hay.Syntax != nil {
|
||||
diff.Diff.B = []string{strings.Join(hay.Syntax.Token, " ")}
|
||||
}
|
||||
diffs[needle.Mod.Path] = diff
|
||||
}
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
121
tools/pipeline/internal/pkg/golang/diff_retract.go
Normal file
121
tools/pipeline/internal/pkg/golang/diff_retract.go
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
func diffRetract(a *modfile.File, b *modfile.File, strictDiff bool) []*Diff {
|
||||
if (a == nil && b == nil) || (len(a.Retract) == 0 && len(b.Retract) == 0) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var diffs []*Diff
|
||||
if strictDiff {
|
||||
diffs = append(diffRetractFindMissing(a, b), diffRetractFindMissing(b, a)...)
|
||||
}
|
||||
versionDiffsA := diffRetractFindDifferent(a, b)
|
||||
versionDiffsB := diffRetractFindDifferent(b, a)
|
||||
maps.Copy(versionDiffsB, versionDiffsA)
|
||||
|
||||
return slices.DeleteFunc(
|
||||
append(diffs, slices.Collect(maps.Values(versionDiffsB))...),
|
||||
func(d *Diff) bool { return d == nil },
|
||||
)
|
||||
}
|
||||
|
||||
func diffRetractFindMissing(a, b *modfile.File) []*Diff {
|
||||
diffs := []*Diff{}
|
||||
for _, needle := range a.Retract {
|
||||
if needle == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// See if there's a matching retract
|
||||
idx := slices.IndexFunc(b.Retract, func(hay *modfile.Retract) bool {
|
||||
if hay == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return needle.VersionInterval.Low == hay.VersionInterval.Low
|
||||
})
|
||||
|
||||
if idx >= 0 {
|
||||
// We have a matching retract
|
||||
continue
|
||||
}
|
||||
|
||||
diff := newDiffFromModFiles(a, b, DirectiveRetract)
|
||||
if needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ")}
|
||||
}
|
||||
diffs = append(diffs, diff)
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
|
||||
func diffRetractFindDifferent(a, b *modfile.File) map[string]*Diff {
|
||||
diffs := map[string]*Diff{}
|
||||
for _, needle := range a.Retract {
|
||||
if needle == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// See if there's a matching require
|
||||
idx := slices.IndexFunc(b.Retract, func(hay *modfile.Retract) bool {
|
||||
if hay == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return needle.VersionInterval.Low == hay.VersionInterval.Low
|
||||
})
|
||||
|
||||
if idx < 0 {
|
||||
// We don't have a matching require
|
||||
continue
|
||||
}
|
||||
|
||||
hay := b.Retract[idx]
|
||||
if retractEqual(needle, hay) {
|
||||
continue
|
||||
}
|
||||
|
||||
diff := newDiffFromModFiles(a, b, DirectiveRetract)
|
||||
if needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ") + "\n"}
|
||||
}
|
||||
if hay.Syntax != nil {
|
||||
diff.Diff.B = []string{strings.Join(hay.Syntax.Token, " ")}
|
||||
}
|
||||
diffs[needle.VersionInterval.Low] = diff
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
|
||||
func retractEqual(a *modfile.Retract, b *modfile.Retract) bool {
|
||||
if a == nil && b == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if (a == nil && b != nil) || (a != nil && b == nil) {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.VersionInterval.Low != b.VersionInterval.Low {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.VersionInterval.High != b.VersionInterval.High {
|
||||
return false
|
||||
}
|
||||
|
||||
return a.Rationale == b.Rationale
|
||||
}
|
||||
53
tools/pipeline/internal/pkg/golang/diff_tool.go
Normal file
53
tools/pipeline/internal/pkg/golang/diff_tool.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
func diffTool(a *modfile.File, b *modfile.File) []*Diff {
|
||||
if (a == nil && b == nil) || (len(a.Tool) == 0 && len(b.Tool) == 0) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return slices.DeleteFunc(
|
||||
append(diffToolFindDiffs(a, b), diffToolFindDiffs(b, a)...),
|
||||
func(d *Diff) bool { return d == nil },
|
||||
)
|
||||
}
|
||||
|
||||
func diffToolFindDiffs(a, b *modfile.File) []*Diff {
|
||||
diffs := []*Diff{}
|
||||
for _, needle := range a.Tool {
|
||||
if needle == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// See if there's a matching tool
|
||||
idx := slices.IndexFunc(b.Tool, func(hay *modfile.Tool) bool {
|
||||
if hay == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return needle.Path == hay.Path
|
||||
})
|
||||
|
||||
if idx >= 0 {
|
||||
// We have a matching tool
|
||||
continue
|
||||
}
|
||||
|
||||
diff := newDiffFromModFiles(a, b, DirectiveTool)
|
||||
if needle.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(needle.Syntax.Token, " ")}
|
||||
}
|
||||
diffs = append(diffs, diff)
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
29
tools/pipeline/internal/pkg/golang/diff_toolchain.go
Normal file
29
tools/pipeline/internal/pkg/golang/diff_toolchain.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
func diffToolchain(a *modfile.File, b *modfile.File) *Diff {
|
||||
if (a == nil && b == nil) ||
|
||||
(a.Toolchain == nil && b.Toolchain == nil) ||
|
||||
((a.Toolchain != nil && b.Toolchain != nil) && (a.Toolchain.Name == b.Toolchain.Name)) {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
diff := newDiffFromModFiles(a, b, DirectiveToolchain)
|
||||
if a != nil && a.Toolchain != nil && a.Toolchain.Syntax != nil {
|
||||
diff.Diff.A = []string{strings.Join(a.Toolchain.Syntax.Token, " ") + "\n"}
|
||||
}
|
||||
if b != nil && b.Toolchain != nil && b.Toolchain.Syntax != nil {
|
||||
diff.Diff.B = []string{strings.Join(b.Toolchain.Syntax.Token, " ")}
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
5
tools/pipeline/internal/pkg/golang/doc.go
Normal file
5
tools/pipeline/internal/pkg/golang/doc.go
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
// Package golang contains functions and types for Go analysis
|
||||
package golang
|
||||
25
tools/pipeline/internal/pkg/golang/fixtures/moda/go.mod
Normal file
25
tools/pipeline/internal/pkg/golang/fixtures/moda/go.mod
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
module github.com/hashicorp/vault/pipeline/golang/moda
|
||||
|
||||
go 1.25
|
||||
|
||||
require github.com/99designs/keyring v1.2.2
|
||||
|
||||
require (
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/term v0.3.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
)
|
||||
|
||||
tool golang.org/x/tools/cmd/bisect
|
||||
|
||||
ignore (
|
||||
./third_party/javascript
|
||||
content/html
|
||||
static
|
||||
)
|
||||
42
tools/pipeline/internal/pkg/golang/fixtures/moda/go.sum
Normal file
42
tools/pipeline/internal/pkg/golang/fixtures/moda/go.sum
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
|
||||
github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0=
|
||||
github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk=
|
||||
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM=
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
|
||||
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
8
tools/pipeline/internal/pkg/golang/fixtures/moda/main.go
Normal file
8
tools/pipeline/internal/pkg/golang/fixtures/moda/main.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/99designs/keyring"
|
||||
)
|
||||
38
tools/pipeline/internal/pkg/golang/fixtures/modb/go.mod
Normal file
38
tools/pipeline/internal/pkg/golang/fixtures/modb/go.mod
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
module github.com/hashicorp/vault/pipeline/golang/modb
|
||||
|
||||
go 1.25.2
|
||||
|
||||
toolchain go1.24
|
||||
|
||||
godebug (
|
||||
default=go1.21
|
||||
httpcookiemaxnum=4000
|
||||
panicnil=1
|
||||
)
|
||||
|
||||
exclude golang.org/x/term v0.2.0
|
||||
|
||||
replace github.com/99designs/keyring => github.com/Jeffail/keyring v1.2.3
|
||||
|
||||
require github.com/99designs/keyring v0.0.0-00010101000000-000000000000
|
||||
|
||||
retract (
|
||||
[v1.0.0, v1.9.9]
|
||||
v0.9.0
|
||||
)
|
||||
|
||||
tool golang.org/x/tools/cmd/stringer
|
||||
|
||||
require (
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/term v0.3.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
)
|
||||
|
||||
ignore ./node_modules
|
||||
44
tools/pipeline/internal/pkg/golang/fixtures/modb/go.sum
Normal file
44
tools/pipeline/internal/pkg/golang/fixtures/modb/go.sum
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
|
||||
github.com/Jeffail/keyring v1.2.3 h1:WRmYdGPmHoJqX66KjGXQBALp6mUN00tD0ds5C4pqEsQ=
|
||||
github.com/Jeffail/keyring v1.2.3/go.mod h1:xIg4RDmDwDuUFoU4IzDIT3b+HV24JUYlzo6ILZUH3Sc=
|
||||
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM=
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
|
||||
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
8
tools/pipeline/internal/pkg/golang/fixtures/modb/main.go
Normal file
8
tools/pipeline/internal/pkg/golang/fixtures/modb/main.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/99designs/keyring"
|
||||
)
|
||||
254
tools/pipeline/internal/pkg/golang/mod_diff.go
Normal file
254
tools/pipeline/internal/pkg/golang/mod_diff.go
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
// DiffOpts are options for the module diff.
|
||||
type DiffOpts struct {
|
||||
ParseLax bool
|
||||
|
||||
Module bool
|
||||
Go bool
|
||||
Toolchain bool
|
||||
Godebug bool
|
||||
Require bool
|
||||
Exclude bool
|
||||
Replace bool
|
||||
Retract bool
|
||||
Tool bool
|
||||
Ignore bool
|
||||
|
||||
StrictDiffRequire bool
|
||||
StrictDiffExclude bool
|
||||
StrictDiffReplace bool
|
||||
StrictDiffRetract bool
|
||||
}
|
||||
|
||||
func DefaultDiffOpts() *DiffOpts {
|
||||
return &DiffOpts{
|
||||
ParseLax: false,
|
||||
Module: true,
|
||||
Go: true,
|
||||
Toolchain: true,
|
||||
Godebug: true,
|
||||
Require: true,
|
||||
Exclude: true,
|
||||
Replace: true,
|
||||
Retract: true,
|
||||
Tool: true,
|
||||
Ignore: true,
|
||||
StrictDiffRequire: true,
|
||||
StrictDiffExclude: true,
|
||||
StrictDiffReplace: true,
|
||||
StrictDiffRetract: true,
|
||||
}
|
||||
}
|
||||
|
||||
// ModDiff is the result of comparing two Go modules.
|
||||
type ModDiff []*Diff
|
||||
|
||||
// ModSource is a go.mod file
|
||||
type ModSource struct {
|
||||
Name string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type Diff struct {
|
||||
Directive Directive
|
||||
Diff *difflib.UnifiedDiff
|
||||
}
|
||||
|
||||
type Directive string
|
||||
|
||||
const (
|
||||
DirectiveModule Directive = "module"
|
||||
DirectiveGo Directive = "go"
|
||||
DirectiveToolchain Directive = "toolchain"
|
||||
DirectiveGodebug Directive = "godebug"
|
||||
DirectiveRequire Directive = "require"
|
||||
DirectiveExclude Directive = "exclude"
|
||||
DirectiveReplace Directive = "replace"
|
||||
DirectiveRetract Directive = "retract"
|
||||
DirectiveTool Directive = "tool"
|
||||
DirectiveIgnore Directive = "ignore"
|
||||
)
|
||||
|
||||
func (d *Diff) Explanation() string {
|
||||
if d == nil || d.Directive == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return d.Directive.Explanation()
|
||||
}
|
||||
|
||||
func (d *Diff) UnifiedText() string {
|
||||
if d == nil || d.Diff == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
txt, _ := difflib.GetUnifiedDiffString(*d.Diff)
|
||||
|
||||
return txt
|
||||
}
|
||||
|
||||
func (d Directive) Explanation() string {
|
||||
return fmt.Sprintf("The '%s' directives do not match", d)
|
||||
}
|
||||
|
||||
// DiffModFiles diffs two go.mod "files" and returns a ModDiff.
|
||||
func DiffModFiles(as *ModSource, bs *ModSource, opts *DiffOpts) (ModDiff, error) {
|
||||
if as == nil {
|
||||
return nil, errors.New("missing a mod source")
|
||||
}
|
||||
|
||||
if bs == nil {
|
||||
return nil, errors.New("missing b mod source")
|
||||
}
|
||||
|
||||
var af *modfile.File
|
||||
var bf *modfile.File
|
||||
var err error
|
||||
if opts.ParseLax {
|
||||
af, err = modfile.ParseLax(as.Name, as.Data, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing %s contents: %w", as.Name, err)
|
||||
}
|
||||
|
||||
bf, err = modfile.ParseLax(bs.Name, bs.Data, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing %s contents: %w", bs.Name, err)
|
||||
}
|
||||
} else {
|
||||
af, err = modfile.Parse(as.Name, as.Data, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing %s contents: %w", as.Name, err)
|
||||
}
|
||||
|
||||
bf, err = modfile.Parse(bs.Name, bs.Data, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing %s contents: %w", bs.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return diffModFiles(af, bf, opts)
|
||||
}
|
||||
|
||||
func diffModFiles(a *modfile.File, b *modfile.File, opts *DiffOpts) (ModDiff, error) {
|
||||
if a == nil {
|
||||
return nil, errors.New("missing a mod file")
|
||||
}
|
||||
|
||||
if b == nil {
|
||||
return nil, errors.New("missing b mod file")
|
||||
}
|
||||
|
||||
diff := ModDiff{}
|
||||
if opts.Module {
|
||||
diff = append(diff, diffModule(a, b))
|
||||
}
|
||||
if opts.Go {
|
||||
diff = append(diff, diffGo(a, b))
|
||||
}
|
||||
if opts.Toolchain {
|
||||
diff = append(diff, diffToolchain(a, b))
|
||||
}
|
||||
if opts.Godebug {
|
||||
diff = append(diff, diffGodebug(a, b)...)
|
||||
}
|
||||
if opts.Require || opts.StrictDiffRequire {
|
||||
diff = append(diff, diffRequire(a, b, opts.StrictDiffRequire)...)
|
||||
}
|
||||
if opts.Exclude || opts.StrictDiffExclude {
|
||||
diff = append(diff, diffExclude(a, b, opts.StrictDiffExclude)...)
|
||||
}
|
||||
if opts.Replace || opts.StrictDiffReplace {
|
||||
diff = append(diff, diffReplace(a, b, opts.StrictDiffReplace)...)
|
||||
}
|
||||
if opts.Retract || opts.StrictDiffRetract {
|
||||
diff = append(diff, diffRetract(a, b, opts.StrictDiffRetract)...)
|
||||
}
|
||||
if opts.Tool {
|
||||
diff = append(diff, diffTool(a, b)...)
|
||||
}
|
||||
if opts.Ignore {
|
||||
diff = append(diff, diffIgnore(a, b)...)
|
||||
}
|
||||
|
||||
if len(diff) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
diff = slices.DeleteFunc(diff, func(d *Diff) bool { return d == nil })
|
||||
slices.SortStableFunc(diff, func(a *Diff, b *Diff) int {
|
||||
// a < b = 1
|
||||
// a == b = 0
|
||||
// a > b = -1
|
||||
if a == nil && b == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if a == nil && b != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
if a != nil && b == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
if n := strings.Compare(string(a.Directive), string(b.Directive)); n != 0 {
|
||||
return n
|
||||
}
|
||||
|
||||
if a.Diff == nil && b.Diff == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if a.Diff == nil && b.Diff != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
if a.Diff != nil && b.Diff == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
atxt, aerr := difflib.GetUnifiedDiffString(*a.Diff)
|
||||
btxt, berr := difflib.GetUnifiedDiffString(*b.Diff)
|
||||
|
||||
if aerr == nil && berr != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
if aerr != nil && berr == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
return strings.Compare(atxt, btxt)
|
||||
})
|
||||
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
func newDiffFromModFiles(a, b *modfile.File, typ Directive) *Diff {
|
||||
res := &Diff{
|
||||
Directive: typ,
|
||||
Diff: &difflib.UnifiedDiff{Context: 1},
|
||||
}
|
||||
if a != nil && a.Syntax != nil {
|
||||
res.Diff.FromFile = a.Syntax.Name
|
||||
}
|
||||
if b != nil && b.Syntax != nil {
|
||||
res.Diff.ToFile = b.Syntax.Name
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
517
tools/pipeline/internal/pkg/golang/mod_diff_test.go
Normal file
517
tools/pipeline/internal/pkg/golang/mod_diff_test.go
Normal file
|
|
@ -0,0 +1,517 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package golang
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_DiffModFiles_Equal(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
modA, err := os.ReadFile("./fixtures/moda/go.mod")
|
||||
require.NoError(t, err)
|
||||
|
||||
modB, err := os.ReadFile("./fixtures/modb/go.mod")
|
||||
require.NoError(t, err)
|
||||
|
||||
for desc, test := range map[string]struct {
|
||||
as *ModSource
|
||||
bs *ModSource
|
||||
opts *DiffOpts
|
||||
}{
|
||||
"moda not lax not strict": {
|
||||
&ModSource{Name: "moda-1", Data: modA},
|
||||
&ModSource{Name: "moda-2", Data: modA},
|
||||
&DiffOpts{StrictDiffRequire: false, ParseLax: false},
|
||||
},
|
||||
"moda lax not strict": {
|
||||
&ModSource{Name: "moda-1", Data: modA},
|
||||
&ModSource{Name: "moda-2", Data: modA},
|
||||
&DiffOpts{StrictDiffRequire: false, ParseLax: true},
|
||||
},
|
||||
"moda not lax strict": {
|
||||
&ModSource{Name: "moda-1", Data: modA},
|
||||
&ModSource{Name: "moda-2", Data: modA},
|
||||
&DiffOpts{StrictDiffRequire: true, ParseLax: false},
|
||||
},
|
||||
"moda lax strict": {
|
||||
&ModSource{Name: "moda-1", Data: modA},
|
||||
&ModSource{Name: "moda-2", Data: modA},
|
||||
&DiffOpts{StrictDiffRequire: true, ParseLax: true},
|
||||
},
|
||||
"modb not lax not strict": {
|
||||
&ModSource{Name: "modb-1", Data: modB},
|
||||
&ModSource{Name: "modb-2", Data: modB},
|
||||
&DiffOpts{StrictDiffRequire: false, ParseLax: false},
|
||||
},
|
||||
"modb lax not strict": {
|
||||
&ModSource{Name: "modb-1", Data: modB},
|
||||
&ModSource{Name: "modb-2", Data: modB},
|
||||
&DiffOpts{StrictDiffRequire: false, ParseLax: true},
|
||||
},
|
||||
"modb not lax strict": {
|
||||
&ModSource{Name: "modb-1", Data: modB},
|
||||
&ModSource{Name: "modb-2", Data: modB},
|
||||
&DiffOpts{StrictDiffRequire: true, ParseLax: false},
|
||||
},
|
||||
"modb lax strict": {
|
||||
&ModSource{Name: "modb-1", Data: modB},
|
||||
&ModSource{Name: "modb-2", Data: modB},
|
||||
&DiffOpts{StrictDiffRequire: true, ParseLax: true},
|
||||
},
|
||||
} {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
diff, err := DiffModFiles(test.as, test.bs, test.opts)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, diff, "expected no diff, got:\n%v", printModDiff(diff))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_DiffModFiles_Diff(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
modA, err := os.ReadFile("./fixtures/moda/go.mod")
|
||||
require.NoError(t, err)
|
||||
|
||||
modB, err := os.ReadFile("./fixtures/modb/go.mod")
|
||||
require.NoError(t, err)
|
||||
|
||||
as := &ModSource{Name: "moda", Data: modA}
|
||||
bs := &ModSource{Name: "modb", Data: modB}
|
||||
|
||||
for desc, test := range map[string]struct {
|
||||
opts *DiffOpts
|
||||
condition func(t *testing.T, got ModDiff)
|
||||
}{
|
||||
"strict parse lax diff": {
|
||||
&DiffOpts{
|
||||
ParseLax: false,
|
||||
Module: true,
|
||||
Go: true,
|
||||
Toolchain: true,
|
||||
Godebug: true,
|
||||
Require: true,
|
||||
Exclude: true,
|
||||
Replace: true,
|
||||
Retract: true,
|
||||
Tool: true,
|
||||
Ignore: true,
|
||||
StrictDiffRequire: false,
|
||||
StrictDiffExclude: false,
|
||||
StrictDiffReplace: false,
|
||||
StrictDiffRetract: false,
|
||||
},
|
||||
func(t *testing.T, got ModDiff) {
|
||||
hasDiffMatching(t, got, DirectiveModule, []string{
|
||||
"-module github.com/hashicorp/vault/pipeline/golang/moda",
|
||||
"+module github.com/hashicorp/vault/pipeline/golang/modb",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveGo, []string{
|
||||
"-go 1.25",
|
||||
"+go 1.25.2",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveToolchain, []string{
|
||||
"+toolchain go1.24",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-default=go1.21",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-httpcookiemaxnum=4000",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-panicnil=1",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveRequire, []string{
|
||||
"-require github.com/99designs/keyring v1.2.2",
|
||||
"+require github.com/99designs/keyring v0.0.0-00010101000000-000000000000",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveRequire, []string{
|
||||
"-github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveExclude, []string{
|
||||
"-exclude golang.org/x/term v0.2.0",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveReplace, []string{
|
||||
"-replace github.com/99designs/keyring => github.com/Jeffail/keyring v1.2.3",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveRetract, []string{
|
||||
"-[ v1.0.0 , v1.9.9 ]",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveRetract, []string{
|
||||
"-v0.9.0",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveTool, []string{
|
||||
"-tool golang.org/x/tools/cmd/bisect",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveTool, []string{
|
||||
"-tool golang.org/x/tools/cmd/stringer",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-./third_party/javascript",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-content/html",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-static",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-ignore ./node_modules",
|
||||
})
|
||||
},
|
||||
},
|
||||
"lax parse lax diff": {
|
||||
&DiffOpts{
|
||||
ParseLax: true,
|
||||
Module: true,
|
||||
Go: true,
|
||||
Toolchain: true,
|
||||
Godebug: true,
|
||||
Require: true,
|
||||
Exclude: true,
|
||||
Replace: true,
|
||||
Retract: true,
|
||||
Tool: true,
|
||||
Ignore: true,
|
||||
StrictDiffRequire: false,
|
||||
StrictDiffExclude: false,
|
||||
StrictDiffReplace: false,
|
||||
StrictDiffRetract: false,
|
||||
},
|
||||
func(t *testing.T, got ModDiff) {
|
||||
hasDiffMatching(t, got, DirectiveModule, []string{
|
||||
"-module github.com/hashicorp/vault/pipeline/golang/moda",
|
||||
"+module github.com/hashicorp/vault/pipeline/golang/modb",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveGo, []string{
|
||||
"-go 1.25",
|
||||
"+go 1.25.2",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveToolchain, []string{
|
||||
"+toolchain go1.24",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-default=go1.21",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-httpcookiemaxnum=4000",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-panicnil=1",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveRequire, []string{
|
||||
"-require github.com/99designs/keyring v1.2.2",
|
||||
"+require github.com/99designs/keyring v0.0.0-00010101000000-000000000000",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveRequire, []string{
|
||||
"-github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveExclude, []string{
|
||||
"-exclude golang.org/x/term v0.2.0",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveReplace, []string{
|
||||
"-replace github.com/99designs/keyring => github.com/Jeffail/keyring v1.2.3",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveRetract, []string{
|
||||
"-[ v1.0.0 , v1.9.9 ]",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveRetract, []string{
|
||||
"-v0.9.0",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveTool, []string{
|
||||
"-tool golang.org/x/tools/cmd/bisect",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveTool, []string{
|
||||
"-tool golang.org/x/tools/cmd/stringer",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveTool, []string{
|
||||
"-tool golang.org/x/tools/cmd/bisect",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveTool, []string{
|
||||
"-tool golang.org/x/tools/cmd/stringer",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-./third_party/javascript",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-content/html",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-static",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-ignore ./node_modules",
|
||||
})
|
||||
},
|
||||
},
|
||||
"strict parse strict diff": {
|
||||
&DiffOpts{
|
||||
ParseLax: false,
|
||||
Module: true,
|
||||
Go: true,
|
||||
Toolchain: true,
|
||||
Godebug: true,
|
||||
Require: true,
|
||||
Exclude: true,
|
||||
Replace: true,
|
||||
Retract: true,
|
||||
Tool: true,
|
||||
Ignore: true,
|
||||
StrictDiffRequire: true,
|
||||
StrictDiffExclude: true,
|
||||
StrictDiffReplace: true,
|
||||
StrictDiffRetract: true,
|
||||
},
|
||||
func(t *testing.T, got ModDiff) {
|
||||
hasDiffMatching(t, got, DirectiveModule, []string{
|
||||
"-module github.com/hashicorp/vault/pipeline/golang/moda",
|
||||
"+module github.com/hashicorp/vault/pipeline/golang/modb",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveGo, []string{
|
||||
"-go 1.25",
|
||||
"+go 1.25.2",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveToolchain, []string{
|
||||
"+toolchain go1.24",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-default=go1.21",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-httpcookiemaxnum=4000",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-panicnil=1",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveRequire, []string{
|
||||
"-require github.com/99designs/keyring v1.2.2",
|
||||
"+require github.com/99designs/keyring v0.0.0-00010101000000-000000000000",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveRequire, []string{
|
||||
"-github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveExclude, []string{
|
||||
"-exclude golang.org/x/term v0.2.0",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveReplace, []string{
|
||||
"-replace github.com/99designs/keyring => github.com/Jeffail/keyring v1.2.3",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveRetract, []string{
|
||||
"-[ v1.0.0 , v1.9.9 ]",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveRetract, []string{
|
||||
"-v0.9.0",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveTool, []string{
|
||||
"-tool golang.org/x/tools/cmd/bisect",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveTool, []string{
|
||||
"-tool golang.org/x/tools/cmd/stringer",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-./third_party/javascript",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-content/html",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-static",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-ignore ./node_modules",
|
||||
})
|
||||
},
|
||||
},
|
||||
"lax parse strict diff": {
|
||||
&DiffOpts{
|
||||
ParseLax: true,
|
||||
Module: true,
|
||||
Go: true,
|
||||
Toolchain: true,
|
||||
Godebug: true,
|
||||
Require: true,
|
||||
Exclude: true,
|
||||
Replace: true,
|
||||
Retract: true,
|
||||
Tool: true,
|
||||
Ignore: true,
|
||||
StrictDiffRequire: true,
|
||||
StrictDiffExclude: true,
|
||||
StrictDiffReplace: true,
|
||||
StrictDiffRetract: true,
|
||||
},
|
||||
func(t *testing.T, got ModDiff) {
|
||||
hasDiffMatching(t, got, DirectiveModule, []string{
|
||||
"-module github.com/hashicorp/vault/pipeline/golang/moda",
|
||||
"+module github.com/hashicorp/vault/pipeline/golang/modb",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveGo, []string{
|
||||
"-go 1.25",
|
||||
"+go 1.25.2",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveToolchain, []string{
|
||||
"+toolchain go1.24",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-default=go1.21",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-httpcookiemaxnum=4000",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveGodebug, []string{
|
||||
"-panicnil=1",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveRequire, []string{
|
||||
"-require github.com/99designs/keyring v1.2.2",
|
||||
"+require github.com/99designs/keyring v0.0.0-00010101000000-000000000000",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveRequire, []string{
|
||||
"-github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveExclude, []string{
|
||||
"-exclude golang.org/x/term v0.2.0",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveReplace, []string{
|
||||
"-replace github.com/99designs/keyring => github.com/Jeffail/keyring v1.2.3",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveRetract, []string{
|
||||
"-[ v1.0.0 , v1.9.9 ]",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveRetract, []string{
|
||||
"-v0.9.0",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveTool, []string{
|
||||
"-tool golang.org/x/tools/cmd/bisect",
|
||||
})
|
||||
noDiffMatching(t, got, DirectiveTool, []string{
|
||||
"-tool golang.org/x/tools/cmd/stringer",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-content/html",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-static",
|
||||
})
|
||||
hasDiffMatching(t, got, DirectiveIgnore, []string{
|
||||
"-ignore ./node_modules",
|
||||
})
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
diff, err := DiffModFiles(as, bs, test.opts)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, diff, "expected a module diff")
|
||||
test.condition(t, diff)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func hasDiffMatching(t *testing.T, diff ModDiff, dir Directive, matches []string) {
|
||||
t.Helper()
|
||||
|
||||
require.NotNil(t, diff)
|
||||
diffs := getDiffsForDirective(dir, diff)
|
||||
require.True(t, len(diffs) > 0, "expected %s matching %v, got diff:\n%s",
|
||||
dir,
|
||||
matches,
|
||||
printModDiff(diff),
|
||||
)
|
||||
for _, df := range diffs {
|
||||
if unifiedTextMatches(df, matches) {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Fatalf("expected %s diff matching %v, got diff:\n%s",
|
||||
dir,
|
||||
matches,
|
||||
printModDiff(diff),
|
||||
)
|
||||
}
|
||||
|
||||
func noDiffMatching(t *testing.T, diff ModDiff, dir Directive, matches []string) {
|
||||
t.Helper()
|
||||
|
||||
require.NotNil(t, diff)
|
||||
diffs := getDiffsForDirective(dir, diff)
|
||||
if len(diffs) < 1 {
|
||||
return
|
||||
}
|
||||
for _, df := range diffs {
|
||||
if unifiedTextMatches(df, matches) {
|
||||
t.Fatalf("expected no %s diff matching %v, got diff:\n%s",
|
||||
dir,
|
||||
matches,
|
||||
printModDiff(diff),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printModDiff(d ModDiff) string {
|
||||
if d == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
b := strings.Builder{}
|
||||
for _, diff := range d {
|
||||
if exp := diff.Explanation(); exp != "" {
|
||||
b.WriteString(exp + "\n")
|
||||
}
|
||||
if dt := diff.UnifiedText(); dt != "" {
|
||||
b.WriteString(dt + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func getDiffsForDirective(dir Directive, diff ModDiff) []*Diff {
|
||||
if len(diff) < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
diffs := []*Diff{}
|
||||
for _, d := range diff {
|
||||
if d == nil {
|
||||
continue
|
||||
}
|
||||
if d.Directive == dir {
|
||||
diffs = append(diffs, d)
|
||||
}
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
|
||||
func unifiedTextMatches(diff *Diff, matches []string) bool {
|
||||
if diff == nil && len(matches) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if diff == nil && len(matches) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
txt := diff.UnifiedText()
|
||||
if txt == "" {
|
||||
return false
|
||||
}
|
||||
for _, m := range matches {
|
||||
if !strings.Contains(txt, m) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
Loading…
Reference in a new issue