github.com/andresbott/yamlfmt@v0.1.0/app/cli/cli.go (about) 1 // ADOBE CONFIDENTIAL 2 // ___________________ 3 // 4 // Copyright 2022 Adobe 5 // All Rights Reserved. 6 // 7 // NOTICE: All information contained herein is, and remains 8 // the property of Adobe and its suppliers, if any. The intellectual 9 // and technical concepts contained herein are proprietary to Adobe 10 // and its suppliers and are protected by all applicable intellectual 11 // property laws, including trade secret and copyright laws. 12 // Dissemination of this information or reproduction of this material 13 // is strictly forbidden unless prior written permission is obtained 14 // from Adobe. 15 16 package cli 17 18 import ( 19 "bytes" 20 "crypto/sha256" 21 "fmt" 22 "io" 23 "log" 24 "os" 25 "path/filepath" 26 "runtime" 27 28 "github.com/andresbott/yamlfmt/internal/filematch" 29 "github.com/andresbott/yamlfmt/internal/yamlfmt" 30 "github.com/google/go-cmp/cmp" 31 "github.com/spf13/cobra" 32 ) 33 34 // Execute is the entry point for the command line 35 func Execute() { 36 if err := rootCmd().Execute(); err != nil { 37 fmt.Println(err) 38 os.Exit(1) 39 } 40 } 41 42 var ( 43 ShaVer string // sha1 revision used to build the program 44 BuildTime string // when the executable was built 45 Version = "development" 46 ) 47 48 const name = "yamlfmt" 49 50 func rootCmd() *cobra.Command { 51 var dry bool 52 var verbose bool 53 var quiet bool 54 var version bool 55 56 cmd := &cobra.Command{ 57 Use: name + " <glob>", 58 Long: name + " format yaml files to a opinionated defaults, it is inspired ing go fmt. \n" + 59 "The input accepts any regular glob patterns plus some convenience aliases:" + 60 "\n- \"./\" is an alias for: \"./*.yaml\" and \"./*.yml\"" + 61 "\n- \"./..\" is an alias for: \"./**/*.yaml\" and \"./**/*.yml\" to search recursively" + 62 "\n- any existing directory will be searched for \"./*.yaml\" and \"./*.yml\"", 63 64 Short: "format yaml files", 65 RunE: func(cmd *cobra.Command, args []string) error { 66 67 if version { 68 fmt.Printf("Version: %s\n", Version) 69 fmt.Printf("Build date: %s\n", BuildTime) 70 fmt.Printf("Commit sha: %s\n", ShaVer) 71 fmt.Printf("Compiler: %s\n", runtime.Version()) 72 73 return nil 74 } 75 76 pattern := "" 77 if len(args) > 0 { 78 pattern = args[0] 79 80 } 81 82 files, err := filematch.FindFiles(pattern) 83 if err != nil { 84 return fmt.Errorf("unable to find files: %v", err) 85 } 86 87 p := printer{ 88 quiet: quiet, 89 verbose: verbose, 90 } 91 92 if len(files) == 0 { 93 p.print("no files found") 94 return nil 95 } 96 97 if dry { 98 p.print("(running in dry run mode, no changes will be written)") 99 } 100 101 for _, f := range files { 102 103 abs, err := filepath.Abs(f) 104 if err != nil { 105 return fmt.Errorf("unable to get absolute path for: %s, %s", f, err.Error()) 106 } 107 fInfo, err := os.Stat(abs) 108 if err != nil { 109 return fmt.Errorf("unable to stat file: %s, %s", f, err.Error()) 110 } 111 if fInfo.IsDir() { 112 return fmt.Errorf("%s is a directoru: %s", f, err.Error()) 113 } 114 115 maxFileSize := int64(50 * 1024 * 1024) 116 if fInfo.Size() > maxFileSize { 117 return fmt.Errorf("file %s is bigger than 50MB", f) 118 } 119 120 inBytes, err := os.ReadFile(abs) 121 if err != nil { 122 return fmt.Errorf("unable to read file: %s", f) 123 } 124 125 inHash := fileHash(inBytes) 126 got, err := yamlfmt.Format(inBytes) 127 if err != nil { 128 return fmt.Errorf("unable to format file %s:%s", f, err.Error()) 129 } 130 outHash := fileHash(got) 131 132 if inHash != outHash { 133 p.print(f) 134 diff := cmp.Diff(string(inBytes), string(got)) 135 p.printVerbose(diff) 136 137 if !dry { 138 err = os.WriteFile(abs, got, fInfo.Mode()) 139 if err != nil { 140 return fmt.Errorf("unable to write file: %s", f) 141 } 142 } 143 } 144 } 145 return nil 146 }, 147 } 148 149 cmd.Flags().BoolVarP(&dry, "dry-run", "d", false, "do not persists changes") 150 cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "print the diff for every file to change") 151 cmd.Flags().BoolVarP(&quiet, "quiet", "q", false, "dont print any output, takes precedence over verbose") 152 cmd.Flags().BoolVar(&version, "version", false, "print version and build information") 153 154 return cmd 155 } 156 157 func fileHash(in []byte) string { 158 r := bytes.NewReader(in) 159 160 h := sha256.New() 161 if _, err := io.Copy(h, r); err != nil { 162 log.Fatal(err) 163 } 164 return fmt.Sprintf("%x", h.Sum(nil)) 165 }