feat: optionally pull config from a custom file (envvar), default to data path. (#557)

Kendall Tauser created

* feat: optionally pull config from a custom file (envvar), default to data path.

* docs: add docs on SOFT_SERVE_CONFIG_LOCATION

* feat: add tests for SOFT_SERVE_CONFIG_LOCATION

Change summary

README.md                       |  5 +++++
pkg/config/config.go            |  8 +++++++-
pkg/config/config_test.go       | 23 +++++++++++++++++++++++
pkg/config/testdata/config.yaml |  3 +++
4 files changed, 38 insertions(+), 1 deletion(-)

Detailed changes

README.md πŸ”—

@@ -108,6 +108,11 @@ Make sure `git` is installed, then run `soft serve`. That’s it.
 This will create a `data` directory that will store all the repos, ssh keys,
 and database.
 
+By default, program configuration is stored within the `data` directory. But,
+this can be overridden by setting a custom path to a config file with `SOFT_SERVE_CONFIG_LOCATION`
+that is pre-created. If a config file pointed to by `SOFT_SERVE_CONFIG_LOCATION`, 
+the default location within the `data` dir is used for generating a default config.
+
 To change the default data path use `SOFT_SERVE_DATA_PATH` environment variable.
 
 ```sh

pkg/config/config.go πŸ”—

@@ -291,6 +291,12 @@ func DefaultDataPath() string {
 
 // ConfigPath returns the path to the config file.
 func (c *Config) ConfigPath() string { // nolint:revive
+	// If we have a custom config location set, then use that.
+	if path := os.Getenv("SOFT_SERVE_CONFIG_LOCATION"); exist(path) {
+		return path
+	}
+
+	// Otherwise, look in the data path.
 	return filepath.Join(c.DataPath, "config.yaml")
 }
 
@@ -301,7 +307,7 @@ func exist(path string) bool {
 
 // Exist returns true if the config file exists.
 func (c *Config) Exist() bool {
-	return exist(filepath.Join(c.DataPath, "config.yaml"))
+	return exist(c.ConfigPath())
 }
 
 // DefaultConfig returns the default Config. All the path values are relative

pkg/config/config_test.go πŸ”—

@@ -56,3 +56,26 @@ func TestValidateInitAdminKeys(t *testing.T) {
 		"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINMwLvyV3ouVrTysUYGoJdl5Vgn5BACKov+n9PlzfPwH",
 	})
 }
+
+func TestCustomConfigLocation(t *testing.T) {
+	is := is.New(t)
+	td := t.TempDir()
+	t.Cleanup(func() {
+		is.NoErr(os.Unsetenv("SOFT_SERVE_CONFIG_LOCATION"))
+	})
+
+	// Test that we get data from the custom file location, and not from the data dir.
+	is.NoErr(os.Setenv("SOFT_SERVE_CONFIG_LOCATION", "testdata/config.yaml"))
+	is.NoErr(os.Setenv("SOFT_SERVE_DATA_PATH", td))
+	cfg := DefaultConfig()
+	is.NoErr(cfg.Parse())
+	is.Equal(cfg.Name, "Test server name")
+	// If we unset the custom location, then use the default location.
+	is.NoErr(os.Unsetenv("SOFT_SERVE_CONFIG_LOCATION"))
+	cfg = DefaultConfig()
+	is.Equal(cfg.Name, "Soft Serve")
+	// Test that if the custom config location doesn't exist, default to datapath config.
+	is.NoErr(os.Setenv("SOFT_SERVE_CONFIG_LOCATION", "testdata/config_nonexistent.yaml"))
+	cfg = DefaultConfig()
+	is.Equal(cfg.Name, "Soft Serve")
+}