Walk parent directories to find config file (#141)
Code is mostly borrowed from [gqlgen](https://github.com/99designs/gqlgen). The idea here is that I want to be able to store `genqlient.yaml` at the top-level, but my client code lives down in `graph/client/`. I put the `//go:generate` line in `graph/client/client.go`.
This commit is contained in:
+3
-1
@@ -1,6 +1,8 @@
|
||||
# genqlient.yaml is genqlient's configuration file. This genqlient.yaml is an
|
||||
# example; use `go run github.com/Khan/genqlient --init` to generate a simple
|
||||
# starting point.
|
||||
# starting point. By default, genqlient looks for the configuration file
|
||||
# named [.]genqlient.y[a]ml in the current directory or any ancestor; or the
|
||||
# filename may be given as an argument.
|
||||
|
||||
# The filename with the GraphQL schema (in SDL format), relative to
|
||||
# genqlient.yaml.
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var cfgFilenames = []string{".genqlient.yml", ".genqlient.yaml", "genqlient.yml", "genqlient.yaml"}
|
||||
|
||||
// Config represents genqlient's configuration, generally read from
|
||||
// genqlient.yaml.
|
||||
//
|
||||
@@ -109,6 +111,17 @@ func ReadAndValidateConfig(filename string) (*Config, error) {
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// ReadAndValidateConfigFromDefaultLocations looks for a config file in the
|
||||
// current directory, and all parent directories walking up the tree. The
|
||||
// closest config file will be returned.
|
||||
func ReadAndValidateConfigFromDefaultLocations() (*Config, error) {
|
||||
cfgFile, err := findCfg()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ReadAndValidateConfig(cfgFile)
|
||||
}
|
||||
|
||||
func initConfig(filename string) error {
|
||||
// TODO(benkraft): Embed this config file into the binary, see
|
||||
// https://github.com/Khan/genqlient/issues/9.
|
||||
@@ -126,3 +139,35 @@ func initConfig(filename string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findCfg searches for the config file in this directory and all parents up the tree
|
||||
// looking for the closest match
|
||||
func findCfg() (string, error) {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", errorf(nil, "unable to get working dir to findCfg: %v", err)
|
||||
}
|
||||
|
||||
cfg := findCfgInDir(dir)
|
||||
|
||||
for cfg == "" && dir != filepath.Dir(dir) {
|
||||
dir = filepath.Dir(dir)
|
||||
cfg = findCfgInDir(dir)
|
||||
}
|
||||
|
||||
if cfg == "" {
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func findCfgInDir(dir string) string {
|
||||
for _, cfgName := range cfgFilenames {
|
||||
path := filepath.Join(dir, cfgName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return path
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFindCfg(t *testing.T) {
|
||||
cwd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := map[string]struct {
|
||||
startDir string
|
||||
expectedCfg string
|
||||
expectedErr error
|
||||
}{
|
||||
"yaml in parent directory": {
|
||||
startDir: cwd + "/testdata/find-config/parent/child",
|
||||
expectedCfg: cwd + "/testdata/find-config/parent/genqlient.yaml",
|
||||
},
|
||||
"yaml in current directory": {
|
||||
startDir: cwd + "/testdata/find-config/current",
|
||||
expectedCfg: cwd + "/testdata/find-config/current/genqlient.yaml",
|
||||
},
|
||||
"no yaml": {
|
||||
startDir: cwd + "/testdata/find-config/none/child",
|
||||
expectedErr: os.ErrNotExist,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
defer func() {
|
||||
require.NoError(t, os.Chdir(cwd), "Test cleanup failed")
|
||||
}()
|
||||
|
||||
err = os.Chdir(tc.startDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
path, err := findCfg()
|
||||
assert.Equal(t, tc.expectedCfg, path)
|
||||
assert.Equal(t, tc.expectedErr, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindCfgInDir(t *testing.T) {
|
||||
cwd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := map[string]struct {
|
||||
startDir string
|
||||
found bool
|
||||
}{
|
||||
"yaml": {
|
||||
startDir: cwd + "/testdata/find-config/filenames/yaml",
|
||||
found: true,
|
||||
},
|
||||
"yml": {
|
||||
startDir: cwd + "/testdata/find-config/filenames/yml",
|
||||
found: true,
|
||||
},
|
||||
".yaml": {
|
||||
startDir: cwd + "/testdata/find-config/filenames/dotyaml",
|
||||
found: true,
|
||||
},
|
||||
".yml": {
|
||||
startDir: cwd + "/testdata/find-config/filenames/dotyml",
|
||||
found: true,
|
||||
},
|
||||
"none": {
|
||||
startDir: cwd + "/testdata/find-config/filenames/none",
|
||||
found: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
path := findCfgInDir(tc.startDir)
|
||||
if tc.found {
|
||||
assert.NotEmpty(t, path)
|
||||
} else {
|
||||
assert.Empty(t, path)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+19
-5
@@ -14,9 +14,18 @@ import (
|
||||
)
|
||||
|
||||
func readConfigGenerateAndWrite(configFilename string) error {
|
||||
config, err := ReadAndValidateConfig(configFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
var config *Config
|
||||
var err error
|
||||
if configFilename != "" {
|
||||
config, err = ReadAndValidateConfig(configFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
config, err = ReadAndValidateConfigFromDefaultLocations()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
generated, err := Generate(config)
|
||||
@@ -42,7 +51,7 @@ func readConfigGenerateAndWrite(configFilename string) error {
|
||||
}
|
||||
|
||||
type cliArgs struct {
|
||||
ConfigFilename string `arg:"positional" placeholder:"CONFIG" default:"genqlient.yaml" help:"path to genqlient configuration (default genqlient.yaml)"`
|
||||
ConfigFilename string `arg:"positional" placeholder:"CONFIG" default:"" help:"path to genqlient configuration (default: genqlient.yaml in current or any parent directory)"`
|
||||
Init bool `arg:"--init" help:"write out and use a default config file"`
|
||||
}
|
||||
|
||||
@@ -67,7 +76,12 @@ func Main() {
|
||||
var args cliArgs
|
||||
arg.MustParse(&args)
|
||||
if args.Init {
|
||||
err := initConfig(args.ConfigFilename)
|
||||
filename := args.ConfigFilename
|
||||
if filename == "" {
|
||||
filename = "genqlient.yaml"
|
||||
}
|
||||
|
||||
err := initConfig(filename)
|
||||
exitIfError(err)
|
||||
}
|
||||
err := readConfigGenerateAndWrite(args.ConfigFilename)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Default genqlient config; for full documentation see:
|
||||
# https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
|
||||
schema: schema.graphql
|
||||
operations:
|
||||
- genqlient.graphql
|
||||
generated: generated.go
|
||||
@@ -0,0 +1,6 @@
|
||||
# Default genqlient config; for full documentation see:
|
||||
# https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
|
||||
schema: schema.graphql
|
||||
operations:
|
||||
- genqlient.graphql
|
||||
generated: generated.go
|
||||
@@ -0,0 +1,6 @@
|
||||
# Default genqlient config; for full documentation see:
|
||||
# https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
|
||||
schema: schema.graphql
|
||||
operations:
|
||||
- genqlient.graphql
|
||||
generated: generated.go
|
||||
@@ -0,0 +1,6 @@
|
||||
# Default genqlient config; for full documentation see:
|
||||
# https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
|
||||
schema: schema.graphql
|
||||
operations:
|
||||
- genqlient.graphql
|
||||
generated: generated.go
|
||||
@@ -0,0 +1,6 @@
|
||||
# Default genqlient config; for full documentation see:
|
||||
# https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
|
||||
schema: schema.graphql
|
||||
operations:
|
||||
- genqlient.graphql
|
||||
generated: generated.go
|
||||
@@ -0,0 +1,6 @@
|
||||
# Default genqlient config; for full documentation see:
|
||||
# https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
|
||||
schema: schema.graphql
|
||||
operations:
|
||||
- genqlient.graphql
|
||||
generated: generated.go
|
||||
Reference in New Issue
Block a user