github.com/jdhenke/godel@v0.0.0-20161213181855-abeb3861bf0d/apps/gonform/cmd/format.go (about) 1 // Copyright 2016 Palantir Technologies, Inc. 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 15 package cmd 16 17 import ( 18 "bytes" 19 "io" 20 "path" 21 22 "github.com/nmiyake/pkg/dirs" 23 "github.com/palantir/amalgomate/amalgomated" 24 "github.com/palantir/pkg/cli" 25 "github.com/palantir/pkg/cli/cfgcli" 26 "github.com/palantir/pkg/cli/flag" 27 "github.com/palantir/pkg/matcher" 28 "github.com/pkg/errors" 29 30 "github.com/palantir/godel/apps/gonform/config" 31 "github.com/palantir/godel/apps/gonform/params" 32 ) 33 34 var ( 35 goFmt = Library.MustNewCmd("gofmt") 36 ptImports = Library.MustNewCmd("ptimports") 37 ) 38 39 func RunAllCommand(supplier amalgomated.CmderSupplier) cli.Command { 40 return cli.Command{ 41 Name: "runAll", 42 Usage: "Run all format commands on Go files", 43 Action: func(ctx cli.Context) error { 44 wd, err := dirs.GetwdEvalSymLinks() 45 if err != nil { 46 return err 47 } 48 return DoRunAll(ctx.Slice(filesParamName), ctx, supplier, wd) 49 }, 50 Flags: []flag.Flag{ 51 FilesParam, 52 }, 53 } 54 } 55 56 func GoFmtCommand(supplier amalgomated.CmderSupplier) cli.Command { 57 return formatCommand(goFmt, "Run ptimports on Go files", supplier) 58 } 59 60 func PTImportsCommand(supplier amalgomated.CmderSupplier) cli.Command { 61 return formatCommand(ptImports, "Run ptimports on Go files", supplier) 62 } 63 64 func DoRunAll(filesParam []string, ctx cli.Context, supplier amalgomated.CmderSupplier, wd string) error { 65 cmds := []amalgomated.Cmd{ 66 goFmt, 67 ptImports, 68 } 69 params, err := createParams(filesParam, ctx, wd) 70 if err != nil { 71 return err 72 } 73 for _, currCmd := range cmds { 74 err := doFormat(currCmd, params, ctx, supplier, wd) 75 if err != nil { 76 return err 77 } 78 } 79 return nil 80 } 81 82 func formatCommand(cmd amalgomated.Cmd, usage string, supplier amalgomated.CmderSupplier) cli.Command { 83 return cli.Command{ 84 Name: cmd.Name(), 85 Usage: usage, 86 Action: func(ctx cli.Context) error { 87 wd, err := dirs.GetwdEvalSymLinks() 88 if err != nil { 89 return err 90 } 91 return doSingleFormat(cmd, ctx.Slice(filesParamName), ctx, supplier, wd) 92 }, 93 Flags: []flag.Flag{ 94 FilesParam, 95 }, 96 } 97 } 98 99 type formatterParams struct { 100 Formatters map[string]params.Formatter 101 Files []string 102 List bool 103 Verbose bool 104 } 105 106 func doSingleFormat(cmd amalgomated.Cmd, filesParam []string, ctx cli.Context, supplier amalgomated.CmderSupplier, wd string) error { 107 params, err := createParams(filesParam, ctx, wd) 108 if err != nil { 109 return err 110 } 111 return doFormat(cmd, params, ctx, supplier, wd) 112 } 113 114 func createParams(filesParam []string, ctx cli.Context, wd string) (formatterParams, error) { 115 cfg, err := config.Load(cfgcli.ConfigPath, cfgcli.ConfigJSON) 116 if err != nil { 117 return formatterParams{}, errors.Wrapf(err, "failed to load configuration") 118 } 119 120 files, err := getFiles(filesParam, cfg.Exclude, wd) 121 if err != nil { 122 return formatterParams{}, errors.Wrapf(err, "failed to get files") 123 } 124 125 return formatterParams{ 126 Formatters: cfg.Formatters, 127 Files: files, 128 List: ctx.Bool(listFlagName), 129 Verbose: ctx.Bool(verboseFlagName), 130 }, nil 131 } 132 133 func doFormat(cmd amalgomated.Cmd, params formatterParams, ctx cli.Context, supplier amalgomated.CmderSupplier, wd string) error { 134 if len(params.Files) == 0 { 135 return nil 136 } 137 138 supplier = amalgomated.SupplierWithPrependedArgs(supplier, func(cmd amalgomated.Cmd) []string { 139 formatArg := "-w" 140 if params.List { 141 formatArg = "-l" 142 } 143 return append(params.Formatters[cmd.Name()].Args, formatArg) 144 }) 145 146 combinedBuf := bytes.Buffer{} 147 stdoutMultiWriter := io.MultiWriter(ctx.App.Stdout, &combinedBuf) 148 stderrMultiWriter := io.MultiWriter(ctx.App.Stderr, &combinedBuf) 149 150 cmder, err := supplier(cmd) 151 if err != nil { 152 return errors.Wrapf(err, "failed to create command %s", cmd.Name()) 153 } 154 155 if params.Verbose { 156 ctx.Printf("Running %s...\n", cmd.Name()) 157 } 158 execCmd := cmder.Cmd(params.Files, wd) 159 execCmd.Stdout = stdoutMultiWriter 160 execCmd.Stderr = stderrMultiWriter 161 162 if err := execCmd.Run(); err != nil { 163 return errors.Wrapf(err, "command %s failed", cmd.Name()) 164 } else if params.List && len(combinedBuf.Bytes()) != 0 { 165 if params.Verbose { 166 return errors.Errorf("%s failed: unformatted Go files found", cmd.Name()) 167 } 168 return errors.Errorf("") 169 } 170 return nil 171 } 172 173 func getFiles(filesParam []string, exclude matcher.Matcher, wd string) ([]string, error) { 174 var files []string 175 var err error 176 if len(filesParam) == 0 { 177 // exclude entries specified by the configuration 178 files, err = matcher.ListFiles(wd, matcher.Name(`.*\.go`), exclude) 179 if err != nil { 180 return nil, errors.Wrapf(err, "failed to list *.go files in %d", wd) 181 } 182 } else { 183 // filter arguments based on exclude config 184 files = make([]string, 0, len(filesParam)) 185 for _, currPath := range filesParam { 186 if !exclude.Match(currPath) { 187 files = append(files, currPath) 188 } 189 190 } 191 } 192 files = toAbsPaths(wd, files) 193 194 if len(files) == 0 { 195 // no files to process after exclusions applied 196 return nil, nil 197 } 198 199 return files, nil 200 } 201 202 func toAbsPaths(basePath string, relPaths []string) []string { 203 paths := make([]string, len(relPaths)) 204 for i, currPath := range relPaths { 205 paths[i] = path.Join(basePath, currPath) 206 } 207 return paths 208 }