vfsgen
vfsgen copied to clipboard
Path mismatch when using `dev` tag
I'm trying to follow the example in the README on how to handle local filesystem vs embedded assets with a dev
build tag.
My project looks like this:
├── Makefile
├── cmd
│ └── main.go
├── frontend
│ └── index.html
├── go.mod
├── go.sum
└── pkg
└── data
├── assets_generate.go
├── data.go
└── dev.go
In my data.go
I have:
// +build ignore
package main
import (
"log"
"github.com/shurcooL/vfsgen"
"github.com/gargath/vfstest/pkg/data"
)
func main() {
err := vfsgen.Generate(data.Assets, vfsgen.Options{
PackageName: "data",
BuildTags: "!dev",
VariableName: "Assets",
})
if err != nil {
log.Fatalln(err)
}
}
while in dev.go
I have:
// +build dev
package data
import (
"net/http"
)
var Assets http.FileSystem = http.Dir("../../frontend/")
My cmd/main.go
is a simple
package main
import (
"net/http"
"log"
"github.com/gargath/vfstest/pkg/data"
)
func main() {
fs := http.FileServer(data.Assets)
http.Handle("/", fs)
err := http.ListenAndServe(":3000", nil)
if err != nil {
log.Fatal(err)
}
}
When I run go generate ./pkg/...
and then go build -o vfserver github.com/gargath/vfstest/cmd
, I get a working binary that serves the embedded content.
However, when running go build -tags dev -o vfserver github.com/gargath/vfstest/cmd
, I get 404 errors from the HTTP server because it is now trying to find the path ../../frontend/
relative to the binary in the project root.
I can fix this by changing the path in data.Assets
to frontend/
, but then go generate
no longer works:
go generate ./pkg/...
2020/03/17 11:22:14 open frontend: no such file or directory
exit status 1
pkg/data/data.go:3: running "go": exit status 1
make: *** [generate] Error 1
How can I structure a project so that the same static assets directory can be used to both generate and serve locally?
I'm curious about this as well.
There are a few other folks who have put together examples of vfsgen's usage (https://github.com/ahrtr/vfsgenDemo and https://github.com/artificerpi/vfsgen-sample), but they all seem to have the same issue: they place assets in a subdirectory of a subpackage, but use a path relative to the root of the subpackage in their http.FileSystem
.
I'm not certain either of these samples have been tested in development mode :smile:
Sorry I missed this earlier.
Your definition of Assets
in dev.go
specifies a relative path, which is prone to this kind of problem.
When go generate
runs data.go
, the working directory is one thing, but when you run your project's binary via go build -tags dev
, the working directory may be different. So a relative path is not a reliable way of specifying a directory.
I suggest fixing this problem by defining Assets
using http.Dir
with an absolute path. There are various ways to do this. I've been using a helper like this:
func importPathToDir(importPath string) string {
p, err := build.Import(importPath, "", build.FindOnly)
if err != nil {
log.Fatalln(err)
}
return p.Dir
}
Then you can do something like:
var Assets http.FileSystem = http.Dir(filepath.Join(importPathToDir("github.com/gargath/vfstest"), "frontend"))
You can see an example of me using vfsgen
in one of my projects here.
This is excellent. Thanks!
I literally just started using vfsgen (30min ago) and bumped into this immediately, no idea how so many projects use relative paths, pretty sure they have never executed with -tags=dev
....
I decided to go with a generate.go
such as:
// +build ignore
package main
import (
"log"
"os"
"github.com/owner/example/assets"
"github.com/shurcooL/vfsgen"
)
func main() {
// need to change directory so assets.Assets "static" works for both dev and vfsgen...
// gap in vfsgen or misuse?
err := os.Chdir("..")
if err != nil {
log.Fatalln(err)
}
err = vfsgen.Generate(assets.Assets, vfsgen.Options{
PackageName: "assets",
BuildTags: "!dev",
VariableName: "Assets",
Filename: "assets/assets_vfsdata.go",
})
if err != nil {
log.Fatalln(err)
}
}
And defining http.Dir to just static
(instead of ../static
)
When executing go run main.go
static is in that folder. When doing go generate ./assets
, regardless of where I call it, current directory will always be the one of generate.go
, so Chdir("..")
should work every time.
But even though I agree that relative paths are prone to errors, maybe the helper mentioned in this issue should be part of the package officially?