github.com/amplia-iiot/yutil@v1.0.1-0.20231229120411-5d96a4c5a136/cmd/replace.go (about) 1 /* 2 Copyright (c) 2023 Adrian Haasler GarcĂa <dev@ahaasler.com> 3 4 Permission is hereby granted, free of charge, to any person obtaining a copy 5 of this software and associated documentation files (the "Software"), to deal 6 in the Software without restriction, including without limitation the rights 7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 copies of the Software, and to permit persons to whom the Software is 9 furnished to do so, subject to the following conditions: 10 11 The above copyright notice and this permission notice shall be included in all 12 copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 SOFTWARE. 21 */ 22 23 package cmd 24 25 import ( 26 "fmt" 27 28 "github.com/amplia-iiot/yutil/internal/io" 29 "github.com/amplia-iiot/yutil/pkg/replace" 30 "github.com/spf13/cobra" 31 ) 32 33 type replaceOptions struct { 34 golang bool 35 jinja2 bool 36 directory string 37 node string 38 include []string 39 exclude []string 40 replacementFiles []string 41 includeEnv bool 42 extensions []string 43 } 44 45 func (o replaceOptions) engine() replace.Engine { 46 if o.jinja2 { 47 return replace.Jinja2 48 } 49 return replace.Golang 50 } 51 52 var rOptions replaceOptions 53 54 // replaceCmd represents the replace command 55 var replaceCmd = &cobra.Command{ 56 Use: "replace", 57 Short: "Replace files using a template engine", 58 Long: `Replace files with a template engine, using one or 59 more replacement files that will be merged. Files should be ordered 60 in ascending level of importance in the hierarchy. A yaml 61 node in the last file replaces values in any previous file. Stdin is 62 treated as first replacement file. 63 64 By default all files in the current directory and subdirectories are passed 65 through the template engine. Use include and exclude to filter files. 66 If no engine is picked the default golang template with slim-sprig functions 67 will be used. 68 69 The extension/s is/are used for including those files by default (unless 70 include flag is used) and renaming replaced files accordingly. 71 72 For example: 73 74 yutil replace -r base.yml -r changes.yml -d directory 75 cat base.yml | yutil replace 76 yutil replace -r config.yml -n root_node --jinja2 77 yutil replace -r config.yml -e .go -e .gotempl --env 78 yutil replace -r config.yml --include 'directory/*.conf' 79 yutil replace -r config.yml --jinja2 -d directory --exclude '*/secret/*' 80 echo "this is not a yaml" | yutil --no-input replace -r base.yml -r changes.yml 81 `, 82 RunE: func(cmd *cobra.Command, args []string) error { 83 engine := rOptions.engine() 84 extensions := rOptions.extensions 85 include := rOptions.include 86 switch engine { 87 case replace.Golang: 88 if len(extensions) == 0 { 89 extensions = append(extensions, ".tmpl") 90 } 91 case replace.Jinja2: 92 if len(extensions) == 0 { 93 extensions = append(extensions, ".j2") 94 } 95 } 96 if len(include) == 0 { 97 for _, ext := range extensions { 98 include = append(include, fmt.Sprintf("*%s.*", ext), fmt.Sprintf("*%s", ext)) 99 } 100 } 101 if !io.Exists(rOptions.directory) { 102 return fmt.Errorf("directory %s does not exist", rOptions.directory) 103 } 104 for _, f := range rOptions.replacementFiles { 105 if !io.Exists(f) { 106 return fmt.Errorf("replacement file %s does not exist", f) 107 } 108 } 109 err := replace.Replace( 110 engine, 111 replace.WithDirectory(rOptions.directory), 112 replace.WithReplacementFiles(rOptions.replacementFiles...), 113 replace.WithRootNode(rOptions.node), 114 replace.WithExtension(extensions...), 115 replace.WithInclude(include...), 116 replace.WithExclude(rOptions.exclude...), 117 replace.WithIncludeEnvironmentInReplacements(rOptions.includeEnv), 118 replace.WithIncludeStdinInReplacements(canAccessStdin()), 119 ) 120 if err != nil { 121 return err 122 } 123 return nil 124 }, 125 } 126 127 func init() { 128 rootCmd.AddCommand(replaceCmd) 129 130 replaceCmd.Flags().BoolVar(&rOptions.golang, "golang", false, "use golang template engine (default), automatically sets include and extension config for .tmpl files unless overriden") 131 replaceCmd.Flags().BoolVar(&rOptions.jinja2, "jinja2", false, "use jinja2 template engine, automatically sets include and extension config for .j2 files unless overriden") 132 replaceCmd.MarkFlagsMutuallyExclusive("golang", "jinja2") 133 replaceCmd.Flags().StringVarP(&rOptions.directory, "directory", "d", ".", "pick root directory to search for files to be replaced (defaults to current directory)") 134 replaceCmd.Flags().StringSliceVarP(&rOptions.replacementFiles, "replacements", "r", []string{}, "replacement files (multiple files will be merged)") 135 replaceCmd.Flags().StringVarP(&rOptions.node, "node", "n", "", "only include replacements from inside this node") 136 replaceCmd.Flags().BoolVar(&rOptions.includeEnv, "env", false, "include environment variables as input for the template engine (available inside the 'env' node)") 137 replaceCmd.Flags().StringSliceVarP(&rOptions.extensions, "extension", "e", []string{}, "define the extension/s of the files to replace and then remove in the file name when saving (normally you should include the dot), automatically sets default include config unless overriden (*<ext> and *<ext>.*)") 138 replaceCmd.Flags().StringSliceVar(&rOptions.include, "include", []string{}, "include files that match the filter/s") 139 replaceCmd.Flags().StringSliceVar(&rOptions.exclude, "exclude", []string{}, "exclude files that match the filter/s (takes precedence over include)") 140 onViperInitialize(func() { 141 bindViperC(replaceCmd, "golang", "replace.golang") 142 bindViperC(replaceCmd, "jinja2", "replace.jinja2") 143 bindViperC(replaceCmd, "directory", "replace.directory") 144 bindViperC(replaceCmd, "replacements", "replace.replacements") 145 bindViperC(replaceCmd, "node", "replace.node") 146 bindViperC(replaceCmd, "env", "replace.env") 147 bindViperC(replaceCmd, "extension", "replace.extension") 148 bindViperC(replaceCmd, "include", "replace.include") 149 bindViperC(replaceCmd, "exclude", "replace.exclude") 150 }) 151 }