-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
There are many tools to embed static asset files into binaries:
- https://godoc.org/perkeep.org/pkg/fileembed / perkeep.org/pkg/fileembed/genfileembed
- https://godoc.org/github.com/gobuffalo/packr
- https://godoc.org/github.com/knadh/stuffbin
- https://github.com/rakyll/statik
- Bazel go_embed_data
Actually, https://tech.townsourced.com/post/embedding-static-files-in-go/ lists more:
- vfsgen - https://github.com/shurcooL/vfsgen
- go.rice - https://github.com/GeertJohan/go.rice
- statik - https://github.com/rakyll/statik
- esc - https://github.com/mjibson/esc
- go-embed - https://github.com/pyros2097/go-embed
- go-resources - https://github.com/omeid/go-resources
- statics - https://github.com/go-playground/statics
- templify - https://github.com/wlbr/templify
- gnoso/go-bindata - https://github.com/gnoso/go-bindata
- shuLhan/go-bindata - https://github.com/shuLhan/go-bindata
- fileb0x - https://github.com/UnnoTed/fileb0x
- gobundle - https://github.com/alecthomas/gobundle
- parcello - https://github.com/phogolabs/parcello
Proposal
I think it's time to do this well once & reduce duplication, adding official support for embedding file resources into the cmd/go tool.
Problems with the current situation:
- There are too many tools
- Using a go:generate-based solution bloats the git history with a second (and slightly larger) copy of each file.
- Not using go:generate means not being
go install
-able or making people write their own Makefiles, etc.
Goals:
- don't check in generated files
- don't generate *.go files at all (at least not in user's workspace)
- make
go install
/go build
do the embedding automatically - let user choose per file/glob which type of access is needed (e.g. []byte,
func() io.Reader
,io.ReaderAt
, etc) - Maybe store assets compressed in the binary where appropriate (e.g. if user only needs an
io.Reader
)? (edit: but probably not; see comments below) - No code execution at compilation time; that is a long-standing Go policy.
go build
orgo install
can not run arbitrary code, just likego:generate
doesn't run automatically at install time.
The two main implementation approaches are //go:embed Logo logo.jpg
or a well-known package (var Logo = embed.File("logo.jpg")
).
go:embed approach
For a go:embed
approach, one might say that any go/build
-selected *.go
file can contain something like:
//go:embed Logo logo.jpg
Which, say, compiles to:
func Logo() *io.SectionReader
(adding a dependency to the io
package)
Or:
//go:embedglob Assets assets/*.css assets/*.js
compiling to, say:
var Assets interface{
Files() []string
Open func(name string) *io.SectionReader
} = runtime.EmbedAsset(123)
Obviously this isn't fully fleshed out. There'd need to be something for compressed files too that yield only an io.Reader
.
embed package approach
The other high-level approach is to not have a magic //go:embed
syntax and instead just let users write Go code in some new "embed"
or "golang.org/x/foo/embed"
package:
var Static = embed.Dir("static")
var Logo = embed.File("images/logo.jpg")
var Words = embed.CompressedReader("dict/words")
Then have cmd/go recognize the calls to embed.Foo("foo/*.js") etc and glob do the work in cmd/go, rather than at runtime. Or maybe certain build tags or flags could make it fall back to doing things at runtime instead. Perkeep (linked above) has such a mode, which is nice to speed up incremental development where you don't care about linking one big binary.
Concerns
- Pick a style (//go:embed* vs a magic package).
- Block certain files?
- Probably block embedding
../../../../../../../../../../etc/shadow
- Maybe block reaching into
.git
too
- Probably block embedding