diff --git a/build/generate.go b/build/generate.go new file mode 100644 index 000000000..c2743851a --- /dev/null +++ b/build/generate.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/mjibson/esc/embed" +) + +func main() { + //go:generate esc -modtime 0 -o js/static.go -pkg js -include helpers\.js -ignore go -prefix js js + conf := &embed.Config{ + ModTime: "0", + OutputFile: "js/static.go", + Package: "js", + Prefix: "js", + Private: true, + Files: []string{`js/helpers.js`}, + } + embed.Run(conf) +} diff --git a/build/validate.go b/build/validate.go new file mode 100644 index 000000000..754252c49 --- /dev/null +++ b/build/validate.go @@ -0,0 +1,84 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "strings" +) + +var status = "" + +func appendErrorStatus(s string) { + if status != "" { + status += ", " + } + status += s +} + +func main() { + if err := checkGoFmt(); err != nil { + fmt.Println(err) + appendErrorStatus("needs gofmt") + } + if err := checkGoGenerate(); err != nil { + fmt.Println(err) + appendErrorStatus("needs go generate") + } +} + +func checkGoFmt() error { + cmd := exec.Command("gofmt", "-s", "-l", ".") + out, err := cmd.CombinedOutput() + if err != nil { + return err + } + if len(out) == 0 { + return nil + } + files := strings.Split(string(out), "\n") + fList := "" + for _, f := range files { + if strings.HasPrefix(f, "vendor") { + continue + } + if fList != "" { + fList += "\n" + } + fList += f + } + if fList == "" { + return nil + } + return fmt.Errorf("ERROR: The following files need to have gofmt run on them:\n%s", fList) +} + +func checkGoGenerate() error { + cmd := exec.Command("go", "generate") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + return err + } + modified, err := getModifiedFiles() + if err != nil { + return err + } + if len(modified) != 0 { + return fmt.Errorf("ERROR: The following files were modified by go generate:\n%s", strings.Join(modified, "\n")) + } + return nil +} + +func getModifiedFiles() ([]string, error) { + cmd := exec.Command("git", strings.Split("diff --name-only", " ")...) + out, err := cmd.CombinedOutput() + if err != nil { + return nil, err + } + if len(out) == 0 { + return nil, nil + } + return strings.Split(string(out), "\n"), nil +} diff --git a/js/js.go b/js/js.go index c74277445..3f9658b66 100644 --- a/js/js.go +++ b/js/js.go @@ -16,7 +16,7 @@ func ExecuteJavascript(script string, devMode bool) (*models.DNSConfig, error) { helperJs := GetHelpers(devMode) // run helper script to prime vm and initialize variables - if _, err := vm.Run(string(helperJs)); err != nil { + if _, err := vm.Run(helperJs); err != nil { return nil, err } diff --git a/js/static.go b/js/static.go index a62e70e0f..336f9ebac 100644 --- a/js/static.go +++ b/js/static.go @@ -129,27 +129,27 @@ func (f *_escFile) Sys() interface{} { return f } -// FS returns a http.Filesystem for the embedded assets. If useLocal is true, +// _escFS returns a http.Filesystem for the embedded assets. If useLocal is true, // the filesystem's contents are instead used. -func FS(useLocal bool) http.FileSystem { +func _escFS(useLocal bool) http.FileSystem { if useLocal { return _escLocal } return _escStatic } -// Dir returns a http.Filesystem for the embedded assets on a given prefix dir. +// _escDir returns a http.Filesystem for the embedded assets on a given prefix dir. // If useLocal is true, the filesystem's contents are instead used. -func Dir(useLocal bool, name string) http.FileSystem { +func _escDir(useLocal bool, name string) http.FileSystem { if useLocal { return _escDirectory{fs: _escLocal, name: name} } return _escDirectory{fs: _escStatic, name: name} } -// FSByte returns the named file from the embedded assets. If useLocal is +// _escFSByte returns the named file from the embedded assets. If useLocal is // true, the filesystem's contents are instead used. -func FSByte(useLocal bool, name string) ([]byte, error) { +func _escFSByte(useLocal bool, name string) ([]byte, error) { if useLocal { f, err := _escLocal.Open(name) if err != nil { @@ -166,24 +166,24 @@ func FSByte(useLocal bool, name string) ([]byte, error) { return f.data, nil } -// FSMustByte is the same as FSByte, but panics if name is not present. -func FSMustByte(useLocal bool, name string) []byte { - b, err := FSByte(useLocal, name) +// _escFSMustByte is the same as _escFSByte, but panics if name is not present. +func _escFSMustByte(useLocal bool, name string) []byte { + b, err := _escFSByte(useLocal, name) if err != nil { panic(err) } return b } -// FSString is the string version of FSByte. -func FSString(useLocal bool, name string) (string, error) { - b, err := FSByte(useLocal, name) +// _escFSString is the string version of _escFSByte. +func _escFSString(useLocal bool, name string) (string, error) { + b, err := _escFSByte(useLocal, name) return string(b), err } -// FSMustString is the string version of FSMustByte. -func FSMustString(useLocal bool, name string) string { - return string(FSMustByte(useLocal, name)) +// _escFSMustString is the string version of _escFSMustByte. +func _escFSMustString(useLocal bool, name string) string { + return string(_escFSMustByte(useLocal, name)) } var _escData = map[string]*_escFile{ @@ -193,7 +193,7 @@ var _escData = map[string]*_escFile{ size: 7196, modtime: 0, compressed: ` -H4sIAAAAAAAA/7RZfU/jzBH/P59iaqkXu/gc4A5aOU+qpgc8OpUEBKFFiiK02JtkOdtr7a6TUhQ+e7Uv +H4sIAAAJbogA/7RZfU/jzBH/P59iaqkXu/gc4A5aOU+qpgc8OpUEBKFFiiK02JtkOdtr7a6TUhQ+e7Uv ttdxcoD0HH+E2Dsvv3nZ2dmJU3AMXDASCaff6awQg4hmcxjASwcAgOEF4YIhxkOYznz1Ls74Q87oisS4 8ZqmiGTqRWdjZMV4jopEDNmCwwCms36nMy+ySBCaAcmIICgh/8Oup5U1NO/T/hME2yjk86avwbWAbCwo Y7y+KVW5GUqxL55z7KdYIM/AIXNw5UuvgiefYDAAZzQc3w0vHa1ooz6l7QwvpDFSXAhKqGIJ1acPUnio diff --git a/main.go b/main.go index b4ea17cf7..8310dec21 100644 --- a/main.go +++ b/main.go @@ -30,7 +30,7 @@ import ( _ "github.com/StackExchange/dnscontrol/providers/route53" ) -//go:generate esc -modtime 0 -o js/static.go -pkg js -include helpers\.js -ignore go -prefix js js +//go:generate go run build/generate.go // One of these config options must be set. var jsFile = flag.String("js", "dnsconfig.js", "Javascript file containing dns config") diff --git a/vendor/github.com/mjibson/esc/LICENSE b/vendor/github.com/mjibson/esc/LICENSE new file mode 100644 index 000000000..8afd34be1 --- /dev/null +++ b/vendor/github.com/mjibson/esc/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Matt Jibson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mjibson/esc/embed/embed.go b/vendor/github.com/mjibson/esc/embed/embed.go new file mode 100644 index 000000000..03db538f1 --- /dev/null +++ b/vendor/github.com/mjibson/esc/embed/embed.go @@ -0,0 +1,405 @@ +//Package embed implements all file embedding logic for github.com/mjibson/esc +package embed + +import ( + "bytes" + "compress/gzip" + "encoding/base64" + "fmt" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "text/template" +) + +//Config contains all information needed to run esc +type Config struct { + //Output file, else stdout + OutputFile string + //Package declaration for generated file + Package string + //Prefix to strip from filenames + Prefix string + //Regexp for files we should ignore (for example \\\\.DS_Store) + Ignore string + //Regexp for files to include. If provided, only files that match will be included + Include string + //Unix timestamp to override as modification time for all files + ModTime string + //If true, do not export autogenerated functions + Private bool + + //List of files or directories to embed + Files []string +} + +var modTime *int64 + +type headerTemplateParams struct { + PackageName string + FunctionPrefix string +} + +type _escFile struct { + data []byte + local string + fileinfo os.FileInfo +} + +func Run(conf *Config) { + var err error + if conf.ModTime != "" { + i, err := strconv.ParseInt(conf.ModTime, 10, 64) + if err != nil { + log.Fatalf("modtime must be an integer: %v", err) + } + modTime = &i + } + var fnames, dirnames []string + content := make(map[string]_escFile) + prefix := filepath.ToSlash(conf.Prefix) + var ignoreRegexp *regexp.Regexp + if conf.Ignore != "" { + ignoreRegexp, err = regexp.Compile(conf.Ignore) + if err != nil { + log.Fatal(err) + } + } + var includeRegexp *regexp.Regexp + if conf.Include != "" { + includeRegexp, err = regexp.Compile(conf.Include) + if err != nil { + log.Fatal(err) + } + } + for _, base := range conf.Files { + files := []string{base} + for len(files) > 0 { + fname := files[0] + files = files[1:] + if ignoreRegexp != nil && ignoreRegexp.MatchString(fname) { + continue + } + f, err := os.Open(fname) + if err != nil { + log.Fatal(err) + } + fi, err := f.Stat() + if err != nil { + log.Fatal(err) + } + if fi.IsDir() { + fis, err := f.Readdir(0) + if err != nil { + log.Fatal(err) + } + for _, fi := range fis { + files = append(files, filepath.Join(fname, fi.Name())) + } + } else if includeRegexp == nil || includeRegexp.MatchString(fname) { + b, err := ioutil.ReadAll(f) + if err != nil { + log.Fatal(err) + } + fpath := filepath.ToSlash(fname) + n := strings.TrimPrefix(fpath, prefix) + n = path.Join("/", n) + content[n] = _escFile{data: b, local: fpath, fileinfo: fi} + fnames = append(fnames, n) + } + f.Close() + } + } + sort.Strings(fnames) + w := os.Stdout + if conf.OutputFile != "" { + if w, err = os.Create(conf.OutputFile); err != nil { + log.Fatal(err) + } + defer w.Close() + } + headerText, err := header(conf.Package, !(conf.Private)) + if nil != err { + log.Fatalf("failed to expand autogenerated code: %s", err) + } + if _, err := w.Write(headerText); err != nil { + log.Fatalf("failed to write output: %s", err) + } + dirs := map[string]bool{"/": true} + for _, fname := range fnames { + f := content[fname] + for b := path.Dir(fname); b != "/"; b = path.Dir(b) { + dirs[b] = true + } + var buf bytes.Buffer + gw := gzip.NewWriter(&buf) + if _, err := gw.Write(f.data); err != nil { + log.Fatal(err) + } + if err := gw.Close(); err != nil { + log.Fatal(err) + } + t := f.fileinfo.ModTime().Unix() + if modTime != nil { + t = *modTime + } + fmt.Fprintf(w, ` + %q: { + local: %q, + size: %v, + modtime: %v, + compressed: %s, + },%s`, fname, f.local, len(f.data), t, segment(&buf), "\n") + } + for d := range dirs { + dirnames = append(dirnames, d) + } + sort.Strings(dirnames) + for _, dir := range dirnames { + local := path.Join(prefix, dir) + if len(local) == 0 { + local = "." + } + fmt.Fprintf(w, ` + %q: { + isDir: true, + local: %q, + },%s`, dir, local, "\n") + } + fmt.Fprint(w, footer) +} + +func segment(s *bytes.Buffer) string { + var b bytes.Buffer + b64 := base64.NewEncoder(base64.StdEncoding, &b) + b64.Write(s.Bytes()) + b64.Close() + res := "`\n" + chunk := make([]byte, 80) + for n, _ := b.Read(chunk); n > 0; n, _ = b.Read(chunk) { + res += string(chunk[0:n]) + "\n" + } + return res + "`" +} + +func header(packageName string, enableExports bool) ([]byte, error) { + functionPrefix := "" + if !enableExports { + functionPrefix = "_esc" + } + headerParams := headerTemplateParams{ + PackageName: packageName, + FunctionPrefix: functionPrefix, + } + tmpl, err := template.New("").Parse(headerTemplate) + if nil != err { + return nil, err + } + var b bytes.Buffer + err = tmpl.Execute(&b, headerParams) + if nil != err { + return nil, err + } + return b.Bytes(), nil +} + +const ( + headerTemplate = `package {{.PackageName}} + +import ( + "bytes" + "compress/gzip" + "encoding/base64" + "io/ioutil" + "net/http" + "os" + "path" + "sync" + "time" +) + +type _escLocalFS struct{} + +var _escLocal _escLocalFS + +type _escStaticFS struct{} + +var _escStatic _escStaticFS + +type _escDirectory struct { + fs http.FileSystem + name string +} + +type _escFile struct { + compressed string + size int64 + modtime int64 + local string + isDir bool + + once sync.Once + data []byte + name string +} + +func (_escLocalFS) Open(name string) (http.File, error) { + f, present := _escData[path.Clean(name)] + if !present { + return nil, os.ErrNotExist + } + return os.Open(f.local) +} + +func (_escStaticFS) prepare(name string) (*_escFile, error) { + f, present := _escData[path.Clean(name)] + if !present { + return nil, os.ErrNotExist + } + var err error + f.once.Do(func() { + f.name = path.Base(name) + if f.size == 0 { + return + } + var gr *gzip.Reader + b64 := base64.NewDecoder(base64.StdEncoding, bytes.NewBufferString(f.compressed)) + gr, err = gzip.NewReader(b64) + if err != nil { + return + } + f.data, err = ioutil.ReadAll(gr) + }) + if err != nil { + return nil, err + } + return f, nil +} + +func (fs _escStaticFS) Open(name string) (http.File, error) { + f, err := fs.prepare(name) + if err != nil { + return nil, err + } + return f.File() +} + +func (dir _escDirectory) Open(name string) (http.File, error) { + return dir.fs.Open(dir.name + name) +} + +func (f *_escFile) File() (http.File, error) { + type httpFile struct { + *bytes.Reader + *_escFile + } + return &httpFile{ + Reader: bytes.NewReader(f.data), + _escFile: f, + }, nil +} + +func (f *_escFile) Close() error { + return nil +} + +func (f *_escFile) Readdir(count int) ([]os.FileInfo, error) { + return nil, nil +} + +func (f *_escFile) Stat() (os.FileInfo, error) { + return f, nil +} + +func (f *_escFile) Name() string { + return f.name +} + +func (f *_escFile) Size() int64 { + return f.size +} + +func (f *_escFile) Mode() os.FileMode { + return 0 +} + +func (f *_escFile) ModTime() time.Time { + return time.Unix(f.modtime, 0) +} + +func (f *_escFile) IsDir() bool { + return f.isDir +} + +func (f *_escFile) Sys() interface{} { + return f +} + +// {{.FunctionPrefix}}FS returns a http.Filesystem for the embedded assets. If useLocal is true, +// the filesystem's contents are instead used. +func {{.FunctionPrefix}}FS(useLocal bool) http.FileSystem { + if useLocal { + return _escLocal + } + return _escStatic +} + +// {{.FunctionPrefix}}Dir returns a http.Filesystem for the embedded assets on a given prefix dir. +// If useLocal is true, the filesystem's contents are instead used. +func {{.FunctionPrefix}}Dir(useLocal bool, name string) http.FileSystem { + if useLocal { + return _escDirectory{fs: _escLocal, name: name} + } + return _escDirectory{fs: _escStatic, name: name} +} + +// {{.FunctionPrefix}}FSByte returns the named file from the embedded assets. If useLocal is +// true, the filesystem's contents are instead used. +func {{.FunctionPrefix}}FSByte(useLocal bool, name string) ([]byte, error) { + if useLocal { + f, err := _escLocal.Open(name) + if err != nil { + return nil, err + } + b, err := ioutil.ReadAll(f) + f.Close() + return b, err + } + f, err := _escStatic.prepare(name) + if err != nil { + return nil, err + } + return f.data, nil +} + +// {{.FunctionPrefix}}FSMustByte is the same as {{.FunctionPrefix}}FSByte, but panics if name is not present. +func {{.FunctionPrefix}}FSMustByte(useLocal bool, name string) []byte { + b, err := {{.FunctionPrefix}}FSByte(useLocal, name) + if err != nil { + panic(err) + } + return b +} + +// {{.FunctionPrefix}}FSString is the string version of {{.FunctionPrefix}}FSByte. +func {{.FunctionPrefix}}FSString(useLocal bool, name string) (string, error) { + b, err := {{.FunctionPrefix}}FSByte(useLocal, name) + return string(b), err +} + +// {{.FunctionPrefix}}FSMustString is the string version of {{.FunctionPrefix}}FSMustByte. +func {{.FunctionPrefix}}FSMustString(useLocal bool, name string) string { + return string({{.FunctionPrefix}}FSMustByte(useLocal, name)) +} + +var _escData = map[string]*_escFile{ +` + footer = `} +` +) diff --git a/vendor/vendor.json b/vendor/vendor.json index 26cd732b1..91352a89d 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -230,6 +230,12 @@ "revision": "db96a2b759cdef4f11a34506a42eb8d1290c598e", "revisionTime": "2016-07-26T03:20:27Z" }, + { + "checksumSHA1": "x4C8qUa39JVIwhD+5nacBaQHcII=", + "path": "github.com/mjibson/esc/embed", + "revision": "703e8aee0a5417c480f8d6a3f072c9eb4f8904ea", + "revisionTime": "2017-01-12T13:50:19Z" + }, { "checksumSHA1": "nS4kKHjMlJpQg3sixqelpCg1jyk=", "path": "github.com/prasmussen/gandi-api/client",