package template import ( "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" "text/template" "forge.cadoles.com/wpetit/scaffold/internal/fs" "gopkg.in/src-d/go-billy.v4" "github.com/Masterminds/sprig" "github.com/mattn/go-zglob" "github.com/pkg/errors" ) func CopyDir(vfs billy.Filesystem, baseDir string, dst string, opts *Option) error { if opts == nil { opts = &Option{} } baseDir = filepath.Clean(baseDir) dst = filepath.Clean(dst) _, err := os.Stat(dst) if err != nil && !os.IsNotExist(err) { return err } if err := os.MkdirAll(dst, 0755); err != nil { return errors.Wrapf(err, "could not create directory '%s'", dst) } err = fs.Walk(vfs, baseDir, func(srcPath string, info os.FileInfo, err error) error { if err != nil { return err } if srcPath == baseDir { return nil } for _, p := range opts.IgnorePatterns { match, err := zglob.Match(p, strings.TrimPrefix(srcPath, "./")) if err != nil { return errors.Wrap(err, "could not match ignored file") } if match { fmt.Printf("Ignoring %s.\n", srcPath) return nil } } relSrcPath, err := filepath.Rel(baseDir, srcPath) if err != nil { return err } dstPath := filepath.Join(dst, relSrcPath) if info.IsDir() { fmt.Printf("Creating dir '%s'.\n", dstPath) if err := os.MkdirAll(dstPath, 0755); err != nil { return errors.Wrapf(err, "could not create directory '%s'", dstPath) } return nil } err = CopyFile(vfs, srcPath, dstPath, opts) if err != nil { return errors.Wrapf(err, "could not copy file '%s'", srcPath) } return nil }) if err != nil { return errors.Wrapf(err, "could not walk source directory '%s'", baseDir) } return nil } func CopyFile(vfs billy.Filesystem, src, dst string, opts *Option) (err error) { if opts == nil { opts = &Option{} } stat, err := os.Stat(dst) if err != nil && !os.IsNotExist(err) { return err } // If dst exists if stat != nil { strategy := opts.PreferredStrategy if opts.Unnattended && opts.PreferredStrategy == -1 { strategy = defaultStrategy } if opts.PreferredStrategy == -1 { var useAsPreferredStrategy bool strategy, useAsPreferredStrategy, err = askOverwriteStrategy(dst) if err != nil { return errors.Wrap(err, "could not ask for overwrite strategy") } if useAsPreferredStrategy { opts.PreferredStrategy = strategy } } switch strategy { case CopyAsDist: fmt.Printf("Using '%s.dist' as destination file.\n", dst) dst += ".dist" case DoNotCopy: fmt.Printf("Skipping existing file '%s'.\n", dst) return nil default: return nil } } if !strings.HasSuffix(src, opts.TemplateExt) { return copyFile(vfs, src, dst) } in, err := vfs.Open(src) if err != nil { return err } defer in.Close() templateData, err := ioutil.ReadAll(in) if err != nil { return err } tmpl, err := template.New(filepath.Base(src)).Funcs(sprig.TxtFuncMap()).Parse(string(templateData)) if err != nil { return err } dst = strings.TrimSuffix(dst, opts.TemplateExt) fmt.Printf("Templating file from '%s' to '%s'.\n", src, dst) out, err := os.Create(dst) if err != nil { return err } defer func() { if e := out.Close(); e != nil { err = e } }() opts.TemplateData["SourceFile"] = src opts.TemplateData["DestFile"] = dst if err := tmpl.Execute(out, opts.TemplateData); err != nil { return err } return nil } func copyFile(vfs billy.Filesystem, src, dst string) (err error) { fmt.Printf("Copying file '%s' to '%s'.\n", src, dst) in, err := vfs.Open(src) if err != nil { return err } defer in.Close() out, err := os.Create(dst) if err != nil { return err } defer func() { if e := out.Close(); e != nil { err = e } }() _, err = io.Copy(out, in) if err != nil { return err } err = out.Sync() if err != nil { return err } return nil }