Embed data files in the binary (#180)

Now that we're on Go 1.16+ we can do this easily! The main advantage is
it means users can build a genqlient binary and use that portably (or we
could distribute one, or whatever). Plus the code is marginally simpler;
the `embed` API is really quite nice.

Fixes #9.

Test plan:
```
make check
go build .
rm -rf generate               # pretend we have no checkout
./genqlient ./internal/integration/genqlient.yaml
./genqlient --init            # fails after generating a default config
```
This commit is contained in:
Ben Kraft
2022-03-22 12:00:56 -07:00
committed by GitHub
parent 13094c3e58
commit 36e86cf97f
4 changed files with 14 additions and 29 deletions
+2
View File
@@ -26,6 +26,8 @@ When releasing a new version:
### New features: ### New features:
- genqlient can now run as a portable binary (i.e. without a local checkout of the repository or `go run`).
### Bug fixes: ### Bug fixes:
## v0.4.0 ## v0.4.0
+5 -16
View File
@@ -1,8 +1,8 @@
package generate package generate
import ( import (
_ "embed"
"go/token" "go/token"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@@ -123,22 +123,11 @@ func ReadAndValidateConfigFromDefaultLocations() (*Config, error) {
return ReadAndValidateConfig(cfgFile) return ReadAndValidateConfig(cfgFile)
} }
//go:embed default_genqlient.yaml
var defaultConfig []byte
func initConfig(filename string) error { func initConfig(filename string) error {
// TODO(benkraft): Embed this config file into the binary, see return os.WriteFile(filename, defaultConfig, 0o644)
// https://github.com/Khan/genqlient/issues/9.
r, err := os.Open(filepath.Join(thisDir, "default_genqlient.yaml"))
if err != nil {
return err
}
w, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o644)
if err != nil {
return errorf(nil, "unable to write default genqlient.yaml: %v", err)
}
_, err = io.Copy(w, r)
if err != nil {
return errorf(nil, "unable to write default genqlient.yaml: %v", err)
}
return nil
} }
// findCfg searches for the config file in this directory and all parents up the tree // findCfg searches for the config file in this directory and all parents up the tree
+2 -2
View File
@@ -124,7 +124,7 @@ func TestGenerate(t *testing.T) {
} }
} }
func defaultConfig(t *testing.T) *Config { func getDefaultConfig(t *testing.T) *Config {
// Parse the config that `genqlient --init` generates, to make sure that // Parse the config that `genqlient --init` generates, to make sure that
// works. // works.
var config Config var config Config
@@ -151,7 +151,7 @@ func TestGenerateWithConfig(t *testing.T) {
baseDir string // relative to dataDir baseDir string // relative to dataDir
config *Config // omits Schema and Operations, set below. config *Config // omits Schema and Operations, set below.
}{ }{
{"DefaultConfig", "", defaultConfig(t)}, {"DefaultConfig", "", getDefaultConfig(t)},
{"Subpackage", "", &Config{ {"Subpackage", "", &Config{
Generated: "mypkg/myfile.go", Generated: "mypkg/myfile.go",
}}, }},
+5 -11
View File
@@ -1,19 +1,14 @@
package generate package generate
import ( import (
"embed"
"io" "io"
"path/filepath"
"runtime"
"strings" "strings"
"text/template" "text/template"
) )
var ( //go:embed *.tmpl
// TODO(benkraft): Embed templates into the binary, see var templates embed.FS
// https://github.com/Khan/genqlient/issues/9.
_, thisFilename, _, _ = runtime.Caller(0)
thisDir = filepath.Dir(thisFilename)
)
func repeat(n int, s string) string { func repeat(n int, s string) string {
var builder strings.Builder var builder strings.Builder
@@ -37,7 +32,6 @@ func sub(x, y int) int { return x - y }
func (g *generator) render(tmplRelFilename string, w io.Writer, data interface{}) error { func (g *generator) render(tmplRelFilename string, w io.Writer, data interface{}) error {
tmpl := g.templateCache[tmplRelFilename] tmpl := g.templateCache[tmplRelFilename]
if tmpl == nil { if tmpl == nil {
absFilename := filepath.Join(thisDir, tmplRelFilename)
funcMap := template.FuncMap{ funcMap := template.FuncMap{
"ref": g.ref, "ref": g.ref,
"repeat": repeat, "repeat": repeat,
@@ -45,9 +39,9 @@ func (g *generator) render(tmplRelFilename string, w io.Writer, data interface{}
"sub": sub, "sub": sub,
} }
var err error var err error
tmpl, err = template.New(tmplRelFilename).Funcs(funcMap).ParseFiles(absFilename) tmpl, err = template.New(tmplRelFilename).Funcs(funcMap).ParseFS(templates, tmplRelFilename)
if err != nil { if err != nil {
return errorf(nil, "could not load template %v: %v", absFilename, err) return errorf(nil, "could not load template %v: %v", tmplRelFilename, err)
} }
g.templateCache[tmplRelFilename] = tmpl g.templateCache[tmplRelFilename] = tmpl
} }