Use urfave/cli functionality for configuration

Instead of re-inventing the wheel regarding configuration location
handling and validation, introduce a new command flag `--config`
allowing for full flexibility of configuration filename and location.
Even environment variable overrides are used so that the custom env
lookup is unnecessary.

Signed-off-by: Steven Kriegler <sk.bunsenbrenner@gmail.com>
This commit is contained in:
justusbunsi 2022-05-22 10:51:31 +02:00
parent 5cb3daab60
commit 85d9e671fd
No known key found for this signature in database
GPG key ID: 82B29BF2507F9F8B
4 changed files with 37 additions and 60 deletions

View file

@ -4,7 +4,6 @@ import (
"fmt"
"log"
"os"
"path"
"gitea-sonarqube-pr-bot/internal/api"
giteaSdk "gitea-sonarqube-pr-bot/internal/clients/gitea"
@ -15,23 +14,22 @@ import (
"github.com/urfave/cli/v2"
)
func getConfigLocation() string {
configPath := path.Join("config")
if customConfigPath, ok := os.LookupEnv("PRBOT_CONFIG_PATH"); ok {
configPath = customConfigPath
}
return configPath
}
func main() {
settings.Load(getConfigLocation())
app := &cli.App{
Name: "gitea-sonarqube-pr-bot",
Usage: "Improve your experience with SonarQube and Gitea",
Description: `By default, gitea-sonarqube-pr-bot will start running the webserver if no arguments are passed.`,
Action: serveApi,
Flags: []cli.Flag{
&cli.PathFlag{
Name: "config",
Aliases: []string{"c"},
Value: "./config/config.yaml",
Usage: "Full path to configuration file.",
EnvVars: []string{"GITEA_SQ_BOT_CONFIG_PATH"},
TakesFile: true,
},
},
}
err := app.Run(os.Args)
@ -42,6 +40,7 @@ func main() {
func serveApi(c *cli.Context) error {
fmt.Println("Hi! I'm the Gitea-SonarQube-PR bot. At your service.")
settings.Load(c.Path("config"))
giteaHandler := api.NewGiteaWebhookHandler(giteaSdk.New(), sonarQubeSdk.New())
sqHandler := api.NewSonarQubeWebhookHandler(giteaSdk.New(), sonarQubeSdk.New())

View file

@ -1,22 +0,0 @@
package main
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetConfigLocationWithDefault(t *testing.T) {
assert.Equal(t, "config", getConfigLocation())
}
func TestGetConfigLocationWithEnvironmentOverride(t *testing.T) {
os.Setenv("PRBOT_CONFIG_PATH", "/tmp/")
assert.Equal(t, "/tmp/", getConfigLocation())
t.Cleanup(func() {
os.Unsetenv("PRBOT_CONFIG_PATH")
})
}

View file

@ -13,10 +13,9 @@ var (
Projects []Project
)
func newConfigReader() *viper.Viper {
func newConfigReader(configFile string) *viper.Viper {
v := viper.New()
v.SetConfigName("config.yaml")
v.SetConfigType("yaml")
v.SetConfigFile(configFile)
v.SetEnvPrefix("prbot")
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
v.AllowEmptyEnv(true)
@ -38,9 +37,8 @@ func newConfigReader() *viper.Viper {
return v
}
func Load(configPath string) {
r := newConfigReader()
r.AddConfigPath(configPath)
func Load(configFile string) {
r := newConfigReader(configFile)
err := r.ReadInConfig()
if err != nil {

View file

@ -31,7 +31,7 @@ projects:
name: pr-bot
`)
func WriteConfigFile(t *testing.T, content []byte) {
func WriteConfigFile(t *testing.T, content []byte) string {
dir := os.TempDir()
config := path.Join(dir, "config.yaml")
@ -40,21 +40,23 @@ func WriteConfigFile(t *testing.T, content []byte) {
})
_ = ioutil.WriteFile(config, content, 0444)
return config
}
func TestLoadWithMissingFile(t *testing.T) {
assert.Panics(t, func() { Load(os.TempDir()) }, "No panic while reading missing file")
assert.Panics(t, func() { Load(path.Join(os.TempDir(), "config.yaml")) }, "No panic while reading missing file")
}
func TestLoadWithExistingFile(t *testing.T) {
WriteConfigFile(t, defaultConfig)
c := WriteConfigFile(t, defaultConfig)
assert.NotPanics(t, func() { Load(os.TempDir()) }, "Unexpected panic while reading existing file")
assert.NotPanics(t, func() { Load(c) }, "Unexpected panic while reading existing file")
}
func TestLoadGiteaStructure(t *testing.T) {
WriteConfigFile(t, defaultConfig)
Load(os.TempDir())
c := WriteConfigFile(t, defaultConfig)
Load(c)
expected := GiteaConfig{
Url: "https://example.com/gitea",
@ -72,8 +74,8 @@ func TestLoadGiteaStructure(t *testing.T) {
func TestLoadGiteaStructureInjectedEnvs(t *testing.T) {
os.Setenv("PRBOT_GITEA_WEBHOOK_SECRET", "injected-webhook-secret")
os.Setenv("PRBOT_GITEA_TOKEN_VALUE", "injected-token")
WriteConfigFile(t, defaultConfig)
Load(os.TempDir())
c := WriteConfigFile(t, defaultConfig)
Load(c)
expected := GiteaConfig{
Url: "https://example.com/gitea",
@ -94,8 +96,8 @@ func TestLoadGiteaStructureInjectedEnvs(t *testing.T) {
}
func TestLoadSonarQubeStructure(t *testing.T) {
WriteConfigFile(t, defaultConfig)
Load(os.TempDir())
c := WriteConfigFile(t, defaultConfig)
Load(c)
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
@ -112,7 +114,7 @@ func TestLoadSonarQubeStructure(t *testing.T) {
}
func TestLoadSonarQubeStructureWithAdditionalMetrics(t *testing.T) {
WriteConfigFile(t, []byte(
c := WriteConfigFile(t, []byte(
`gitea:
url: https://example.com/gitea
token:
@ -129,7 +131,7 @@ projects:
owner: example-organization
name: pr-bot
`))
Load(os.TempDir())
Load(c)
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
@ -152,8 +154,8 @@ projects:
func TestLoadSonarQubeStructureInjectedEnvs(t *testing.T) {
os.Setenv("PRBOT_SONARQUBE_WEBHOOK_SECRET", "injected-webhook-secret")
os.Setenv("PRBOT_SONARQUBE_TOKEN_VALUE", "injected-token")
WriteConfigFile(t, defaultConfig)
Load(os.TempDir())
c := WriteConfigFile(t, defaultConfig)
Load(c)
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
@ -186,7 +188,7 @@ func TestLoadStructureWithFileReferenceResolving(t *testing.T) {
sonarqubeTokenFile := path.Join(os.TempDir(), "token-secret-sonarqube")
_ = ioutil.WriteFile(sonarqubeTokenFile, []byte(`a09eb5785b25bb2cbacf48808a677a0709f02d8e`), 0444)
WriteConfigFile(t, []byte(
c := WriteConfigFile(t, []byte(
`gitea:
url: https://example.com/gitea
token:
@ -232,7 +234,7 @@ projects:
AdditionalMetrics: []string{},
}
Load(os.TempDir())
Load(c)
assert.EqualValues(t, expectedGitea, Gitea)
assert.EqualValues(t, expectedSonarQube, SonarQube)
@ -249,8 +251,8 @@ projects:
}
func TestLoadProjectsStructure(t *testing.T) {
WriteConfigFile(t, defaultConfig)
Load(os.TempDir())
c := WriteConfigFile(t, defaultConfig)
Load(c)
expectedProjects := []Project{
{
@ -283,7 +285,7 @@ sonarqube:
secret: haxxor-sonarqube-secret
projects: []
`)
WriteConfigFile(t, invalidConfig)
c := WriteConfigFile(t, invalidConfig)
assert.Panics(t, func() { Load(os.TempDir()) }, "No panic for empty project mapping that is required")
assert.Panics(t, func() { Load(c) }, "No panic for empty project mapping that is required")
}