Skip to content

Commit

Permalink
Walk parent directories to find config file (#141)
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
johnmaguire authored Oct 22, 2021
1 parent 9ecc62e commit 10dc388
Show file tree
Hide file tree
Showing 14 changed files with 193 additions and 6 deletions.
4 changes: 3 additions & 1 deletion docs/genqlient.yaml
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
45 changes: 45 additions & 0 deletions generate/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down Expand Up @@ -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.
Expand All @@ -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 ""
}
90 changes: 90 additions & 0 deletions generate/config_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}
24 changes: 19 additions & 5 deletions generate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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"`
}

Expand All @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions generate/testdata/find-config/current/genqlient.yaml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions generate/testdata/find-config/filenames/dotyml/.genqlient.yml
Original file line number Diff line number Diff line change
@@ -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
Empty file.
6 changes: 6 additions & 0 deletions generate/testdata/find-config/filenames/yaml/genqlient.yaml
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions generate/testdata/find-config/filenames/yml/genqlient.yml
Original file line number Diff line number Diff line change
@@ -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
Empty file.
Empty file.
Empty file.
6 changes: 6 additions & 0 deletions generate/testdata/find-config/parent/genqlient.yaml
Original file line number Diff line number Diff line change
@@ -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 comments on commit 10dc388

Please sign in to comment.