func Compile() gonzo.Stage { return func(ctx context.Context, in <-chan gonzo.File, out chan<- gonzo.File) error { for { select { case file, ok := <-in: if !ok { return nil } buff := new(bytes.Buffer) name := strings.TrimSuffix(file.FileInfo().Name(), ".gcss") + ".css" ctx.Infof("Compiling %s to %s", file.FileInfo().Name(), name) n, err := gcss.Compile(buff, file) if err != nil { return err } file = gonzo.NewFile(ioutil.NopCloser(buff), file.FileInfo()) file.FileInfo().SetSize(int64(n)) file.FileInfo().SetName(name) out <- file case <-ctx.Done(): return nil } } } }
// Untar files from input channel and pass the result // to the output channel. func Uncompress() gonzo.Stage { return func(ctx context.Context, in <-chan gonzo.File, out chan<- gonzo.File) error { for { select { case file, ok := <-in: if !ok { return nil } content, err := gzip.NewReader(file) if err != nil { return err } fs := gonzo.NewFile( &doublecloser{content, file.Close}, gonzo.FileInfoFrom(file.FileInfo()), ) out <- fs case <-ctx.Done(): return ctx.Err() } } } }
func Minify() gonzo.Stage { return func(ctx context.Context, in <-chan gonzo.File, out chan<- gonzo.File) error { for { select { case file, ok := <-in: if !ok { return nil } buff := new(bytes.Buffer) name := strings.TrimSuffix(file.FileInfo().Name(), ".css") + ".min.css" ctx.Infof("Compiling %s to %s", file.FileInfo().Name(), name) m := minify.New() m.AddFunc("text/css", css.Minify) err := m.Minify("text/css", buff, file) if err != nil { return err } file = gonzo.NewFile(ioutil.NopCloser(buff), file.FileInfo()) file.FileInfo().SetSize(int64(buff.Len())) file.FileInfo().SetName(name) out <- file case <-ctx.Done(): return ctx.Err() } } } }
func Compile(conf Config) gonzo.Stage { return func(ctx context.Context, in <-chan gonzo.File, out chan<- gonzo.File) error { b := cache{conf, make(map[string]gonzo.File)} for file := range in { path := file.FileInfo().Name() ctx.Infof("Adding %s", path) b.Files[path] = file defer file.Close() //Close files AFTER we have build our package. } buff := new(bytes.Buffer) err := cacheTemplate.Execute(buff, b) if err != nil { ctx.Error(err) return err } fi := gonzo.NewFileInfo() fi.SetName(b.Name) fi.SetSize(int64(buff.Len())) sf := gonzo.NewFile(ioutil.NopCloser(buff), fi) out <- sf return nil } }
func Compile() gonzo.Stage { return func(ctx context.Context, in <-chan gonzo.File, out chan<- gonzo.File) error { for { select { case file, ok := <-in: if !ok { return nil } source, err := ioutil.ReadAll(file) if err != nil { return err } dc := sass.NewDataContext(string(source)) defer dc.Destroy() opts := dc.Context().Options() base := filepath.Join( file.FileInfo().Base(), filepath.Dir(file.FileInfo().Name()), ) opts.SetIncludePath(base) compiler := dc.Compiler() defer compiler.Destroy() err = compiler.Parse() if err != nil { ctx.Error(err) //ctx.Error(dc.Context().Error().Error()) return errors.New(dc.Context().Error().Error()) } err = compiler.Execute() if err != nil { ctx.Error(err) return errors.New(dc.Context().Error().Error()) } output := dc.Context().OutputString() buff := bytes.NewBufferString(output) name := strings.TrimSuffix(file.FileInfo().Name(), ".scss") + ".css" ctx.Infof("Compiling %s to %s", file.FileInfo().Name(), name) file = gonzo.NewFile(ioutil.NopCloser(buff), file.FileInfo()) file.FileInfo().SetSize(int64(buff.Len())) file.FileInfo().SetName(name) out <- file case <-ctx.Done(): return nil } } } }
// bin is the binary name, it will be passed to os/exec.Command, so the same // path rules applies. // the args are the argumetns passed to the program. func Run(bin string, args ...string) gonzo.Stage { return func(ctx context.Context, in <-chan gonzo.File, out chan<- gonzo.File) error { for { select { case file, ok := <-in: if !ok { return nil } cmd := exec.Command(bin, args...) cmd.Stderr = os.Stderr //TODO: io.Writer logger. cmd.Stdin = file ctx = context.WithValue(ctx, "cmd", bin) ctx.Infof("Passing %s", file.FileInfo().Name()) output, err := cmd.Output() if err != nil { return err } content := ioutil.NopCloser(bytes.NewReader(output)) out <- gonzo.NewFile(content, file.FileInfo()) case <-ctx.Done(): return ctx.Err() } } } }
// Untar files from input channel and pass the result to the output channel. func Untar(opt Options) gonzo.Stage { return func(ctx context.Context, in <-chan gonzo.File, out chan<- gonzo.File) error { //Check patterns. pluck := len(opt.Pluck) > 0 if pluck { err := match.Good(opt.Pluck...) if err != nil { return err } } for { select { case file, ok := <-in: if !ok { return nil } context.WithValue(ctx, "archive", file.FileInfo().Name()).Debug("Untaring") tr := tar.NewReader(file) defer file.Close() // Iterate through the files in the archive. for { hdr, err := tr.Next() if err == io.EOF { // end of tar archive break } if err != nil { return err } name := strip(opt.StripComponenets, hdr.Name) if pluck && !match.Any(name, opt.Pluck...) { continue } context.WithValue(ctx, "file", name).Debug("Untaring") content := new(bytes.Buffer) n, err := content.ReadFrom(tr) if err != nil { return err } fs := gonzo.NewFile(ioutil.NopCloser(content), gonzo.FileInfoFrom(hdr.FileInfo())) fs.FileInfo().SetName(name) fs.FileInfo().SetSize(int64(n)) out <- fs } case <-ctx.Done(): return ctx.Err() } } } }
// A build stage creates a new Package and adds all the files coming through the channel to // the package and returns the result of build as a File on the output channel. func Build(config Config) gonzo.Stage { return func(ctx context.Context, files <-chan gonzo.File, out chan<- gonzo.File) error { ctx, cancel := context.WithCancel(ctx) res := resources.New() res.Config = resources.Config(config) var err error buff := &bytes.Buffer{} for { select { case file, ok := <-files: if !ok { goto BUILD } if file.FileInfo().IsDir() { continue } path, _ := filepath.Rel(file.FileInfo().Base(), file.FileInfo().Name()) res.Add(filepath.ToSlash(path), file) ctx.Infof("Adding %s", path) defer func(path string) { ctx.Debug("Closing %s", path) file.Close() //Close files AFTER we have build our package. }(path) case <-ctx.Done(): err = ctx.Err() goto BUILD } } BUILD: if err != nil { return err } ctx.Debug("Runnig build...") err = res.Build(buff) if err != nil { cancel() return err } path := fmt.Sprintf(FilenameFormat, strings.ToLower(config.Var)) sf := gonzo.NewFile(ioutil.NopCloser(buff), gonzo.NewFileInfo()) sf.FileInfo().SetName(path) sf.FileInfo().SetSize(int64(buff.Len())) out <- sf return nil } }
func Put(c Config) gonzo.Stage { return func(ctx context.Context, files <-chan gonzo.File, out chan<- gonzo.File) error { err := checkconfig(c) if err != nil { return err } auth := aws.Auth{ AccessKey: c.AccessKey, SecretKey: c.SecretKey, } con := s3.New(auth, aws.Region(c.Region)) bucket := con.Bucket(c.Name) for { select { case file, ok := <-files: if !ok { return nil } if file.FileInfo().IsDir() { continue } content, err := ioutil.ReadAll(file) if err != nil { return err } name := file.FileInfo().Name() contentType := mime.TypeByExtension(filepath.Ext(name)) if contentType == "" { contentType = http.DetectContentType(content) } ctx = context.WithValue(ctx, "Content-Type", contentType) ctx.Infof("Uploading %s", name) err = bucket.Put(name, content, contentType, s3.ACL(c.Perm)) if err != nil { return err } out <- gonzo.NewFile(ioutil.NopCloser(bytes.NewReader(content)), file.FileInfo()) case <-ctx.Done(): return ctx.Err() } } } }
// Concatenates all the files from the input channel // and passes them to output channel with the given name. func Concat(ctx context.Context, name string) gonzo.Stage { return func(ctx context.Context, files <-chan gonzo.File, out chan<- gonzo.File) error { var ( size int64 bigfile = new(bytes.Buffer) ) err := func() error { for { select { case f, ok := <-files: if !ok { return nil } ctx.Infof( "Adding %s to %s", filepath.Join(f.FileInfo().Base(), f.FileInfo().Name()), name, ) n, err := bigfile.ReadFrom(f) if err != nil { return err } bigfile.WriteRune('\n') size += n + 1 f.Close() case <-ctx.Done(): return ctx.Err() } } }() if err != nil { return err } file := gonzo.NewFile(ioutil.NopCloser(bigfile), gonzo.NewFileInfo()) file.FileInfo().SetSize(size) file.FileInfo().SetName(name) out <- file return nil } }
// A simple helper function that opens the file from the given path and // returns a pointer to a gonzo.File or an error. func Read(path string) (gonzo.File, error) { Stat, err := os.Stat(path) if err != nil { return nil, err } if Stat.IsDir() { return nil, ErrIsDir } f, err := os.Open(path) if err != nil { return nil, err } return gonzo.NewFile(f, gonzo.FileInfoFrom(Stat)), nil }
// Unzip the zip files from input channel and pass the result // to the output channel. func Unzip() gonzo.Stage { return func(ctx context.Context, in <-chan gonzo.File, out chan<- gonzo.File) error { for { select { case file, ok := <-in: if !ok { return nil } raw, err := ioutil.ReadAll(file) if err != nil { return err } file.Close() r, err := zip.NewReader(bytes.NewReader(raw), int64(len(raw))) if err != nil { return err } //counter := c.Counter("unzipping", len(r.File)) // Iterate through the files in the archive, for _, f := range r.File { ctx = context.WithValue(ctx, "file", f.Name) ctx.Info("Unziping") //counter.Set(i+1, f.Name) content, err := f.Open() if err != nil { } fs := gonzo.NewFile(content, gonzo.FileInfoFrom(f.FileInfo())) fs.FileInfo().SetName(f.Name) out <- fs } case <-ctx.Done(): return ctx.Err() } } } }
// Dest writes the files from the input channel to the dst folder and closes the files. // It never returns Files. func Dest(dst string) gonzo.Stage { return func(ctx context.Context, files <-chan gonzo.File, out chan<- gonzo.File) error { for { select { case file, ok := <-files: if !ok { return nil } name := file.FileInfo().Name() path := filepath.Join(dst, filepath.Dir(name)) err := os.MkdirAll(path, 0700) if err != nil { return err } if file.FileInfo().IsDir() { out <- file continue } content, err := ioutil.ReadAll(file) if err != nil { file.Close() return err } ctx = context.WithValue(ctx, "path", path) ctx.Infof("Writing %s", name) err = writeFile(filepath.Join(dst, name), content) if err != nil { return err } out <- gonzo.NewFile(ioutil.NopCloser(bytes.NewReader(content)), file.FileInfo()) case <-ctx.Done(): return ctx.Err() } } } }
func get(ctx context.Context, client *http.Client, url string) (gonzo.File, error) { resp, err := ctxhttp.Get(ctx, client, url) if err != nil { return nil, err } if resp.StatusCode < 200 || resp.StatusCode > 399 { return nil, fmt.Errorf("%s (%s)", resp.Status, url) } _, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition")) name, ok := params["filename"] if !ok || err != nil { name = path.Base(url) } file := gonzo.NewFile(resp.Body, gonzo.NewFileInfo()) file.FileInfo().SetName(name) file.FileInfo().SetSize(resp.ContentLength) return file, nil }
func Minify(opt Options) gonzo.Stage { return func(ctx context.Context, in <-chan gonzo.File, out chan<- gonzo.File) error { for { select { case file, ok := <-in: if !ok { return nil } buff := new(bytes.Buffer) ctx.Infof("Minfiying %s", file.FileInfo().Name()) m := minify.New() m.Add("text/html", &html.Minifier{ KeepDefaultAttrVals: opt.KeepDefaultAttrVals, KeepWhitespace: opt.KeepWhitespace, }) m.AddFunc("text/css", css.Minify) m.AddFunc("text/javascript", js.Minify) m.AddFunc("image/svg+xml", svg.Minify) err := m.Minify("text/html", buff, file) if err != nil { return err } file = gonzo.NewFile(ioutil.NopCloser(buff), file.FileInfo()) file.FileInfo().SetSize(int64(buff.Len())) out <- file case <-ctx.Done(): return ctx.Err() } } } }
func Compile(options Options, data interface{}) gonzo.Stage { return func(ctx context.Context, in <-chan gonzo.File, out chan<- gonzo.File) error { options := ace.Options(options) fs := []*ace.File{} for { select { case file, ok := <-in: if !ok { return nil } buf := new(bytes.Buffer) _, err := buf.ReadFrom(file) file.Close() if err != nil { ctx.Error(err) continue } s, err := file.Stat() if err != nil { return err } name := s.Name() //Probably filepath.Rel(file.Dir, file.Path) ?? f := ace.NewFile(name, buf.Bytes()) source := ace.NewSource( ace.NewFile("", nil), f, fs, ) fs = append(fs, f) r, err := ace.ParseSource(source, &options) if err != nil { ctx.Error(err) continue } t, err := ace.CompileResultWithTemplate(html.New(name), r, &options) if err != nil { ctx.Error(err) continue } if filepath.Base(file.FileInfo().Name())[0] != '_' { buf := new(bytes.Buffer) err = t.Execute(buf, data) if err != nil { ctx.Error(err) continue } file = gonzo.NewFile(ioutil.NopCloser(buf), file.FileInfo()) file.FileInfo().SetSize(int64(buf.Len())) name = strings.TrimSuffix(file.FileInfo().Name(), ".ace") + ".html" file.FileInfo().SetName(name) } out <- file case <-ctx.Done(): return ctx.Err() } return nil } } }