Use vfsgen instead of go-bindata (#7080)

* use vfsgen instead of go-bindata

* fix templates

* fix fmt

* vendor vsfgen
release/v1.9
Lunny Xiao 5 years ago committed by Lauris BH
parent 8eba27c792
commit 83b90e4199

@ -97,10 +97,7 @@ vet:
.PHONY: generate
generate:
@hash go-bindata > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/jteeuwen/go-bindata/go-bindata; \
fi
$(GO) generate $(PACKAGES)
GO111MODULE=on $(GO) generate $(PACKAGES)
.PHONY: generate-swagger
generate-swagger:

@ -30,7 +30,6 @@ require (
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f
github.com/dgrijalva/jwt-go v0.0.0-20161101193935-9ed569b5d1ac
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 // indirect
github.com/elazarl/go-bindata-assetfs v0.0.0-20151224045452-57eb5e1fc594 // indirect
github.com/emirpasic/gods v1.12.0
github.com/etcd-io/bbolt v1.3.2 // indirect
github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a
@ -44,7 +43,6 @@ require (
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd // indirect
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e // indirect
github.com/go-macaron/bindata v0.0.0-20161222093048-85786f57eee3
github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443
github.com/go-macaron/cache v0.0.0-20151013081102-561735312776
github.com/go-macaron/captcha v0.0.0-20151123225153-8aa5919789ab
@ -103,7 +101,9 @@ require (
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
github.com/satori/go.uuid v1.2.0
github.com/sergi/go-diff v1.0.0
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect
github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc // indirect
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d // indirect
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff // indirect
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 // indirect

@ -62,8 +62,6 @@ github.com/dgrijalva/jwt-go v0.0.0-20161101193935-9ed569b5d1ac h1:xrQJVwQCGqDvOO
github.com/dgrijalva/jwt-go v0.0.0-20161101193935-9ed569b5d1ac/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/go-bindata-assetfs v0.0.0-20151224045452-57eb5e1fc594 h1:McZ/pt/pP/XAbLMDQGzm/iQUwW6OXmKVbFtmH9klWmc=
github.com/elazarl/go-bindata-assetfs v0.0.0-20151224045452-57eb5e1fc594/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
@ -97,8 +95,6 @@ github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd h1:r04M
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e h1:SiEs4J3BKVIeaWrH3tKaz3QLZhJ68iJ/A4xrzIoE5+Y=
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/go-macaron/bindata v0.0.0-20161222093048-85786f57eee3 h1:n0H90987ZwasTc9r/RnuaU1G5ePfzLG6Bkc1cY3KqnY=
github.com/go-macaron/bindata v0.0.0-20161222093048-85786f57eee3/go.mod h1:NkmXhFuAlCOqgV5EWhU1DKLrgztCEIVXGmr2DZv/+sk=
github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443 h1:i801KPR7j76uRMLLlGVyb0hiYbgX1FM5+ur81TJWzIw=
github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443/go.mod h1:u+H6rwW+HQwUL+w5uaEJSpIlVZDye1o9MB4Su0JfRfM=
github.com/go-macaron/cache v0.0.0-20151013081102-561735312776 h1:UYIHS1r0WotqB5cIa0PAiV0m6GzD9rDBcn4alp5JgCw=
@ -282,8 +278,12 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b h1:4kg1wyftSKxLtnPAvcRWakIPpokB9w780/KwrNLnfPA=
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc h1:3wIrJvFb3Pf6B/2mDBnN1G5IfUVev4X5apadQlWOczE=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0=
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d h1:qQWKKOvHN7Q9c6GdmUteCef2F9ubxMpxY1IKwpIKz68=
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
@ -348,6 +348,7 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqY
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635 h1:2eB4G6bDQDeP69ZXbOKC00S2Kf6TIiRS+DzfKsKeQU0=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0 h1:S0iUepdCWODXRvtE+gcRDd15L+k+k1AiHlMiMjefH24=

@ -21,6 +21,9 @@ import (
_ "code.gitea.io/gitea/modules/markup/markdown"
_ "code.gitea.io/gitea/modules/markup/orgmode"
// for embed
_ "github.com/shurcooL/vfsgen"
"github.com/urfave/cli"
)

@ -0,0 +1,23 @@
// +build ignore
package main
import (
"log"
"net/http"
"github.com/shurcooL/vfsgen"
)
func main() {
var fsTemplates http.FileSystem = http.Dir("../../options")
err := vfsgen.Generate(fsTemplates, vfsgen.Options{
PackageName: "options",
BuildTags: "bindata",
VariableName: "Assets",
Filename: "bindata.go",
})
if err != nil {
log.Fatal("%v", err)
}
}

@ -4,10 +4,8 @@
package options
//go:generate go-bindata -tags "bindata" -ignore "TRANSLATORS" -pkg "options" -o "bindata.go" ../../options/...
//go:generate go run -mod=vendor main.go
//go:generate go fmt bindata.go
//go:generate sed -i.bak s/..\/..\/options\/// bindata.go
//go:generate rm -f bindata.go.bak
type directorySet map[string][]string

@ -41,7 +41,7 @@ func Dir(name string) ([]string, error) {
result = append(result, files...)
}
files, err := AssetDir(path.Join("..", "..", "options", name))
files, err := AssetDir(name)
if err != nil {
return []string{}, fmt.Errorf("Failed to read embedded directory. %v", err)
@ -52,6 +52,24 @@ func Dir(name string) ([]string, error) {
return directories.AddAndGet(name, result), nil
}
func AssetDir(dirName string) ([]string, error) {
d, err := Assets.Open(dirName)
if err != nil {
return nil, err
}
defer d.Close()
files, err := d.Readdir(-1)
if err != nil {
return nil, err
}
var results = make([]string, 0, len(files))
for _, file := range files {
results = append(results, file.Name())
}
return results, nil
}
// Locale reads the content of a specific locale from bindata or custom path.
func Locale(name string) ([]byte, error) {
return fileFromDir(path.Join("locale", name))
@ -85,5 +103,11 @@ func fileFromDir(name string) ([]byte, error) {
return ioutil.ReadFile(customPath)
}
return Asset(name)
f, err := Assets.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
return ioutil.ReadAll(f)
}

@ -0,0 +1,23 @@
// +build ignore
package main
import (
"log"
"net/http"
"github.com/shurcooL/vfsgen"
)
func main() {
var fsPublic http.FileSystem = http.Dir("../../public")
err := vfsgen.Generate(fsPublic, vfsgen.Options{
PackageName: "public",
BuildTags: "bindata",
VariableName: "Assets",
Filename: "bindata.go",
})
if err != nil {
log.Fatal("%v", err)
}
}

@ -17,10 +17,8 @@ import (
"gopkg.in/macaron.v1"
)
//go:generate go-bindata -tags "bindata" -ignore "\\.go|\\.less" -pkg "public" -o "bindata.go" ../../public/...
//go:generate go run -mod=vendor main.go
//go:generate go fmt bindata.go
//go:generate sed -i.bak s/..\/..\/public\/// bindata.go
//go:generate rm -f bindata.go.bak
// Options represents the available options to configure the macaron handler.
type Options struct {

@ -7,19 +7,12 @@
package public
import (
"github.com/go-macaron/bindata"
"gopkg.in/macaron.v1"
)
// Static implements the macaron static handler for serving assets.
func Static(opts *Options) macaron.Handler {
opts.FileSystem = bindata.Static(bindata.Options{
Asset: Asset,
AssetDir: AssetDir,
AssetInfo: AssetInfo,
AssetNames: AssetNames,
Prefix: "",
})
opts.FileSystem = Assets
// we don't need to pass the directory, because the directory var is only
// used when in the options there is no FileSystem.
return opts.staticHandler("")

@ -0,0 +1,23 @@
// +build ignore
package main
import (
"log"
"net/http"
"github.com/shurcooL/vfsgen"
)
func main() {
var fsTemplates http.FileSystem = http.Dir("../../templates")
err := vfsgen.Generate(fsTemplates, vfsgen.Options{
PackageName: "templates",
BuildTags: "bindata",
VariableName: "Assets",
Filename: "bindata.go",
})
if err != nil {
log.Fatal("%v", err)
}
}

@ -203,3 +203,21 @@ func Mailer() *template.Template {
return templates
}
func Asset(name string) ([]byte, error) {
f, err := Assets.Open("/" + name)
if err != nil {
return nil, err
}
defer f.Close()
return ioutil.ReadAll(f)
}
func AssetNames() []string {
realFS := Assets.(vfsgen۰FS)
var results = make([]string, 0, len(realFS))
for k := range realFS {
results = append(results, k[1:])
}
return results
}

@ -4,7 +4,5 @@
package templates
//go:generate go-bindata -tags "bindata" -ignore "\\.go" -pkg "templates" -o "bindata.go" ../../templates/...
//go:generate go run -mod=vendor main.go
//go:generate go fmt bindata.go
//go:generate sed -i.bak s/..\/..\/templates\/// bindata.go
//go:generate rm -f bindata.go.bak

@ -65,7 +65,6 @@ parts:
build: |
export PATH=$SNAPCRAFT_PART_INSTALL/../go/bin/:$SNAPCRAFT_PART_INSTALL/../../go/install/bin:$PATH
export GOPATH=$SNAPCRAFT_PART_INSTALL/../go
go get -u github.com/jteeuwen/go-bindata/...
cd $GOPATH/src/code.gitea.io/gitea
TAGS="bindata sqlite sqlite_unlock_notify pam cert" make generate build
install: |

@ -1,23 +0,0 @@
Copyright (c) 2014, Elazar Leibovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -1,46 +0,0 @@
# go-bindata-assetfs
Serve embedded files from [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) with `net/http`.
[GoDoc](http://godoc.org/github.com/elazarl/go-bindata-assetfs)
### Installation
Install with
$ go get github.com/jteeuwen/go-bindata/...
$ go get github.com/elazarl/go-bindata-assetfs/...
### Creating embedded data
Usage is identical to [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) usage,
instead of running `go-bindata` run `go-bindata-assetfs`.
The tool will create a `bindata_assetfs.go` file, which contains the embedded data.
A typical use case is
$ go-bindata-assetfs data/...
### Using assetFS in your code
The generated file provides an `assetFS()` function that returns a `http.Filesystem`
wrapping the embedded files. What you usually want to do is:
http.Handle("/", http.FileServer(assetFS()))
This would run an HTTP server serving the embedded files.
## Without running binary tool
You can always just run the `go-bindata` tool, and then
use
import "github.com/elazarl/go-bindata-assetfs"
...
http.Handle("/",
http.FileServer(
&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo, Prefix: "data"}))
to serve files embedded from the `data` directory.

@ -1,158 +0,0 @@
package assetfs
import (
"bytes"
"errors"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"time"
)
var (
defaultFileTimestamp = time.Now()
)
// FakeFile implements os.FileInfo interface for a given path and size
type FakeFile struct {
// Path is the path of this file
Path string
// Dir marks of the path is a directory
Dir bool
// Len is the length of the fake file, zero if it is a directory
Len int64
// Timestamp is the ModTime of this file
Timestamp time.Time
}
func (f *FakeFile) Name() string {
_, name := filepath.Split(f.Path)
return name
}
func (f *FakeFile) Mode() os.FileMode {
mode := os.FileMode(0644)
if f.Dir {
return mode | os.ModeDir
}
return mode
}
func (f *FakeFile) ModTime() time.Time {
return f.Timestamp
}
func (f *FakeFile) Size() int64 {
return f.Len
}
func (f *FakeFile) IsDir() bool {
return f.Mode().IsDir()
}
func (f *FakeFile) Sys() interface{} {
return nil
}
// AssetFile implements http.File interface for a no-directory file with content
type AssetFile struct {
*bytes.Reader
io.Closer
FakeFile
}
func NewAssetFile(name string, content []byte, timestamp time.Time) *AssetFile {
if timestamp.IsZero() {
timestamp = defaultFileTimestamp
}
return &AssetFile{
bytes.NewReader(content),
ioutil.NopCloser(nil),
FakeFile{name, false, int64(len(content)), timestamp}}
}
func (f *AssetFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, errors.New("not a directory")
}
func (f *AssetFile) Size() int64 {
return f.FakeFile.Size()
}
func (f *AssetFile) Stat() (os.FileInfo, error) {
return f, nil
}
// AssetDirectory implements http.File interface for a directory
type AssetDirectory struct {
AssetFile
ChildrenRead int
Children []os.FileInfo
}
func NewAssetDirectory(name string, children []string, fs *AssetFS) *AssetDirectory {
fileinfos := make([]os.FileInfo, 0, len(children))
for _, child := range children {
_, err := fs.AssetDir(filepath.Join(name, child))
fileinfos = append(fileinfos, &FakeFile{child, err == nil, 0, time.Time{}})
}
return &AssetDirectory{
AssetFile{
bytes.NewReader(nil),
ioutil.NopCloser(nil),
FakeFile{name, true, 0, time.Time{}},
},
0,
fileinfos}
}
func (f *AssetDirectory) Readdir(count int) ([]os.FileInfo, error) {
if count <= 0 {
return f.Children, nil
}
if f.ChildrenRead+count > len(f.Children) {
count = len(f.Children) - f.ChildrenRead
}
rv := f.Children[f.ChildrenRead : f.ChildrenRead+count]
f.ChildrenRead += count
return rv, nil
}
func (f *AssetDirectory) Stat() (os.FileInfo, error) {
return f, nil
}
// AssetFS implements http.FileSystem, allowing
// embedded files to be served from net/http package.
type AssetFS struct {
// Asset should return content of file in path if exists
Asset func(path string) ([]byte, error)
// AssetDir should return list of files in the path
AssetDir func(path string) ([]string, error)
// AssetInfo should return the info of file in path if exists
AssetInfo func(path string) (os.FileInfo, error)
// Prefix would be prepended to http requests
Prefix string
}
func (fs *AssetFS) Open(name string) (http.File, error) {
name = path.Join(fs.Prefix, name)
if len(name) > 0 && name[0] == '/' {
name = name[1:]
}
if b, err := fs.Asset(name); err == nil {
timestamp := defaultFileTimestamp
if info, err := fs.AssetInfo(name); err == nil {
timestamp = info.ModTime()
}
return NewAssetFile(name, b, timestamp), nil
}
if children, err := fs.AssetDir(name); err == nil {
return NewAssetDirectory(name, children, fs), nil
} else {
return nil, err
}
}

@ -1,13 +0,0 @@
// assetfs allows packages to serve static content embedded
// with the go-bindata tool with the standard net/http package.
//
// See https://github.com/jteeuwen/go-bindata for more information
// about embedding binary data with go-bindata.
//
// Usage example, after running
// $ go-bindata data/...
// use:
// http.Handle("/",
// http.FileServer(
// &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "data"}))
package assetfs

@ -1,14 +0,0 @@
sudo: false
language: go
go:
- 1.3
- 1.4
- 1.5
- tip
script: go test -v -cover -race
notifications:
email:
- u@gogs.io

@ -1,191 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -1,16 +0,0 @@
# bindata [![Build Status](https://travis-ci.org/go-macaron/bindata.svg?branch=master)](https://travis-ci.org/go-macaron/bindata) [![](http://gocover.io/_badge/github.com/go-macaron/bindata)](http://gocover.io/github.com/go-macaron/bindata)
Package bindata is a helper module that allows to use in-memory static and template files for Macaron via [go-bindata](https://github.com/jteeuwen/go-bindata).
### Installation
go get github.com/go-macaron/bindata
## Getting Help
- [API Reference](https://gowalker.org/github.com/go-macaron/bindata)
- [Documentation](http://go-macaron.com/docs/middlewares/bindata)
## License
This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text.

@ -1,117 +0,0 @@
// Copyright 2014 Dustin Webber
// Copyright 2015 The Macaron Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package bindata is a helper module that allows to use in-memory static and template files for Macaron.
package bindata
import (
"bytes"
"fmt"
"io"
"os"
"github.com/elazarl/go-bindata-assetfs"
"gopkg.in/macaron.v1"
)
const _VERSION = "0.1.1"
func Version() string {
return _VERSION
}
type (
templateFileSystem struct {
files []macaron.TemplateFile
}
templateFile struct {
name string
data []byte
ext string
}
Options struct {
// Asset should return content of file in path if exists
Asset func(path string) ([]byte, error)
// AssetDir should return list of files in the path
AssetDir func(path string) ([]string, error)
// AssetInfo should return the info of file in path if exists
AssetInfo func(path string) (os.FileInfo, error)
// AssetNames should return list of all asset names
AssetNames func() []string
// Prefix would be prepended to http requests
Prefix string
}
)
func Static(opt Options) *assetfs.AssetFS {
fs := &assetfs.AssetFS{
Asset: opt.Asset,
AssetDir: opt.AssetDir,
AssetInfo: opt.AssetInfo,
Prefix: opt.Prefix,
}
return fs
}
func (templates templateFileSystem) ListFiles() []macaron.TemplateFile {
return templates.files
}
func (templates templateFileSystem) Get(name string) (io.Reader, error) {
for i := range templates.files {
if templates.files[i].Name()+templates.files[i].Ext() == name {
return bytes.NewReader(templates.files[i].Data()), nil
}
}
return nil, fmt.Errorf("file '%s' not found", name)
}
func (f *templateFile) Name() string {
return f.name
}
func (f *templateFile) Data() []byte {
return f.data
}
func (f *templateFile) Ext() string {
return f.ext
}
func Templates(opt Options) templateFileSystem {
fs := templateFileSystem{}
fs.files = make([]macaron.TemplateFile, 0, 10)
list := opt.AssetNames()
for _, key := range list {
ext := macaron.GetExt(key)
data, err := opt.Asset(key)
if err != nil {
continue
}
name := (key[0 : len(key)-len(ext)])
fs.files = append(fs.files, &templateFile{name, data, ext})
}
return fs
}

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2015 Dmitri Shuralyov
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.

@ -0,0 +1,21 @@
package vfsutil
import (
"net/http"
"os"
)
// File implements http.FileSystem using the native file system restricted to a
// specific file served at root.
//
// While the FileSystem.Open method takes '/'-separated paths, a File's string
// value is a filename on the native file system, not a URL, so it is separated
// by filepath.Separator, which isn't necessarily '/'.
type File string
func (f File) Open(name string) (http.File, error) {
if name != "/" {
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
}
return os.Open(string(f))
}

@ -0,0 +1,39 @@
// Package vfsutil implements some I/O utility functions for http.FileSystem.
package vfsutil
import (
"io/ioutil"
"net/http"
"os"
)
// ReadDir reads the contents of the directory associated with file and
// returns a slice of FileInfo values in directory order.
func ReadDir(fs http.FileSystem, name string) ([]os.FileInfo, error) {
f, err := fs.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
return f.Readdir(0)
}
// Stat returns the FileInfo structure describing file.
func Stat(fs http.FileSystem, name string) (os.FileInfo, error) {
f, err := fs.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
return f.Stat()
}
// ReadFile reads the file named by path from fs and returns the contents.
func ReadFile(fs http.FileSystem, path string) ([]byte, error) {
rc, err := fs.Open(path)
if err != nil {
return nil, err
}
defer rc.Close()
return ioutil.ReadAll(rc)
}

@ -0,0 +1,146 @@
package vfsutil
import (
"io"
"net/http"
"os"
pathpkg "path"
"path/filepath"
"sort"
)
// Walk walks the filesystem rooted at root, calling walkFn for each file or
// directory in the filesystem, including root. All errors that arise visiting files
// and directories are filtered by walkFn. The files are walked in lexical
// order.
func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error {
info, err := Stat(fs, root)
if err != nil {
return walkFn(root, nil, err)
}
return walk(fs, root, info, walkFn)
}
// readDirNames reads the directory named by dirname and returns
// a sorted list of directory entries.
func readDirNames(fs http.FileSystem, dirname string) ([]string, error) {
fis, err := ReadDir(fs, dirname)
if err != nil {
return nil, err
}
names := make([]string, len(fis))
for i := range fis {
names[i] = fis[i].Name()
}
sort.Strings(names)
return names, nil
}
// walk recursively descends path, calling walkFn.
func walk(fs http.FileSystem, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
err := walkFn(path, info, nil)
if err != nil {
if info.IsDir() && err == filepath.SkipDir {
return nil
}
return err
}
if !info.IsDir() {
return nil
}
names, err := readDirNames(fs, path)
if err != nil {
return walkFn(path, info, err)
}
for _, name := range names {
filename := pathpkg.Join(path, name)
fileInfo, err := Stat(fs, filename)
if err != nil {
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
return err
}
} else {
err = walk(fs, filename, fileInfo, walkFn)
if err != nil {
if !fileInfo.IsDir() || err != filepath.SkipDir {
return err
}
}
}
}
return nil
}
// WalkFilesFunc is the type of the function called for each file or directory visited by WalkFiles.
// It's like filepath.WalkFunc, except it provides an additional ReadSeeker parameter for file being visited.
type WalkFilesFunc func(path string, info os.FileInfo, rs io.ReadSeeker, err error) error
// WalkFiles walks the filesystem rooted at root, calling walkFn for each file or
// directory in the filesystem, including root. In addition to FileInfo, it passes an
// ReadSeeker to walkFn for each file it visits.
func WalkFiles(fs http.FileSystem, root string, walkFn WalkFilesFunc) error {
file, info, err := openStat(fs, root)
if err != nil {
return walkFn(root, nil, nil, err)
}
return walkFiles(fs, root, info, file, walkFn)
}
// walkFiles recursively descends path, calling walkFn.
// It closes the input file after it's done with it, so the caller shouldn't.
func walkFiles(fs http.FileSystem, path string, info os.FileInfo, file http.File, walkFn WalkFilesFunc) error {
err := walkFn(path, info, file, nil)
file.Close()
if err != nil {
if info.IsDir() && err == filepath.SkipDir {
return nil
}
return err
}
if !info.IsDir() {
return nil
}
names, err := readDirNames(fs, path)
if err != nil {
return walkFn(path, info, nil, err)
}
for _, name := range names {
filename := pathpkg.Join(path, name)
file, fileInfo, err := openStat(fs, filename)
if err != nil {
if err := walkFn(filename, nil, nil, err); err != nil && err != filepath.SkipDir {
return err
}
} else {
err = walkFiles(fs, filename, fileInfo, file, walkFn)
// file is closed by walkFiles, so we don't need to close it here.
if err != nil {
if !fileInfo.IsDir() || err != filepath.SkipDir {
return err
}
}
}
}
return nil
}
// openStat performs Open and Stat and returns results, or first error encountered.
// The caller is responsible for closing the returned file when done.
func openStat(fs http.FileSystem, name string) (http.File, os.FileInfo, error) {
f, err := fs.Open(name)
if err != nil {
return nil, nil, err
}
fi, err := f.Stat()
if err != nil {
f.Close()
return nil, nil, err
}
return f, fi, nil
}

@ -0,0 +1,16 @@
sudo: false
language: go
go:
- 1.x
- master
matrix:
allow_failures:
- go: master
fast_finish: true
install:
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
script:
- go get -t -v ./...
- diff -u <(echo -n) <(gofmt -d -s .)
- go tool vet .
- go test -v -race ./...

@ -0,0 +1,10 @@
Contributing
============
vfsgen is open source, thanks for considering contributing!
Please note that vfsgen aims to be simple and minimalistic, with as little to configure as possible. If you'd like to remove or simplify code (while having tests continue to pass), fix bugs, or improve code (e.g., add missing error checking, etc.), PRs and issues are welcome.
However, if you'd like to add new functionality that increases complexity or scope, please make an issue and discuss your proposal first. I'm unlikely to accept such changes outright. It might be that your request is already a part of other similar packages, or it might fit in their scope better. See [Comparison and Alternatives](https://github.com/shurcooL/vfsgen/tree/README-alternatives-and-comparison-section#comparison) sections.
Thank you!

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2015 Dmitri Shuralyov
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.

@ -0,0 +1,201 @@
vfsgen
======
[![Build Status](https://travis-ci.org/shurcooL/vfsgen.svg?branch=master)](https://travis-ci.org/shurcooL/vfsgen) [![GoDoc](https://godoc.org/github.com/shurcooL/vfsgen?status.svg)](https://godoc.org/github.com/shurcooL/vfsgen)
Package vfsgen takes an http.FileSystem (likely at `go generate` time) and
generates Go code that statically implements the provided http.FileSystem.
Features:
- Efficient generated code without unneccessary overhead.
- Uses gzip compression internally (selectively, only for files that compress well).
- Enables direct access to internal gzip compressed bytes via an optional interface.
- Outputs `gofmt`ed Go code.
Installation
------------
```bash
go get -u github.com/shurcooL/vfsgen
```
Usage
-----
Package `vfsgen` is a Go code generator library. It has a `Generate` function that takes an input filesystem (as a [`http.FileSystem`](https://godoc.org/net/http#FileSystem) type), and generates a Go code file that statically implements the contents of the input filesystem.
For example, we can use [`http.Dir`](https://godoc.org/net/http#Dir) as a `http.FileSystem` implementation that uses the contents of the `/path/to/assets` directory:
```Go
var fs http.FileSystem = http.Dir("/path/to/assets")
```
Now, when you execute the following code:
```Go
err := vfsgen.Generate(fs, vfsgen.Options{})
if err != nil {
log.Fatalln(err)
}
```
An assets_vfsdata.go file will be generated in the current directory:
```Go
// Code generated by vfsgen; DO NOT EDIT.
package main
import ...
// assets statically implements the virtual filesystem provided to vfsgen.Generate.
var assets http.FileSystem = ...
```
Then, in your program, you can use `assets` as any other [`http.FileSystem`](https://godoc.org/net/http#FileSystem), for example:
```Go
file, err := assets.Open("/some/file.txt")
if err != nil {
return err
}
defer file.Close()
```
```Go
http.Handle("/assets/", http.FileServer(assets))
```
`vfsgen` can be more useful when combined with build tags and go generate directives. This is described below.
### `go generate` Usage
vfsgen is great to use with go generate directives. The code invoking `vfsgen.Generate` can go in an assets_generate.go file, which can then be invoked via "//go:generate go run assets_generate.go". The input virtual filesystem can read directly from disk, or it can be more involved.
By using build tags, you can create a development mode where assets are loaded directly from disk via `http.Dir`, but then statically implemented for final releases.
For example, suppose your source filesystem is defined in a package with import path "example.com/project/data" as:
```Go
// +build dev
package data
import "net/http"
// Assets contains project assets.
var Assets http.FileSystem = http.Dir("assets")
```
When built with the "dev" build tag, accessing `data.Assets` will read from disk directly via `http.Dir`.
A generate helper file assets_generate.go can be invoked via "//go:generate go run -tags=dev assets_generate.go" directive:
```Go
// +build ignore
package main
import (
"log"
"example.com/project/data"
"github.com/shurcooL/vfsgen"
)
func main() {
err := vfsgen.Generate(data.Assets, vfsgen.Options{
PackageName: "data",
BuildTags: "!dev",
VariableName: "Assets",
})
if err != nil {
log.Fatalln(err)
}
}
```
Note that "dev" build tag is used to access the source filesystem, and the output file will contain "!dev" build tag. That way, the statically implemented version will be used during normal builds and `go get`, when custom builds tags are not specified.
### `vfsgendev` Usage
`vfsgendev` is a binary that can be used to replace the need for the assets_generate.go file.
Make sure it's installed and available in your PATH.
```bash
go get -u github.com/shurcooL/vfsgen/cmd/vfsgendev
```
Then the "//go:generate go run -tags=dev assets_generate.go" directive can be replaced with:
```
//go:generate vfsgendev -source="example.com/project/data".Assets
```
vfsgendev accesses the source variable using "dev" build tag, and generates an output file with "!dev" build tag.
### Additional Embedded Information
All compressed files implement [`httpgzip.GzipByter` interface](https://godoc.org/github.com/shurcooL/httpgzip#GzipByter) for efficient direct access to the internal compressed bytes:
```Go
// GzipByter is implemented by compressed files for
// efficient direct access to the internal compressed bytes.
type GzipByter interface {
// GzipBytes returns gzip compressed contents of the file.
GzipBytes() []byte
}
```
Files that have been determined to not be worth gzip compressing (their compressed size is larger than original) implement [`httpgzip.NotWorthGzipCompressing` interface](https://godoc.org/github.com/shurcooL/httpgzip#NotWorthGzipCompressing):
```Go
// NotWorthGzipCompressing is implemented by files that were determined
// not to be worth gzip compressing (the file size did not decrease as a result).
type NotWorthGzipCompressing interface {
// NotWorthGzipCompressing is a noop. It's implemented in order to indicate
// the file is not worth gzip compressing.
NotWorthGzipCompressing()
}
```
Comparison
----------
vfsgen aims to be conceptually simple to use. The [`http.FileSystem`](https://godoc.org/net/http#FileSystem) abstraction is central to vfsgen. It's used as both input for code generation, and as output in the generated code.
That enables great flexibility through orthogonality, since helpers and wrappers can operate on `http.FileSystem` without knowing about vfsgen. If you want, you can perform pre-processing, minifying assets, merging folders, filtering out files and otherwise modifying input via generic `http.FileSystem` middleware.
It avoids unneccessary overhead by merging what was previously done with two distinct packages into a single package.
It strives to be the best in its class in terms of code quality and efficiency of generated code. However, if your use goals are different, there are other similar packages that may fit your needs better.
### Alternatives
- [`go-bindata`](https://github.com/jteeuwen/go-bindata) - Reads from disk, generates Go code that provides access to data via a [custom API](https://github.com/jteeuwen/go-bindata#accessing-an-asset).
- [`go-bindata-assetfs`](https://github.com/elazarl/go-bindata-assetfs) - Takes output of go-bindata and provides a wrapper that implements `http.FileSystem` interface (the same as what vfsgen outputs directly).
- [`becky`](https://github.com/tv42/becky) - Embeds assets as string literals in Go source.
- [`statik`](https://github.com/rakyll/statik) - Embeds a directory of static files to be accessed via `http.FileSystem` interface (sounds very similar to vfsgen); implementation sourced from [camlistore](https://camlistore.org).
- [`go.rice`](https://github.com/GeertJohan/go.rice) - Makes working with resources such as HTML, JS, CSS, images and templates very easy.
- [`esc`](https://github.com/mjibson/esc) - Embeds files into Go programs and provides `http.FileSystem` interfaces to them.
- [`staticfiles`](https://github.com/bouk/staticfiles) - Allows you to embed a directory of files into your Go binary.
- [`togo`](https://github.com/flazz/togo) - Generates a Go source file with a `[]byte` var containing the given file's contents.
- [`fileb0x`](https://github.com/UnnoTed/fileb0x) - Simple customizable tool to embed files in Go.
- [`embedfiles`](https://github.com/leighmcculloch/embedfiles) - Simple tool for embedding files in Go code as a map.
- [`packr`](https://github.com/gobuffalo/packr) - Simple solution for bundling static assets inside of Go binaries.
- [`rsrc`](https://github.com/akavel/rsrc) - Tool for embedding .ico & manifest resources in Go programs for Windows.
Attribution
-----------
This package was originally based on the excellent work by [@jteeuwen](https://github.com/jteeuwen) on [`go-bindata`](https://github.com/jteeuwen/go-bindata) and [@elazarl](https://github.com/elazarl) on [`go-bindata-assetfs`](https://github.com/elazarl/go-bindata-assetfs).
License
-------
- [MIT License](LICENSE)

@ -0,0 +1,45 @@
package vfsgen
import "io"
// commentWriter writes a Go comment to the underlying io.Writer,
// using line comment form (//).
type commentWriter struct {
W io.Writer
wroteSlashes bool // Wrote "//" at the beginning of the current line.
}
func (c *commentWriter) Write(p []byte) (int, error) {
var n int
for i, b := range p {
if !c.wroteSlashes {
s := "//"
if b != '\n' {
s = "// "
}
if _, err := io.WriteString(c.W, s); err != nil {
return n, err
}
c.wroteSlashes = true
}
n0, err := c.W.Write(p[i : i+1])
n += n0
if err != nil {
return n, err
}
if b == '\n' {
c.wroteSlashes = false
}
}
return len(p), nil
}
func (c *commentWriter) Close() error {
if !c.wroteSlashes {
if _, err := io.WriteString(c.W, "//"); err != nil {
return err
}
c.wroteSlashes = true
}
return nil
}

@ -0,0 +1,15 @@
/*
Package vfsgen takes an http.FileSystem (likely at `go generate` time) and
generates Go code that statically implements the provided http.FileSystem.
Features:
- Efficient generated code without unneccessary overhead.
- Uses gzip compression internally (selectively, only for files that compress well).
- Enables direct access to internal gzip compressed bytes via an optional interface.
- Outputs `gofmt`ed Go code.
*/
package vfsgen

@ -0,0 +1,485 @@
package vfsgen
import (
"bytes"
"compress/gzip"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
pathpkg "path"
"sort"
"strconv"
"text/template"
"time"
"github.com/shurcooL/httpfs/vfsutil"
)
// Generate Go code that statically implements input filesystem,
// write the output to a file specified in opt.
func Generate(input http.FileSystem, opt Options) error {
opt.fillMissing()
// Use an in-memory buffer to generate the entire output.
buf := new(bytes.Buffer)
err := t.ExecuteTemplate(buf, "Header", opt)
if err != nil {
return err
}
var toc toc
err = findAndWriteFiles(buf, input, &toc)
if err != nil {
return err
}
err = t.ExecuteTemplate(buf, "DirEntries", toc.dirs)
if err != nil {
return err
}
err = t.ExecuteTemplate(buf, "Trailer", toc)
if err != nil {
return err
}
// Write output file (all at once).
fmt.Println("writing", opt.Filename)
err = ioutil.WriteFile(opt.Filename, buf.Bytes(), 0644)
return err
}
type toc struct {
dirs []*dirInfo
HasCompressedFile bool // There's at least one compressedFile.
HasFile bool // There's at least one uncompressed file.
}
// fileInfo is a definition of a file.
type fileInfo struct {
Path string
Name string
ModTime time.Time
UncompressedSize int64
}
// dirInfo is a definition of a directory.
type dirInfo struct {
Path string
Name string
ModTime time.Time
Entries []string
}
// findAndWriteFiles recursively finds all the file paths in the given directory tree.
// They are added to the given map as keys. Values will be safe function names
// for each file, which will be used when generating the output code.
func findAndWriteFiles(buf *bytes.Buffer, fs http.FileSystem, toc *toc) error {
walkFn := func(path string, fi os.FileInfo, r io.ReadSeeker, err error) error {
if err != nil {
// Consider all errors reading the input filesystem as fatal.
return err
}
switch fi.IsDir() {
case false:
file := &fileInfo{
Path: path,
Name: pathpkg.Base(path),
ModTime: fi.ModTime().UTC(),
UncompressedSize: fi.Size(),
}
marker := buf.Len()
// Write CompressedFileInfo.
err = writeCompressedFileInfo(buf, file, r)
switch err {
default:
return err
case nil:
toc.HasCompressedFile = true
// If compressed file is not smaller than original, revert and write original file.
case errCompressedNotSmaller:
_, err = r.Seek(0, io.SeekStart)
if err != nil {
return err
}
buf.Truncate(marker)
// Write FileInfo.
err = writeFileInfo(buf, file, r)
if err != nil {
return err
}
toc.HasFile = true
}
case true:
entries, err := readDirPaths(fs, path)
if err != nil {
return err
}
dir := &dirInfo{
Path: path,
Name: pathpkg.Base(path),
ModTime: fi.ModTime().UTC(),
Entries: entries,
}
toc.dirs = append(toc.dirs, dir)
// Write DirInfo.
err = t.ExecuteTemplate(buf, "DirInfo", dir)
if err != nil {
return err
}
}
return nil
}
err := vfsutil.WalkFiles(fs, "/", walkFn)
return err
}
// readDirPaths reads the directory named by dirname and returns
// a sorted list of directory paths.
func readDirPaths(fs http.FileSystem, dirname string) ([]string, error) {
fis, err := vfsutil.ReadDir(fs, dirname)
if err != nil {
return nil, err
}
paths := make([]string, len(fis))
for i := range fis {
paths[i] = pathpkg.Join(dirname, fis[i].Name())
}
sort.Strings(paths)
return paths, nil
}
// writeCompressedFileInfo writes CompressedFileInfo.
// It returns errCompressedNotSmaller if compressed file is not smaller than original.
func writeCompressedFileInfo(w io.Writer, file *fileInfo, r io.Reader) error {
err := t.ExecuteTemplate(w, "CompressedFileInfo-Before", file)
if err != nil {
return err
}
sw := &stringWriter{Writer: w}
gw := gzip.NewWriter(sw)
_, err = io.Copy(gw, r)
if err != nil {
return err
}
err = gw.Close()
if err != nil {
return err
}
if sw.N >= file.UncompressedSize {
return errCompressedNotSmaller
}
err = t.ExecuteTemplate(w, "CompressedFileInfo-After", file)
return err
}
var errCompressedNotSmaller = errors.New("compressed file is not smaller than original")
// Write FileInfo.
func writeFileInfo(w io.Writer, file *fileInfo, r io.Reader) error {
err := t.ExecuteTemplate(w, "FileInfo-Before", file)
if err != nil {
return err
}
sw := &stringWriter{Writer: w}
_, err = io.Copy(sw, r)
if err != nil {
return err
}
err = t.ExecuteTemplate(w, "FileInfo-After", file)
return err
}
var t = template.Must(template.New("").Funcs(template.FuncMap{
"quote": strconv.Quote,
"comment": func(s string) (string, error) {
var buf bytes.Buffer
cw := &commentWriter{W: &buf}
_, err := io.WriteString(cw, s)
if err != nil {
return "", err
}
err = cw.Close()
return buf.String(), err
},
}).Parse(`{{define "Header"}}// Code generated by vfsgen; DO NOT EDIT.
{{with .BuildTags}}// +build {{.}}
{{end}}package {{.PackageName}}
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
pathpkg "path"
"time"
)
{{comment .VariableComment}}
var {{.VariableName}} = func() http.FileSystem {
fs := vfsgen۰FS{
{{end}}
{{define "CompressedFileInfo-Before"}} {{quote .Path}}: &vfsgen۰CompressedFileInfo{
name: {{quote .Name}},
modTime: {{template "Time" .ModTime}},
uncompressedSize: {{.UncompressedSize}},
{{/* This blank line separating compressedContent is neccessary to prevent potential gofmt issues. See issue #19. */}}
compressedContent: []byte("{{end}}{{define "CompressedFileInfo-After"}}"),
},
{{end}}
{{define "FileInfo-Before"}} {{quote .Path}}: &vfsgen۰FileInfo{
name: {{quote .Name}},
modTime: {{template "Time" .ModTime}},
content: []byte("{{end}}{{define "FileInfo-After"}}"),
},
{{end}}
{{define "DirInfo"}} {{quote .Path}}: &vfsgen۰DirInfo{
name: {{quote .Name}},
modTime: {{template "Time" .ModTime}},
},
{{end}}
{{define "DirEntries"}} }
{{range .}}{{if .Entries}} fs[{{quote .Path}}].(*vfsgen۰DirInfo).entries = []os.FileInfo{{"{"}}{{range .Entries}}
fs[{{quote .}}].(os.FileInfo),{{end}}
}
{{end}}{{end}}
return fs
}()
{{end}}
{{define "Trailer"}}
type vfsgen۰FS map[string]interface{}
func (fs vfsgen۰FS) Open(path string) (http.File, error) {
path = pathpkg.Clean("/" + path)
f, ok := fs[path]
if !ok {
return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
}
switch f := f.(type) {{"{"}}{{if .HasCompressedFile}}
case *vfsgen۰CompressedFileInfo:
gr, err := gzip.NewReader(bytes.NewReader(f.compressedContent))
if err != nil {
// This should never happen because we generate the gzip bytes such that they are always valid.
panic("unexpected error reading own gzip compressed bytes: " + err.Error())
}
return &vfsgen۰CompressedFile{
vfsgen۰CompressedFileInfo: f,
gr: gr,
}, nil{{end}}{{if .HasFile}}
case *vfsgen۰FileInfo:
return &vfsgen۰File{
vfsgen۰FileInfo: f,
Reader: bytes.NewReader(f.content),
}, nil{{end}}
case *vfsgen۰DirInfo:
return &vfsgen۰Dir{
vfsgen۰DirInfo: f,
}, nil
default:
// This should never happen because we generate only the above types.
panic(fmt.Sprintf("unexpected type %T", f))
}
}
{{if .HasCompressedFile}}
// vfsgen۰CompressedFileInfo is a static definition of a gzip compressed file.
type vfsgen۰CompressedFileInfo struct {
name string
modTime time.Time
compressedContent []byte
uncompressedSize int64
}
func (f *vfsgen۰CompressedFileInfo) Readdir(count int) ([]os.FileInfo, error) {
return nil, fmt.Errorf("cannot Readdir from file %s", f.name)
}
func (f *vfsgen۰CompressedFileInfo) Stat() (os.FileInfo, error) { return f, nil }
func (f *vfsgen۰CompressedFileInfo) GzipBytes() []byte {
return f.compressedContent
}
func (f *vfsgen۰CompressedFileInfo) Name() string { return f.name }
func (f *vfsgen۰CompressedFileInfo) Size() int64 { return f.uncompressedSize }
func (f *vfsgen۰CompressedFileInfo) Mode() os.FileMode { return 0444 }
func (f *vfsgen۰CompressedFileInfo) ModTime() time.Time { return f.modTime }
func (f *vfsgen۰CompressedFileInfo) IsDir() bool { return false }
func (f *vfsgen۰CompressedFileInfo) Sys() interface{} { return nil }
// vfsgen۰CompressedFile is an opened compressedFile instance.
type vfsgen۰CompressedFile struct {
*vfsgen۰CompressedFileInfo
gr *gzip.Reader
grPos int64 // Actual gr uncompressed position.
seekPos int64 // Seek uncompressed position.
}
func (f *vfsgen۰CompressedFile) Read(p []byte) (n int, err error) {
if f.grPos > f.seekPos {
// Rewind to beginning.
err = f.gr.Reset(bytes.NewReader(f.compressedContent))
if err != nil {
return 0, err
}
f.grPos = 0
}
if f.grPos < f.seekPos {
// Fast-forward.
_, err = io.CopyN(ioutil.Discard, f.gr, f.seekPos-f.grPos)
if err != nil {
return 0, err
}
f.grPos = f.seekPos
}
n, err = f.gr.Read(p)
f.grPos += int64(n)
f.seekPos = f.grPos
return n, err
}
func (f *vfsgen۰CompressedFile) Seek(offset int64, whence int) (int64, error) {
switch whence {
case io.SeekStart:
f.seekPos = 0 + offset
case io.SeekCurrent:
f.seekPos += offset
case io.SeekEnd:
f.seekPos = f.uncompressedSize + offset
default:
panic(fmt.Errorf("invalid whence value: %v", whence))
}
return f.seekPos, nil
}
func (f *vfsgen۰CompressedFile) Close() error {
return f.gr.Close()
}
{{else}}
// We already imported "compress/gzip" and "io/ioutil", but ended up not using them. Avoid unused import error.
var _ = gzip.Reader{}
var _ = ioutil.Discard
{{end}}{{if .HasFile}}
// vfsgen۰FileInfo is a static definition of an uncompressed file (because it's not worth gzip compressing).
type vfsgen۰FileInfo struct {
name string
modTime time.Time
content []byte
}
func (f *vfsgen۰FileInfo) Readdir(count int) ([]os.FileInfo, error) {
return nil, fmt.Errorf("cannot Readdir from file %s", f.name)
}
func (f *vfsgen۰FileInfo) Stat() (os.FileInfo, error) { return f, nil }
func (f *vfsgen۰FileInfo) NotWorthGzipCompressing() {}
func (f *vfsgen۰FileInfo) Name() string { return f.name }
func (f *vfsgen۰FileInfo) Size() int64 { return int64(len(f.content)) }
func (f *vfsgen۰FileInfo) Mode() os.FileMode { return 0444 }
func (f *vfsgen۰FileInfo) ModTime() time.Time { return f.modTime }
func (f *vfsgen۰FileInfo) IsDir() bool { return false }
func (f *vfsgen۰FileInfo) Sys() interface{} { return nil }
// vfsgen۰File is an opened file instance.
type vfsgen۰File struct {
*vfsgen۰FileInfo
*bytes.Reader
}
func (f *vfsgen۰File) Close() error {
return nil
}
{{else if not .HasCompressedFile}}
// We already imported "bytes", but ended up not using it. Avoid unused import error.
var _ = bytes.Reader{}
{{end}}
// vfsgen۰DirInfo is a static definition of a directory.
type vfsgen۰DirInfo struct {
name string
modTime time.Time
entries []os.FileInfo
}
func (d *vfsgen۰DirInfo) Read([]byte) (int, error) {
return 0, fmt.Errorf("cannot Read from directory %s", d.name)
}
func (d *vfsgen۰DirInfo) Close() error { return nil }
func (d *vfsgen۰DirInfo) Stat() (os.FileInfo, error) { return d, nil }
func (d *vfsgen۰DirInfo) Name() string { return d.name }
func (d *vfsgen۰DirInfo) Size() int64 { return 0 }
func (d *vfsgen۰DirInfo) Mode() os.FileMode { return 0755 | os.ModeDir }
func (d *vfsgen۰DirInfo) ModTime() time.Time { return d.modTime }
func (d *vfsgen۰DirInfo) IsDir() bool { return true }
func (d *vfsgen۰DirInfo) Sys() interface{} { return nil }
// vfsgen۰Dir is an opened dir instance.
type vfsgen۰Dir struct {
*vfsgen۰DirInfo
pos int // Position within entries for Seek and Readdir.
}
func (d *vfsgen۰Dir) Seek(offset int64, whence int) (int64, error) {
if offset == 0 && whence == io.SeekStart {
d.pos = 0
return 0, nil
}
return 0, fmt.Errorf("unsupported Seek in directory %s", d.name)
}
func (d *vfsgen۰Dir) Readdir(count int) ([]os.FileInfo, error) {
if d.pos >= len(d.entries) && count > 0 {
return nil, io.EOF
}
if count <= 0 || count > len(d.entries)-d.pos {
count = len(d.entries) - d.pos
}
e := d.entries[d.pos : d.pos+count]
d.pos += count
return e, nil
}
{{end}}
{{define "Time"}}
{{- if .IsZero -}}
time.Time{}
{{- else -}}
time.Date({{.Year}}, {{printf "%d" .Month}}, {{.Day}}, {{.Hour}}, {{.Minute}}, {{.Second}}, {{.Nanosecond}}, time.UTC)
{{- end -}}
{{end}}
`))

@ -0,0 +1,45 @@
package vfsgen
import (
"fmt"
"strings"
)
// Options for vfsgen code generation.
type Options struct {
// Filename of the generated Go code output (including extension).
// If left empty, it defaults to "{{toLower .VariableName}}_vfsdata.go".
Filename string
// PackageName is the name of the package in the generated code.
// If left empty, it defaults to "main".
PackageName string
// BuildTags are the optional build tags in the generated code.
// The build tags syntax is specified by the go tool.
BuildTags string
// VariableName is the name of the http.FileSystem variable in the generated code.
// If left empty, it defaults to "assets".
VariableName string
// VariableComment is the comment of the http.FileSystem variable in the generated code.
// If left empty, it defaults to "{{.VariableName}} statically implements the virtual filesystem provided to vfsgen.".
VariableComment string
}
// fillMissing sets default values for mandatory options that are left empty.
func (opt *Options) fillMissing() {
if opt.PackageName == "" {
opt.PackageName = "main"
}
if opt.VariableName == "" {
opt.VariableName = "assets"
}
if opt.Filename == "" {
opt.Filename = fmt.Sprintf("%s_vfsdata.go", strings.ToLower(opt.VariableName))
}
if opt.VariableComment == "" {
opt.VariableComment = fmt.Sprintf("%s statically implements the virtual filesystem provided to vfsgen.", opt.VariableName)
}
}

@ -0,0 +1,27 @@
package vfsgen
import (
"io"
)
// stringWriter writes given bytes to underlying io.Writer as a Go interpreted string literal value,
// not including double quotes. It tracks the total number of bytes written.
type stringWriter struct {
io.Writer
N int64 // Total bytes written.
}
func (sw *stringWriter) Write(p []byte) (n int, err error) {
const hex = "0123456789abcdef"
buf := []byte{'\\', 'x', 0, 0}
for _, b := range p {
buf[2], buf[3] = hex[b/16], hex[b%16]
_, err = sw.Writer.Write(buf)
if err != nil {
return n, err
}
n++
sw.N++
}
return n, nil
}

@ -91,8 +91,6 @@ github.com/denisenkom/go-mssqldb
github.com/dgrijalva/jwt-go
# github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712
github.com/edsrzf/mmap-go
# github.com/elazarl/go-bindata-assetfs v0.0.0-20151224045452-57eb5e1fc594
github.com/elazarl/go-bindata-assetfs
# github.com/emirpasic/gods v1.12.0
github.com/emirpasic/gods/trees/binaryheap
github.com/emirpasic/gods/containers
@ -115,8 +113,6 @@ github.com/facebookgo/httpdown
github.com/facebookgo/stats
# github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd
github.com/glycerine/go-unsnap-stream
# github.com/go-macaron/bindata v0.0.0-20161222093048-85786f57eee3
github.com/go-macaron/bindata
# github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443
github.com/go-macaron/binding
# github.com/go-macaron/cache v0.0.0-20151013081102-561735312776
@ -296,8 +292,12 @@ github.com/russross/blackfriday
github.com/satori/go.uuid
# github.com/sergi/go-diff v1.0.0
github.com/sergi/go-diff/diffmatchpatch
# github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b
github.com/shurcooL/httpfs/vfsutil
# github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc
github.com/shurcooL/sanitized_anchor_name
# github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
github.com/shurcooL/vfsgen
# github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d
github.com/siddontang/go-snappy/snappy
# github.com/src-d/gcfg v1.4.0

Loading…
Cancel
Save