1// Copyright 2019 CUE Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package cmd 16 17import ( 18 "io/ioutil" 19 "os" 20 "path/filepath" 21 "strings" 22 23 "cuelang.org/go/cue/ast" 24 "cuelang.org/go/cue/errors" 25 "cuelang.org/go/cue/format" 26 "cuelang.org/go/cue/load" 27 "cuelang.org/go/cue/token" 28 "cuelang.org/go/tools/fix" 29 "github.com/spf13/cobra" 30) 31 32func newFixCmd(c *Command) *cobra.Command { 33 cmd := &cobra.Command{ 34 Use: "fix [packages]", 35 Short: "rewrite packages to latest standards", 36 Long: `Fix finds CUE programs that use old syntax and old APIs and rewrites them to use newer ones. 37After you update to a new CUE release, fix helps make the necessary changes 38to your program. 39 40Without any packages, fix applies to all files within a module. 41`, 42 RunE: mkRunE(c, runFixAll), 43 } 44 45 cmd.Flags().BoolP(string(flagForce), "f", false, 46 "rewrite even when there are errors") 47 48 return cmd 49} 50 51func runFixAll(cmd *Command, args []string) error { 52 dir, err := os.Getwd() 53 if err != nil { 54 return err 55 } 56 57 var opts []fix.Option 58 if flagSimplify.Bool(cmd) { 59 opts = append(opts, fix.Simplify()) 60 } 61 62 if len(args) == 0 { 63 args = []string{"./..."} 64 65 for { 66 if fi, err := os.Stat(filepath.Join(dir, "cue.mod")); err == nil { 67 if fi.IsDir() { 68 args = appendDirs(args, filepath.Join(dir, "cue.mod", "gen")) 69 args = appendDirs(args, filepath.Join(dir, "cue.mod", "pkg")) 70 args = appendDirs(args, filepath.Join(dir, "cue.mod", "usr")) 71 } else { 72 args = appendDirs(args, filepath.Join(dir, "pkg")) 73 } 74 break 75 } 76 77 dir = filepath.Dir(dir) 78 if info, _ := os.Stat(dir); !info.IsDir() { 79 return errors.Newf(token.NoPos, "no module root found") 80 } 81 } 82 } 83 84 instances := load.Instances(args, &load.Config{ 85 Tests: true, 86 Tools: true, 87 }) 88 89 errs := fix.Instances(instances, opts...) 90 91 if errs != nil && flagForce.Bool(cmd) { 92 return errs 93 } 94 95 done := map[*ast.File]bool{} 96 97 for _, i := range instances { 98 for _, f := range i.Files { 99 if done[f] || !strings.HasSuffix(f.Filename, ".cue") { 100 continue 101 } 102 done[f] = true 103 104 b, err := format.Node(f) 105 if err != nil { 106 errs = errors.Append(errs, errors.Promote(err, "format")) 107 } 108 109 err = ioutil.WriteFile(f.Filename, b, 0644) 110 if err != nil { 111 errs = errors.Append(errs, errors.Promote(err, "write")) 112 } 113 } 114 } 115 116 return errs 117} 118 119func appendDirs(a []string, base string) []string { 120 _ = filepath.Walk(base, func(path string, fi os.FileInfo, err error) error { 121 if err == nil && fi.IsDir() && path != base { 122 short := filepath.ToSlash(path[len(base)+1:]) 123 if strings.ContainsAny(short, "/") { 124 a = append(a, short) 125 } 126 } 127 return nil 128 }) 129 return a 130} 131