Embed static files in Go binaries (replacement for gobuffalo/packr)
Go
1,182
305 commits
updated Aug 27, 2021
github.com/markbates/pkger is a tool for embedding static files into Go binaries. It will, hopefully, be a replacement for github.com/gobuffalo/packr/v2.
Pkger is powered by the dark magic of Go Modules, so they're like, totally required.
With Go Modules pkger can resolve packages with accuracy. No more guessing and trying to figure out build paths, GOPATHS, etc... for this tired old lad.
With the module's path correctly resolved, it can serve as the "root" directory for that module, and all files in that module's directory are available.
Paths:
/cmd/pkger/main.go: seperator.
github.com/markbates/pkger:/cmd/pkger/main.go"github.com/gobuffalo/buffalo:/go.mod" => /go/pkg/mod/github.com/gobuffalo/buffalo@v0.14.7/go.mod
$ go get github.com/markbates/pkger/cmd/pkger
$ pkger -h
$ pkger
The result will be a pkged.go file in the root of the module with the embedded information and the package name of the module.
// ./pkged.go
package <.>
// Pkger stuff here
The -o flag can be used to specify the directory of the pkged.go file.
$ pkger -o cmd/reader
The result will be a pkged.go file in the cmd/reader folder with the embedded information and the package name of that folder.
// cmd/reader/pkged.go
package <reader>
// Pkger stuff here
There may be reasons where you don't reference a particular file, or folder, that you want embedded in your application, such as a build artifact.
To do this you may use either the github.com/markbates/pkger#Include function to set a no-op parser directive to include the specified path.
Alternatively, you may use the -include flag with the pkger and pkger list commands.
$ pkger list -include /actions -include github.com/gobuffalo/buffalo:/app.go
app
> app:/actions
> app:/actions/actions.go
> app:/assets
> app:/assets/css
> app:/assets/css/_buffalo.scss
> app:/assets/css/application.scss
> app:/assets/images
> app:/assets/images/favicon.ico
> app:/assets/images/logo.svg
> app:/assets/js
> app:/assets/js/application.js
> app:/go.mod
> app:/locales/all.en-us.yaml
> app:/public
> app:/public/assets
> app:/public/assets/.keep
> app:/public/assets/app.css
> app:/public/images
> app:/public/images/img1.png
> app:/public/index.html
> app:/public/robots.txt
> app:/templates
> app:/templates/_flash.plush.html
> app:/templates/application.plush.html
> app:/templates/index.plush.html
> app:/web
> app:/web/web.go
> github.com/gobuffalo/buffalo:/app.go
> github.com/gobuffalo/buffalo:/logo.svg
The reference application for the README examples, as well as all testing, can be found at https://github.com/markbates/pkger/tree/master/pkging/pkgtest/testdata/ref.
├── actions
│ └── actions.go
├── assets
│ ├── css
│ │ ├── _buffalo.scss
│ │ └── application.scss
│ ├── images
│ │ ├── favicon.ico
│ │ └── logo.svg
│ └── js
│ └── application.js
├── go.mod
├── go.sum
├── locales
│ └── all.en-us.yaml
├── main.go
├── mod
│ └── mod.go
├── models
│ └── models.go
├── public
│ ├── assets
│ │ └── app.css
│ ├── images
│ │ └── img1.png
│ ├── index.html
│ └── robots.txt
├── templates
│ ├── _flash.plush.html
│ ├── application.plush.html
│ └── index.plush.html
└── web
└── web.go
13 directories, 20 files
Pkger's API is modeled on that of the os package in Go's standard library. This makes Pkger usage familiar to Go developers.
The two most important interfaces are github.com/markbates/pkger/pkging#Pkger and github.com/markbates/pkger/pkging#File.
type Pkger interface {
Parse(p string) (Path, error)
Current() (here.Info, error)
Info(p string) (here.Info, error)
Create(name string) (File, error)
MkdirAll(p string, perm os.FileMode) error
Open(name string) (File, error)
Stat(name string) (os.FileInfo, error)
Walk(p string, wf filepath.WalkFunc) error
Remove(name string) error
RemoveAll(path string) error
}
type File interface {
Close() error
Info() here.Info
Name() string
Open(name string) (http.File, error)
Path() Path
Read(p []byte) (int, error)
Readdir(count int) ([]os.FileInfo, error)
Seek(offset int64, whence int) (int64, error)
Stat() (os.FileInfo, error)
Write(b []byte) (int, error)
}
These two interfaces, along with the os#FileInfo, provide the bulk of the API surface area.
func run() error {
f, err := pkger.Open("/public/index.html")
if err != nil {
return err
}
defer f.Close()
info, err := f.Stat()
if err != nil {
return err
}
fmt.Println("Name: ", info.Name())
fmt.Println("Size: ", info.Size())
fmt.Println("Mode: ", info.Mode())
fmt.Println("ModTime: ", info.ModTime())
if _, err := io.Copy(os.Stdout, f); err != nil {
return err
}
return nil
}
func run() error {
info, err := pkger.Stat("/public/index.html")
if err != nil {
return err
}
fmt.Println("Name: ", info.Name())
fmt.Println("Size: ", info.Size())
fmt.Println("Mode: ", info.Mode())
fmt.Println("ModTime: ", info.ModTime())
return nil
}
func run() error {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 0, ' ', tabwriter.Debug)
defer w.Flush()
return pkger.Walk("/public", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
fmt.Fprintf(w,
"%s \t %d \t %s \t %s \t\n",
info.Name(),
info.Size(),
info.Mode(),
info.ModTime().Format(time.RFC3339),
)
return nil
})
}
The github.com/markbates/pkger/parser#Parser works by statically analyzing the source code of your module using the go/parser to find a selection of declarations.
The following declarations in your source code will tell the parser to embed files or folders.
pkger.Dir("<path>") - Embeds all files under the specified path.pkger.Open("<path>") - Embeds the file, or folder, of the specified path.pkger.Stat("<path>") - Embeds the file, or folder, of the specified path.pkger.Walk("<path>", filepath.WalkFunc) - Embeds all files under the specified path.pkger.Include("<path>") - Include is a no-op that directs the pkger tool to include the desired file or folder.To see what declarations the parser has found, you can use the pkger parse command to get a JSON list of the declarations.
$ pkger parse
{
".": [
{
"file": {
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/foo/bar/baz",
"Path": {
"Pkg": "app",
"Name": "/foo/bar/baz"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"ImportPath": "app",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "main"
}
},
"pos": {
"Filename": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/main.go",
"Offset": 629,
"Line": 47,
"Column": 27
},
"type": "pkger.MkdirAll",
"value": "/foo/bar/baz"
},
{
"file": {
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/foo/bar/baz/biz.txt",
"Path": {
"Pkg": "app",
"Name": "/foo/bar/baz/biz.txt"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"ImportPath": "app",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "main"
}
},
"pos": {
"Filename": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/main.go",
"Offset": 706,
"Line": 51,
"Column": 25
},
"type": "pkger.Create",
"value": "/foo/bar/baz/biz.txt"
},
...
]
}
For a module aware list use the pkger list command.
$ pkger list
app
> app:/assets
> app:/assets/css
> app:/assets/css/_buffalo.scss
> app:/assets/css/application.scss
> app:/assets/images
> app:/assets/images/favicon.ico
> app:/assets/images/logo.svg
> app:/assets/js
> app:/assets/js/application.js
> app:/go.mod
> app:/locales/all.en-us.yaml
> app:/public
> app:/public/assets
> app:/public/assets/.keep
> app:/public/assets/app.css
> app:/public/images
> app:/public/images/img1.png
> app:/public/index.html
> app:/public/robots.txt
> app:/templates
> app:/templates/_flash.plush.html
> app:/templates/application.plush.html
> app:/templates/index.plush.html
> app:/web
> app:/web/web.go
> github.com/gobuffalo/buffalo:/logo.svg
The -json flag can be used to get a more detailed list in JSON.
$ pkger list -json
{
"ImportPath": "app",
"Files": [
{
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets",
"Path": {
"Pkg": "app",
"Name": "/assets"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets",
"ImportPath": "",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "assets"
}
},
{
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets/css",
"Path": {
"Pkg": "app",
"Name": "/assets/css"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets",
"ImportPath": "",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "assets"
}
},
...
}
Overall
Maintained
Code review
Go module
Latest release
Releases this year
5,038 followers · starred Dec 2020
248 followers · starred Aug 2021
1,093 followers · starred Nov 2019
170 followers · starred May 2022
Embed static files in Go binaries (replacement for gobuffalo/packr)
Go
1,182
305 commits
updated Aug 27, 2021
github.com/markbates/pkger is a tool for embedding static files into Go binaries. It will, hopefully, be a replacement for github.com/gobuffalo/packr/v2.
Pkger is powered by the dark magic of Go Modules, so they're like, totally required.
With Go Modules pkger can resolve packages with accuracy. No more guessing and trying to figure out build paths, GOPATHS, etc... for this tired old lad.
With the module's path correctly resolved, it can serve as the "root" directory for that module, and all files in that module's directory are available.
Paths:
/cmd/pkger/main.go: seperator.
github.com/markbates/pkger:/cmd/pkger/main.go"github.com/gobuffalo/buffalo:/go.mod" => /go/pkg/mod/github.com/gobuffalo/buffalo@v0.14.7/go.mod
$ go get github.com/markbates/pkger/cmd/pkger
$ pkger -h
$ pkger
The result will be a pkged.go file in the root of the module with the embedded information and the package name of the module.
// ./pkged.go
package <.>
// Pkger stuff here
The -o flag can be used to specify the directory of the pkged.go file.
$ pkger -o cmd/reader
The result will be a pkged.go file in the cmd/reader folder with the embedded information and the package name of that folder.
// cmd/reader/pkged.go
package <reader>
// Pkger stuff here
There may be reasons where you don't reference a particular file, or folder, that you want embedded in your application, such as a build artifact.
To do this you may use either the github.com/markbates/pkger#Include function to set a no-op parser directive to include the specified path.
Alternatively, you may use the -include flag with the pkger and pkger list commands.
$ pkger list -include /actions -include github.com/gobuffalo/buffalo:/app.go
app
> app:/actions
> app:/actions/actions.go
> app:/assets
> app:/assets/css
> app:/assets/css/_buffalo.scss
> app:/assets/css/application.scss
> app:/assets/images
> app:/assets/images/favicon.ico
> app:/assets/images/logo.svg
> app:/assets/js
> app:/assets/js/application.js
> app:/go.mod
> app:/locales/all.en-us.yaml
> app:/public
> app:/public/assets
> app:/public/assets/.keep
> app:/public/assets/app.css
> app:/public/images
> app:/public/images/img1.png
> app:/public/index.html
> app:/public/robots.txt
> app:/templates
> app:/templates/_flash.plush.html
> app:/templates/application.plush.html
> app:/templates/index.plush.html
> app:/web
> app:/web/web.go
> github.com/gobuffalo/buffalo:/app.go
> github.com/gobuffalo/buffalo:/logo.svg
The reference application for the README examples, as well as all testing, can be found at https://github.com/markbates/pkger/tree/master/pkging/pkgtest/testdata/ref.
├── actions
│ └── actions.go
├── assets
│ ├── css
│ │ ├── _buffalo.scss
│ │ └── application.scss
│ ├── images
│ │ ├── favicon.ico
│ │ └── logo.svg
│ └── js
│ └── application.js
├── go.mod
├── go.sum
├── locales
│ └── all.en-us.yaml
├── main.go
├── mod
│ └── mod.go
├── models
│ └── models.go
├── public
│ ├── assets
│ │ └── app.css
│ ├── images
│ │ └── img1.png
│ ├── index.html
│ └── robots.txt
├── templates
│ ├── _flash.plush.html
│ ├── application.plush.html
│ └── index.plush.html
└── web
└── web.go
13 directories, 20 files
Pkger's API is modeled on that of the os package in Go's standard library. This makes Pkger usage familiar to Go developers.
The two most important interfaces are github.com/markbates/pkger/pkging#Pkger and github.com/markbates/pkger/pkging#File.
type Pkger interface {
Parse(p string) (Path, error)
Current() (here.Info, error)
Info(p string) (here.Info, error)
Create(name string) (File, error)
MkdirAll(p string, perm os.FileMode) error
Open(name string) (File, error)
Stat(name string) (os.FileInfo, error)
Walk(p string, wf filepath.WalkFunc) error
Remove(name string) error
RemoveAll(path string) error
}
type File interface {
Close() error
Info() here.Info
Name() string
Open(name string) (http.File, error)
Path() Path
Read(p []byte) (int, error)
Readdir(count int) ([]os.FileInfo, error)
Seek(offset int64, whence int) (int64, error)
Stat() (os.FileInfo, error)
Write(b []byte) (int, error)
}
These two interfaces, along with the os#FileInfo, provide the bulk of the API surface area.
func run() error {
f, err := pkger.Open("/public/index.html")
if err != nil {
return err
}
defer f.Close()
info, err := f.Stat()
if err != nil {
return err
}
fmt.Println("Name: ", info.Name())
fmt.Println("Size: ", info.Size())
fmt.Println("Mode: ", info.Mode())
fmt.Println("ModTime: ", info.ModTime())
if _, err := io.Copy(os.Stdout, f); err != nil {
return err
}
return nil
}
func run() error {
info, err := pkger.Stat("/public/index.html")
if err != nil {
return err
}
fmt.Println("Name: ", info.Name())
fmt.Println("Size: ", info.Size())
fmt.Println("Mode: ", info.Mode())
fmt.Println("ModTime: ", info.ModTime())
return nil
}
func run() error {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 0, ' ', tabwriter.Debug)
defer w.Flush()
return pkger.Walk("/public", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
fmt.Fprintf(w,
"%s \t %d \t %s \t %s \t\n",
info.Name(),
info.Size(),
info.Mode(),
info.ModTime().Format(time.RFC3339),
)
return nil
})
}
The github.com/markbates/pkger/parser#Parser works by statically analyzing the source code of your module using the go/parser to find a selection of declarations.
The following declarations in your source code will tell the parser to embed files or folders.
pkger.Dir("<path>") - Embeds all files under the specified path.pkger.Open("<path>") - Embeds the file, or folder, of the specified path.pkger.Stat("<path>") - Embeds the file, or folder, of the specified path.pkger.Walk("<path>", filepath.WalkFunc) - Embeds all files under the specified path.pkger.Include("<path>") - Include is a no-op that directs the pkger tool to include the desired file or folder.To see what declarations the parser has found, you can use the pkger parse command to get a JSON list of the declarations.
$ pkger parse
{
".": [
{
"file": {
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/foo/bar/baz",
"Path": {
"Pkg": "app",
"Name": "/foo/bar/baz"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"ImportPath": "app",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "main"
}
},
"pos": {
"Filename": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/main.go",
"Offset": 629,
"Line": 47,
"Column": 27
},
"type": "pkger.MkdirAll",
"value": "/foo/bar/baz"
},
{
"file": {
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/foo/bar/baz/biz.txt",
"Path": {
"Pkg": "app",
"Name": "/foo/bar/baz/biz.txt"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"ImportPath": "app",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "main"
}
},
"pos": {
"Filename": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/main.go",
"Offset": 706,
"Line": 51,
"Column": 25
},
"type": "pkger.Create",
"value": "/foo/bar/baz/biz.txt"
},
...
]
}
For a module aware list use the pkger list command.
$ pkger list
app
> app:/assets
> app:/assets/css
> app:/assets/css/_buffalo.scss
> app:/assets/css/application.scss
> app:/assets/images
> app:/assets/images/favicon.ico
> app:/assets/images/logo.svg
> app:/assets/js
> app:/assets/js/application.js
> app:/go.mod
> app:/locales/all.en-us.yaml
> app:/public
> app:/public/assets
> app:/public/assets/.keep
> app:/public/assets/app.css
> app:/public/images
> app:/public/images/img1.png
> app:/public/index.html
> app:/public/robots.txt
> app:/templates
> app:/templates/_flash.plush.html
> app:/templates/application.plush.html
> app:/templates/index.plush.html
> app:/web
> app:/web/web.go
> github.com/gobuffalo/buffalo:/logo.svg
The -json flag can be used to get a more detailed list in JSON.
$ pkger list -json
{
"ImportPath": "app",
"Files": [
{
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets",
"Path": {
"Pkg": "app",
"Name": "/assets"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets",
"ImportPath": "",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "assets"
}
},
{
"Abs": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets/css",
"Path": {
"Pkg": "app",
"Name": "/assets/css"
},
"Here": {
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/assets",
"ImportPath": "",
"Module": {
"Path": "app",
"Main": true,
"Dir": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref",
"GoMod": "/go/src/github.com/markbates/pkger/pkging/pkgtest/testdata/ref/go.mod",
"GoVersion": "1.13"
},
"Name": "assets"
}
},
...
}
Overall
Maintained
Code review
Go module
Latest release
Releases this year
5,038 followers · starred Dec 2020
248 followers · starred Aug 2021
1,093 followers · starred Nov 2019
170 followers · starred May 2022