scaffold/internal/template/copy.go
William Petit 0fa845b3ff Add basic interactive overwrite strategy
Available options:

- Do not copy
- Overwrite dest
- Copy source as dest.dist
2020-05-23 11:57:49 +02:00

208 lines
3.8 KiB
Go

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
}