func CopyDirectory(src, dst string) error {
	copier := dirCopier{dst: dst, src: src}
	return filepath.Walk(src, copier.Copy)
}
CopyDirectory copies the directory src to dst so that after
a successful operation the contents of src and dst are equal.
package plan
import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
)
type ActionConditions struct {
	Conditions []Check
}
func (c ActionConditions) Check(path string, setup Setup) error {
	if len(c.Conditions) > 0 {
		setup.Logf("checking action conditions")
	}
	for _, condition := range c.Conditions {
		err := condition.Check(path, setup)
		if err != nil {
			return err
		}
	}
	return nil
}
type OverwriteFileAction struct {
	ActionConditions
	Params struct {
		Create bool `json:"create"`
	}
}
func (a OverwriteFileAction) Run(path string, setup Setup) error {
	setup.Logf("overwriting file %q", path)
	src := setup.PathInRepo(SourceRepo, path)
	dst := setup.PathInRepo(TargetRepo, path)
	dstInfo, err := os.Stat(dst)
	switch {
	case os.IsNotExist(err):
		if !a.Params.Create {
			return fmt.Errorf("path %q does not exist, not creating", dst)
		}
	case err != nil:
		return fmt.Errorf("failed to check path %q: %v", dst, err)
	case dstInfo.IsDir():
		return fmt.Errorf("path %q is a directory", dst)
	}
	srcInfo, err := os.Stat(src)
	if os.IsNotExist(err) {
		return fmt.Errorf("file %q does not exist", src)
	} else if err != nil {
		return fmt.Errorf("failed to check path %q: %v", src, err)
	}
	if srcInfo.IsDir() {
		return fmt.Errorf("path %q is a directory", src)
	}
	srcF, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("failed to open %q: %v", src, err)
	}
	defer srcF.Close()
	dstF, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, srcInfo.Mode())
	if err != nil {
		return fmt.Errorf("failed to open %q: %v", src, err)
	}
	defer dstF.Close()
	_, err = io.Copy(dstF, srcF)
	if err != nil {
		return fmt.Errorf("failed to copy file %q: %v", path, err)
	}
	return nil
}
type OverwriteDirectoryAction struct {
	ActionConditions
	Params struct {
		Create bool `json:"create"`
	}
}
func (a OverwriteDirectoryAction) Run(path string, setup Setup) error {
	setup.Logf("overwriting directory %q", path)
	src := setup.PathInRepo(SourceRepo, path)
	dst := setup.PathInRepo(TargetRepo, path)
	dstInfo, err := os.Stat(dst)
	switch {
	case os.IsNotExist(err):
		if !a.Params.Create {
			return fmt.Errorf("path %q does not exist, not creating", dst)
		}
	case err != nil:
		return fmt.Errorf("failed to check path %q: %v", dst, err)
	default:
		if !dstInfo.IsDir() {
			return fmt.Errorf("path %q is not a directory", dst)
		}
		err = os.RemoveAll(dst)
		if err != nil {
			return fmt.Errorf("failed to remove directory %q: %v", dst, err)
		}
	}
	srcInfo, err := os.Stat(src)
	if os.IsNotExist(err) {
		return fmt.Errorf("directory %q does not exist", src)
	} else if err != nil {
		return fmt.Errorf("failed to check path %q: %v", src, err)
	}
	if !srcInfo.IsDir() {
		return fmt.Errorf("path %q is not a directory", src)
	}
	err = CopyDirectory(src, dst)
	if err != nil {
		return fmt.Errorf("failed to copy path %q: %v", path, err)
	}
	return nil
}