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:
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user