Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Walk parent directories to find config file #141

Merged
merged 7 commits into from
Oct 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -123,3 +136,35 @@ func initConfig(filename string) error {
_, err = io.Copy(w, r)
return errorf(nil, "unable to write default genqlient.yaml: %v", err)
}

// 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
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