github.com/thetreep/go-swagger@v0.0.0-20240223100711-35af64f14f01/cmd/swagger/commands/diff.go (about) 1 package commands 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io" 8 "log" 9 "os" 10 11 "github.com/go-openapi/loads" 12 "github.com/thetreep/go-swagger/cmd/swagger/commands/diff" 13 ) 14 15 // JSONFormat for json 16 const JSONFormat = "json" 17 18 // DiffCommand is a command that generates the diff of two swagger specs. 19 // 20 // There are no specific options for this expansion. 21 type DiffCommand struct { 22 OnlyBreakingChanges bool `long:"break" short:"b" description:"When present, only shows incompatible changes"` 23 Format string `long:"format" short:"f" description:"When present, writes output as json" default:"txt" choice:"txt" choice:"json"` 24 IgnoreFile string `long:"ignore" short:"i" description:"Exception file of diffs to ignore (copy output from json diff format)" default:"none specified"` 25 Destination string `long:"dest" short:"d" description:"Output destination file or stdout" default:"stdout"` 26 Args struct { 27 OldSpec string `positional-arg-name:"{old spec}"` 28 NewSpec string `positional-arg-name:"{new spec}"` 29 } `required:"2" positional-args:"specs" description:"Input specs to be diff-ed"` 30 } 31 32 // Execute diffs the two specs provided 33 func (c *DiffCommand) Execute(_ []string) error { 34 if c.Args.OldSpec == "" || c.Args.NewSpec == "" { 35 return errors.New(`missing arguments for diff command (use --help for more info)`) 36 } 37 38 c.printInfo() 39 40 var ( 41 output io.WriteCloser 42 err error 43 ) 44 if c.Destination != "stdout" { 45 output, err = os.OpenFile(c.Destination, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) 46 if err != nil { 47 return fmt.Errorf("%s: %w", c.Destination, err) 48 } 49 defer func() { 50 _ = output.Close() 51 }() 52 } else { 53 output = os.Stdout 54 } 55 56 diffs, err := c.getDiffs() 57 if err != nil { 58 return err 59 } 60 61 ignores, err := c.readIgnores() 62 if err != nil { 63 return err 64 } 65 66 diffs = diffs.FilterIgnores(ignores) 67 if len(ignores) > 0 { 68 log.Printf("Diff Report Ignored Items from IgnoreFile") 69 for _, eachItem := range ignores { 70 log.Printf("%s", eachItem.String()) 71 } 72 } 73 74 var ( 75 input io.Reader 76 warn error 77 ) 78 if c.Format != JSONFormat && c.OnlyBreakingChanges { 79 input, err, warn = diffs.ReportCompatibility() 80 } else { 81 input, err, warn = diffs.ReportAllDiffs(c.Format == JSONFormat) 82 } 83 if err != nil { 84 return err 85 } 86 _, err = io.Copy(output, input) 87 if err != nil { 88 return err 89 } 90 return warn 91 } 92 93 func (c *DiffCommand) readIgnores() (diff.SpecDifferences, error) { 94 ignoreFile := c.IgnoreFile 95 ignoreDiffs := diff.SpecDifferences{} 96 97 if ignoreFile == "none specified" || ignoreFile == "" { 98 return ignoreDiffs, nil 99 } 100 // Open our jsonFile 101 jsonFile, err := os.Open(ignoreFile) 102 if err != nil { 103 return nil, fmt.Errorf("%s: %w", ignoreFile, err) 104 } 105 defer func() { 106 _ = jsonFile.Close() 107 }() 108 byteValue, err := io.ReadAll(jsonFile) 109 if err != nil { 110 return nil, fmt.Errorf("reading %s: %w", ignoreFile, err) 111 } 112 err = json.Unmarshal(byteValue, &ignoreDiffs) 113 if err != nil { 114 return nil, err 115 } 116 return ignoreDiffs, nil 117 } 118 119 func (c *DiffCommand) getDiffs() (diff.SpecDifferences, error) { 120 oldSpecPath, newSpecPath := c.Args.OldSpec, c.Args.NewSpec 121 swaggerDoc1 := oldSpecPath 122 specDoc1, err := loads.Spec(swaggerDoc1) 123 if err != nil { 124 return nil, err 125 } 126 127 swaggerDoc2 := newSpecPath 128 specDoc2, err := loads.Spec(swaggerDoc2) 129 if err != nil { 130 return nil, err 131 } 132 133 return diff.Compare(specDoc1.Spec(), specDoc2.Spec()) 134 } 135 136 func (c *DiffCommand) printInfo() { 137 log.Println("Run Config:") 138 log.Printf("Spec1: %s", c.Args.OldSpec) 139 log.Printf("Spec2: %s", c.Args.NewSpec) 140 log.Printf("ReportOnlyBreakingChanges (-c) :%v", c.OnlyBreakingChanges) 141 log.Printf("OutputFormat (-f) :%s", c.Format) 142 log.Printf("IgnoreFile (-i) :%s", c.IgnoreFile) 143 log.Printf("Diff Report Destination (-d) :%s", c.Destination) 144 }