cmd/cue/cmd: add safeguard to trim operation
Compare output of generated trim to ensure
the results are identical.
Issue #277
Change-Id: I96c0eeb960d34141dc8de0f60c2d5102b164501c
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/4909
Reviewed-by: Daniel Martà <mvdan@mvdan.cc>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/trim.go b/cmd/cue/cmd/trim.go
index 44d4e36..3df6902 100644
--- a/cmd/cue/cmd/trim.go
+++ b/cmd/cue/cmd/trim.go
@@ -15,6 +15,7 @@
package cmd
import (
+ "errors"
"fmt"
"io/ioutil"
"os"
@@ -22,7 +23,9 @@
"github.com/spf13/cobra"
"cuelang.org/go/cue/format"
+ "cuelang.org/go/cue/load"
"cuelang.org/go/internal"
+ "cuelang.org/go/internal/diff"
"cuelang.org/go/tools/trim"
)
@@ -117,6 +120,8 @@
}
}
+ overlay := map[string]load.Source{}
+
for i, inst := range binst {
root := instances[i]
err := trim.Files(inst.Files, root, &trim.Config{
@@ -126,10 +131,35 @@
return err
}
- if flagDryrun.Bool(cmd) {
- continue
+ for _, f := range inst.Files {
+ overlay[f.Filename] = load.FromFile(f)
}
+ }
+
+ cfg := *defaultConfig
+ cfg.Overlay = overlay
+ tinsts := buildInstances(cmd, load.Instances(args, &cfg))
+ if len(tinsts) != len(binst) {
+ return errors.New("unexpected number of new instances")
+ }
+ if !flagIgnore.Bool(cmd) {
+ for i, p := range instances {
+ k, script := diff.Final.Diff(p.Value(), tinsts[i].Value())
+ if k != diff.Identity {
+ diff.Print(os.Stdout, script)
+ fmt.Println("Aborting trim, output differs after trimming. This is a bug! Use -i to force trim.")
+ fmt.Println("You can file a bug here: https://github.com/cuelang/cue/issues/new?assignees=&labels=NeedsInvestigation&template=bug_report.md&title=")
+ os.Exit(1)
+ }
+ }
+ }
+
+ if flagDryrun.Bool(cmd) {
+ return nil
+ }
+
+ for _, inst := range binst {
for _, f := range inst.Files {
filename := f.Filename
diff --git a/internal/diff/diff.go b/internal/diff/diff.go
index 2344a22..e3a5013 100644
--- a/internal/diff/diff.go
+++ b/internal/diff/diff.go
@@ -22,9 +22,29 @@
"cuelang.org/go/cue/errors"
)
-// Diff returns an edit script representing the difference between x and y.
+// Profile configures a diff operation.
+type Profile struct {
+ Concrete bool
+}
+
+var (
+ // Schema is the standard profile used for comparing schema.
+ Schema = &Profile{}
+
+ // Final is the standard profile for comparing data.
+ Final = &Profile{
+ Concrete: true,
+ }
+)
+
+// Diff is a shorthand for Schema.Diff.
func Diff(x, y cue.Value) (Kind, *EditScript) {
- d := differ{}
+ return Schema.Diff(x, y)
+}
+
+// Diff returns an edit script representing the difference between x and y.
+func (p *Profile) Diff(x, y cue.Value) (Kind, *EditScript) {
+ d := differ{cfg: p}
return d.diffValue(x, y)
}
@@ -137,11 +157,16 @@
func (e Edit) YPos() int { return int(e.yPos - 1) }
type differ struct {
+ cfg *Profile
options []cue.Option
errs errors.Error
}
func (d *differ) diffValue(x, y cue.Value) (Kind, *EditScript) {
+ if d.cfg.Concrete {
+ x, _ = x.Default()
+ y, _ = y.Default()
+ }
if x.IncompleteKind() != y.IncompleteKind() {
return Modified, nil
}