diff --git a/util/git/client.go b/util/git/client.go index 283a470ae073d..43bfdde556062 100644 --- a/util/git/client.go +++ b/util/git/client.go @@ -58,6 +58,7 @@ type Client interface { Root() string Init() error Fetch(revision string) error + Submodule() error Checkout(revision string, submoduleEnabled bool) error LsRefs() (*Refs, error) LsRemote(revision string) (string, error) @@ -392,6 +393,17 @@ func (m *nativeGitClient) LsLargeFiles() ([]string, error) { return ss, nil } +// Submodule embed other repositories into this repository +func (m *nativeGitClient) Submodule() error { + if err := m.runCredentialedCmd("git", "submodule", "sync", "--recursive"); err != nil { + return err + } + if err := m.runCredentialedCmd("git", "submodule", "update", "--init", "--recursive"); err != nil { + return err + } + return nil +} + // Checkout checkout specified revision func (m *nativeGitClient) Checkout(revision string, submoduleEnabled bool) error { if revision == "" || revision == "HEAD" { @@ -415,7 +427,7 @@ func (m *nativeGitClient) Checkout(revision string, submoduleEnabled bool) error } if _, err := os.Stat(m.root + "/.gitmodules"); !os.IsNotExist(err) { if submoduleEnabled { - if err := m.runCredentialedCmd("git", "submodule", "update", "--init", "--recursive"); err != nil { + if err := m.Submodule(); err != nil { return err } } diff --git a/util/git/client_test.go b/util/git/client_test.go index 4addc82aa4413..ce3f05cb0d0d5 100644 --- a/util/git/client_test.go +++ b/util/git/client_test.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -92,3 +93,117 @@ func Test_nativeGitClient_Fetch_Prune(t *testing.T) { err = client.Fetch("") assert.NoError(t, err) } + +func Test_nativeGitClient_Submodule(t *testing.T) { + tempDir, err := os.MkdirTemp("", "") + require.NoError(t, err) + + foo := filepath.Join(tempDir, "foo") + err = os.Mkdir(foo, 0755) + require.NoError(t, err) + + cmd := exec.Command("git", "init") + cmd.Dir = foo + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + require.NoError(t, err) + + bar := filepath.Join(tempDir, "bar") + err = os.Mkdir(bar, 0755) + require.NoError(t, err) + + cmd = exec.Command("git", "init") + cmd.Dir = bar + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + require.NoError(t, err) + + cmd = exec.Command("git", "commit", "-m", "Initial commit", "--allow-empty") + cmd.Dir = bar + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + require.NoError(t, err) + + // Embed repository bar into repository foo + cmd = exec.Command("git", "submodule", "add", bar) + cmd.Dir = foo + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + require.NoError(t, err) + + cmd = exec.Command("git", "commit", "-m", "Initial commit") + cmd.Dir = foo + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + require.NoError(t, err) + + tempDir, err = os.MkdirTemp("", "") + require.NoError(t, err) + + // Clone foo + cmd = exec.Command("git", "clone", foo) + cmd.Dir = tempDir + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + require.NoError(t, err) + + client, err := NewClient(fmt.Sprintf("file://%s", foo), NopCreds{}, true, false, "") + require.NoError(t, err) + + err = client.Init() + require.NoError(t, err) + + err = client.Fetch("") + assert.NoError(t, err) + + commitSHA, err := client.LsRemote("HEAD") + assert.NoError(t, err) + + // Call Checkout() with submoduleEnabled=false. + err = client.Checkout(commitSHA, false) + assert.NoError(t, err) + + // Check if submodule url does not exist in .git/config + cmd = exec.Command("git", "config", "submodule.bar.url") + cmd.Dir = client.Root() + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + assert.Error(t, err) + + // Call Submodule() via Checkout() with submoduleEnabled=true. + err = client.Checkout(commitSHA, true) + assert.NoError(t, err) + + // Check if the .gitmodule URL is reflected in .git/config + cmd = exec.Command("git", "config", "submodule.bar.url") + cmd.Dir = client.Root() + result, err := cmd.Output() + assert.NoError(t, err) + assert.Equal(t, bar+"\n", string(result)) + + // Change URL of submodule bar + cmd = exec.Command("git", "config", "--file=.gitmodules", "submodule.bar.url", bar+"baz") + cmd.Dir = client.Root() + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + require.NoError(t, err) + + // Call Submodule() + err = client.Submodule() + assert.NoError(t, err) + + // Check if the URL change in .gitmodule is reflected in .git/config + cmd = exec.Command("git", "config", "submodule.bar.url") + cmd.Dir = client.Root() + result, err = cmd.Output() + assert.NoError(t, err) + assert.Equal(t, bar+"baz\n", string(result)) +} diff --git a/util/git/mocks/Client.go b/util/git/mocks/Client.go index c1624df0617a8..3d4471b1f23c1 100644 --- a/util/git/mocks/Client.go +++ b/util/git/mocks/Client.go @@ -3,9 +3,8 @@ package mocks import ( - mock "github.com/stretchr/testify/mock" - git "github.com/argoproj/argo-cd/v2/util/git" + mock "github.com/stretchr/testify/mock" ) // Client is an autogenerated mock type for the Client type @@ -203,6 +202,20 @@ func (_m *Client) Root() string { return r0 } +// Submodule provides a mock function with given fields: +func (_m *Client) Submodule() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + // VerifyCommitSignature provides a mock function with given fields: _a0 func (_m *Client) VerifyCommitSignature(_a0 string) (string, error) { ret := _m.Called(_a0)