Repository: honeydipper/honeydipper
Pull request #511 (2023-02-12 00:51:54)

feat(core): allow override a repo with a directory

#### Description
Allowing override a repo with a directory is useful when running tests and config checks.

#### This PR fixes the following issues
N/A


Patch:
diff --git a/internal/config/config.go b/internal/config/config.go
index a31e4e4..2f6ea4e 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -12,6 +12,7 @@ import (
 	"bytes"
 	"encoding/gob"
 	"fmt"
+	"os"
 	"strings"
 	"sync"
 	"time"
@@ -79,6 +80,7 @@ type Config struct {
 	Stage         int
 	Staged        *DataSet
 	StageWG       []*sync.WaitGroup
+	Overrides     map[string]string
 }
 
 // ResetStage resets the stage of the config.
@@ -110,9 +112,29 @@ func (c *Config) ResetStage() {
 	c.StageWG[StageServing].Add(len(c.Services))
 }
 
+// loadOverrides from the environment variables.
+func (c *Config) loadOverrides() {
+	if c.Overrides == nil {
+		c.Overrides = map[string]string{}
+		for _, ev := range os.Environ() {
+			k, v, _ := strings.Cut(ev, "=")
+			if k == "REPO_OVERRIDE" || strings.HasPrefix(k, "REPO_OVERRIDE_") {
+				remote, override, found := strings.Cut(v, "=>")
+				remote = strings.TrimSpace(remote)
+				override = strings.TrimSpace(override)
+				if !found || remote == "" || override == "" {
+					dipper.Logger.Panicf("invalid repo override definition: %s", ev)
+				}
+				c.Overrides[remote] = override
+			}
+		}
+	}
+}
+
 // Bootstrap loads the configuration during daemon bootstrap.
 // WorkingDir is where git will clone remote repo into.
 func (c *Config) Bootstrap(wd string) {
+	c.loadOverrides()
 	if !c.IsConfigCheck && !c.IsDocGen {
 		c.ResetStage()
 	}
diff --git a/internal/config/repo.go b/internal/config/repo.go
index a4d8b8c..626fd02 100644
--- a/internal/config/repo.go
+++ b/internal/config/repo.go
@@ -144,15 +144,14 @@ func (c *Repo) cloneFetchRepo() {
 func (c *Repo) loadRepo() {
 	defer c.recovering("", c.repo.Repo)
 
-	if c.parent.IsConfigCheck && *c.repo == c.parent.InitRepo {
-		c.root = dipper.Must(filepath.Abs(c.repo.Repo)).(string)
-		dipper.Logger.Infof("using working copy of repo [%v]", c.root)
-		// uncomment below to ensure the working copy is a repo
-		// if _, err = git.PlainOpen(c.root); err != nil {
-		//   panic(err)
-		// }
-	} else {
+	if override, found := c.parent.Overrides[c.repo.Repo]; found {
+		c.root = dipper.Must(filepath.Abs(override)).(string)
+		dipper.Logger.Infof("using [%s] overriding repo [%v]", c.root, c.repo.Repo)
+	} else if _, err := os.Stat(c.repo.Repo); os.IsNotExist(err) || c.repo.Branch != "" {
 		c.cloneFetchRepo()
+	} else {
+		c.root = dipper.Must(filepath.Abs(c.repo.Repo)).(string)
+		dipper.Logger.Infof("using uncommitted files [%v]", c.root)
 	}
 
 	dipper.Logger.Infof("start loading repo [%v]", c.repo.Repo)
@@ -169,37 +168,38 @@ func (c *Repo) refreshRepo() bool {
 
 	defer c.recovering("", c.repo.Repo)
 
-	var repoObj *git.Repository
-	var err error
 	dipper.Logger.Infof("refreshing repo [%v]", c.repo.Repo)
-	if repoObj, err = git.PlainOpen(c.root); err != nil {
-		panic(err)
-	}
-
-	tree := dipper.Must(repoObj.Worktree()).(*git.Worktree)
+	_, overridden := c.parent.Overrides[c.repo.Repo]
+	_, e := os.Stat(c.repo.Repo)
+	usingUncommitted := e == nil && c.repo.Branch == ""
+
+	if !overridden && !usingUncommitted {
+		repoObj := dipper.Must(git.PlainOpen(c.root)).(*git.Repository)
+		tree := dipper.Must(repoObj.Worktree()).(*git.Worktree)
+		opts := &git.PullOptions{
+			RemoteName: "origin",
+		}
 
-	branch := "master"
-	if c.repo.Branch != "" {
-		branch = c.repo.Branch
-	}
+		switch c.repo.Branch {
+		case "":
+			opts.ReferenceName = plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", c.repo.Branch))
+		default:
+			opts.ReferenceName = plumbing.ReferenceName("refs/heads/master")
+		}
 
-	opts := &git.PullOptions{
-		RemoteName:    "origin",
-		ReferenceName: plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", branch)),
-	}
-	if strings.HasPrefix(c.repo.Repo, "git@") {
-		if auth := GetGitSSHAuth(c.repo.KeyFile, c.repo.KeyPassEnv); auth != nil {
-			opts.Auth = auth
+		if strings.HasPrefix(c.repo.Repo, "git@") {
+			if auth := GetGitSSHAuth(c.repo.KeyFile, c.repo.KeyPassEnv); auth != nil {
+				opts.Auth = auth
+			}
 		}
-	}
 
-	err = tree.Pull(opts)
-	if errors.Is(err, git.NoErrAlreadyUpToDate) {
-		dipper.Logger.Infof("no changes skip repo [%s]", c.repo.Repo)
+		err := tree.Pull(opts)
+		if errors.Is(err, git.NoErrAlreadyUpToDate) {
+			dipper.Logger.Infof("no changes skip repo [%s]", c.repo.Repo)
 
-		return false
-	} else if err != nil {
-		panic(err)
+			return false
+		}
+		dipper.Must(err)
 	}
 
 	c.DataSet = DataSet{}


Test patch:
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index e6448be..70871a6 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -82,3 +82,21 @@ func TestRegexParsing(t *testing.T) {
 	assert.Equal(t, "non regex", config.DataSet.Workflows["test-workflow"].UnlessMatch.(map[string]interface{})["key4"], "workflow unless_match non-regex should remain")
 	assert.Equal(t, "non regex", config.DataSet.Rules[0].When.Match["key6"], "rule match non-regex should remain")
 }
+
+func TestLoadValidOverrides(t *testing.T) {
+	config := &Config{}
+	t.Setenv("REPO_OVERRIDE", "https://test.com/foo/bar.git => /tmp/foobar")
+	t.Setenv("REPO_OVERRIDE_ANOTHER", "git@github.com:foo/bar.git => /tmp/foobar")
+
+	assert.NotPanics(t, func() { config.loadOverrides() }, "loadOverride shoud not panic with valid definition")
+	assert.Equal(t, 2, len(config.Overrides), "loadOverride should detect two override definitions")
+	assert.Equal(t, "/tmp/foobar", config.Overrides["https://test.com/foo/bar.git"], "loadOverride should accept REPO_OVERRIDE")
+	assert.Equal(t, "/tmp/foobar", config.Overrides["git@github.com:foo/bar.git"], "loadOverride should accept REPO_OVERRIDE_*")
+}
+
+func TestLoadInvalidOverrides(t *testing.T) {
+	config := &Config{}
+	t.Setenv("REPO_OVERRIDE", "https://test.com/foo/bar.git =>")
+
+	assert.Panics(t, func() { config.loadOverrides() }, "loadOverride shoud panic with invalid definition")
+}