Files
revel-cmd/utils/file.go

286 lines
7.3 KiB
Go

package utils
// DirExists returns true if the given path exists and is a directory.
import (
"os"
"archive/tar"
"strings"
"io"
"path/filepath"
"fmt"
"html/template"
"compress/gzip"
"go/build"
"io/ioutil"
"bytes"
)
func DirExists(filename string) bool {
fileInfo, err := os.Stat(filename)
return err == nil && fileInfo.IsDir()
}
// MustReadLines reads the lines of the given file. Panics in the case of error.
func MustReadLines(filename string) []string {
r, err := ReadLines(filename)
if err != nil {
panic(err)
}
return r
}
// ReadLines reads the lines of the given file. Panics in the case of error.
func ReadLines(filename string) ([]string, error) {
dataBytes, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return strings.Split(string(dataBytes), "\n"), nil
}
func MustCopyFile(destFilename, srcFilename string) {
destFile, err := os.Create(destFilename)
PanicOnError(err, "Failed to create file "+destFilename)
srcFile, err := os.Open(srcFilename)
PanicOnError(err, "Failed to open file "+srcFilename)
_, err = io.Copy(destFile, srcFile)
PanicOnError(err,
fmt.Sprintf("Failed to copy data from %s to %s", srcFile.Name(), destFile.Name()))
err = destFile.Close()
PanicOnError(err, "Failed to close file "+destFile.Name())
err = srcFile.Close()
PanicOnError(err, "Failed to close file "+srcFile.Name())
}
// GenerateTemplate renders the given template to produce source code, which it writes
// to the given file.
func MustGenerateTemplate(filename, templateSource string, args map[string]interface{}) (err error) {
tmpl := template.Must(template.New("").Parse(templateSource))
var b bytes.Buffer
if err = tmpl.Execute(&b, args); err != nil {
Logger.Fatal("ExecuteTemplate: Execute failed", "error", err)
return
}
sourceCode := b.String()
filePath := filepath.Dir(filename)
if !DirExists(filePath) {
err = os.Mkdir(filePath, 0777)
if err != nil && !os.IsExist(err) {
Logger.Fatal("Failed to make directory","dir", filePath, "error", err)
}
}
// Create the file
file, err := os.Create(filename)
if err != nil {
Logger.Fatal("Failed to create file","error", err)
return
}
defer func() {
_ = file.Close()
}()
if _, err = file.WriteString(sourceCode); err != nil {
Logger.Fatal("Failed to write to file: ", "error", err)
}
return
}
// Given the target path and source path and data. A template
func MustRenderTemplate(destPath, srcPath string, data interface{}) {
tmpl, err := template.ParseFiles(srcPath)
PanicOnError(err, "Failed to parse template "+srcPath)
f, err := os.Create(destPath)
PanicOnError(err, "Failed to create "+destPath)
err = tmpl.Execute(f, data)
PanicOnError(err, "Failed to render template "+srcPath)
err = f.Close()
PanicOnError(err, "Failed to close "+f.Name())
}
// Given the target path and source path and data. A template
func MustRenderTemplateToStream(output io.Writer, srcPath []string, data interface{}) {
tmpl, err := template.ParseFiles(srcPath...)
PanicOnError(err, "Failed to parse template "+srcPath[0])
err = tmpl.Execute(output, data)
PanicOnError(err, "Failed to render template "+srcPath[0])
}
func MustChmod(filename string, mode os.FileMode) {
err := os.Chmod(filename, mode)
PanicOnError(err, fmt.Sprintf("Failed to chmod %d %q", mode, filename))
}
// Called if panic
func PanicOnError(err error, msg string) {
if revErr, ok := err.(*Error); (ok && revErr != nil) || (!ok && err != nil) {
Logger.Fatalf("Abort: %s: %s %s\n", msg, revErr, err)
//panic(NewLoggedError(err))
}
}
// copyDir copies a directory tree over to a new directory. Any files ending in
// ".template" are treated as a Go template and rendered using the given data.
// Additionally, the trailing ".template" is stripped from the file name.
// Also, dot files and dot directories are skipped.
func MustCopyDir(destDir, srcDir string, data map[string]interface{}) error {
return fsWalk(srcDir, srcDir, func(srcPath string, info os.FileInfo, err error) error {
// Get the relative path from the source base, and the corresponding path in
// the dest directory.
relSrcPath := strings.TrimLeft(srcPath[len(srcDir):], string(os.PathSeparator))
destPath := filepath.Join(destDir, relSrcPath)
// Skip dot files and dot directories.
if strings.HasPrefix(relSrcPath, ".") {
if info.IsDir() {
return filepath.SkipDir
}
return nil
}
// Create a subdirectory if necessary.
if info.IsDir() {
err := os.MkdirAll(filepath.Join(destDir, relSrcPath), 0777)
if !os.IsExist(err) {
PanicOnError(err, "Failed to create directory")
}
return nil
}
// If this file ends in ".template", render it as a template.
if strings.HasSuffix(relSrcPath, ".template") {
MustRenderTemplate(destPath[:len(destPath)-len(".template")], srcPath, data)
return nil
}
// Else, just copy it over.
MustCopyFile(destPath, srcPath)
return nil
})
}
func Walk(root string, walkFn filepath.WalkFunc) error {
return fsWalk(root,root,walkFn)
}
func fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) error {
fsWalkFunc := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
var name string
name, err = filepath.Rel(fname, path)
if err != nil {
return err
}
path = filepath.Join(linkName, name)
if err == nil && info.Mode()&os.ModeSymlink == os.ModeSymlink {
var symlinkPath string
symlinkPath, err = filepath.EvalSymlinks(path)
if err != nil {
return err
}
// https://github.com/golang/go/blob/master/src/path/filepath/path.go#L392
info, err = os.Lstat(symlinkPath)
if err != nil {
return walkFn(path, info, err)
}
if info.IsDir() {
return fsWalk(symlinkPath, path, walkFn)
}
}
return walkFn(path, info, err)
}
err := filepath.Walk(fname, fsWalkFunc)
return err
}
func MustTarGzDir(destFilename, srcDir string) string {
zipFile, err := os.Create(destFilename)
PanicOnError(err, "Failed to create archive")
defer func() {
_ = zipFile.Close()
}()
gzipWriter := gzip.NewWriter(zipFile)
defer func() {
_ = gzipWriter.Close()
}()
tarWriter := tar.NewWriter(gzipWriter)
defer func() {
_ = tarWriter.Close()
}()
_ = fsWalk(srcDir, srcDir, func(srcPath string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
srcFile, err := os.Open(srcPath)
PanicOnError(err, "Failed to read source file")
defer func() {
_ = srcFile.Close()
}()
err = tarWriter.WriteHeader(&tar.Header{
Name: strings.TrimLeft(srcPath[len(srcDir):], string(os.PathSeparator)),
Size: info.Size(),
Mode: int64(info.Mode()),
ModTime: info.ModTime(),
})
PanicOnError(err, "Failed to write tar entry header")
_, err = io.Copy(tarWriter, srcFile)
PanicOnError(err, "Failed to copy")
return nil
})
return zipFile.Name()
}
func Exists(filename string) bool {
_, err := os.Stat(filename)
return err == nil
}
// empty returns true if the given directory is empty.
// the directory must exist.
func Empty(dirname string) bool {
dir, err := os.Open(dirname)
if err != nil {
Logger.Infof("error opening directory: %s", err)
}
defer func() {
_ = dir.Close()
}()
results, _ := dir.Readdir(1)
return len(results) == 0
}
func ImportPathFromCurrentDir() string {
pwd, _ := os.Getwd()
importPath, _ := filepath.Rel(filepath.Join(build.Default.GOPATH, "src"), pwd)
return filepath.ToSlash(importPath)
}