Skip to content

Commit

Permalink
feat(reposerver): Skip calling git fetch if commit to checkout exists…
Browse files Browse the repository at this point in the history
… locally (argoproj#18657)

* Skip fetch if revision to check out exists locally

Signed-off-by: Shady Rafehi <shady@canva.com>

* Test reposerver/repository.checkoutRevision

Signed-off-by: Shady Rafehi <shady@canva.com>

* Test client.IsRevisionPresent

Signed-off-by: Shady Rafehi <shady@canva.com>

* Signoff

Signed-off-by: Shady Rafehi <shady@canva.com>

* Update reposerver/repository/repository.go

Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Shady Rafehi <shady@canva.com>

---------

Signed-off-by: Shady Rafehi <shady@canva.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
  • Loading branch information
2 people authored and Hariharasuthan99 committed Jun 16, 2024
1 parent c62060f commit d028d50
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 4 deletions.
17 changes: 13 additions & 4 deletions reposerver/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -2428,10 +2428,19 @@ func checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bo
return status.Errorf(codes.Internal, "Failed to initialize git repo: %v", err)
}

// Fetching with no revision first. Fetching with an explicit version can cause repo bloat. https://github.com/argoproj/argo-cd/issues/8845
err = gitClient.Fetch("")
if err != nil {
return status.Errorf(codes.Internal, "Failed to fetch default: %v", err)
revisionPresent := gitClient.IsRevisionPresent(revision)

log.WithFields(map[string]interface{}{
"skipFetch": revisionPresent,
}).Debugf("Checking out revision %v", revision)

// Fetching can be skipped if the revision is already present locally.
if !revisionPresent {
// Fetching with no revision first. Fetching with an explicit version can cause repo bloat. https://github.com/argoproj/argo-cd/issues/8845
err = gitClient.Fetch("")
if err != nil {
return status.Errorf(codes.Internal, "Failed to fetch default: %v", err)
}
}

err = gitClient.Checkout(revision, submoduleEnabled)
Expand Down
33 changes: 33 additions & 0 deletions reposerver/repository/repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func newServiceWithMocks(t *testing.T, root string, signed bool) (*Service, *git
}
return newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) {
gitClient.On("Init").Return(nil)
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
gitClient.On("Fetch", mock.Anything).Return(nil)
gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil)
gitClient.On("LsRemote", mock.Anything).Return(mock.Anything, nil)
Expand Down Expand Up @@ -177,6 +178,7 @@ func newServiceWithCommitSHA(t *testing.T, root, revision string) *Service {

service, gitClient, _ := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) {
gitClient.On("Init").Return(nil)
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
gitClient.On("Fetch", mock.Anything).Return(nil)
gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil)
gitClient.On("LsRemote", revision).Return(revision, revisionErr)
Expand Down Expand Up @@ -2984,6 +2986,31 @@ func TestCheckoutRevisionCanGetNonstandardRefs(t *testing.T) {
assert.NoError(t, err)
}

func TestCheckoutRevisionPresentSkipFetch(t *testing.T) {
revision := "0123456789012345678901234567890123456789"

gitClient := &gitmocks.Client{}
gitClient.On("Init").Return(nil)
gitClient.On("IsRevisionPresent", revision).Return(true)
gitClient.On("Checkout", revision, mock.Anything).Return(nil)

err := checkoutRevision(gitClient, revision, false)
assert.NoError(t, err)
}

func TestCheckoutRevisionNotPresentCallFetch(t *testing.T) {
revision := "0123456789012345678901234567890123456789"

gitClient := &gitmocks.Client{}
gitClient.On("Init").Return(nil)
gitClient.On("IsRevisionPresent", revision).Return(false)
gitClient.On("Fetch", "").Return(nil)
gitClient.On("Checkout", revision, mock.Anything).Return(nil)

err := checkoutRevision(gitClient, revision, false)
assert.NoError(t, err)
}

// runGit runs a git command in the given working directory. If the command succeeds, it returns the combined standard
// and error output. If it fails, it stops the test with a failure message.
func runGit(t *testing.T, workDir string, args ...string) string {
Expand Down Expand Up @@ -3355,6 +3382,7 @@ func TestGetGitDirectories(t *testing.T) {
root := "./testdata/git-files-dirs"
s, _, cacheMocks := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) {
gitClient.On("Init").Return(nil)
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
gitClient.On("Fetch", mock.Anything).Return(nil)
gitClient.On("Checkout", mock.Anything, mock.Anything).Once().Return(nil)
gitClient.On("LsRemote", "HEAD").Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
Expand Down Expand Up @@ -3387,6 +3415,7 @@ func TestGetGitDirectoriesWithHiddenDirSupported(t *testing.T) {
root := "./testdata/git-files-dirs"
s, _, cacheMocks := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) {
gitClient.On("Init").Return(nil)
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
gitClient.On("Fetch", mock.Anything).Return(nil)
gitClient.On("Checkout", mock.Anything, mock.Anything).Once().Return(nil)
gitClient.On("LsRemote", "HEAD").Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
Expand Down Expand Up @@ -3480,6 +3509,7 @@ func TestGetGitFiles(t *testing.T) {
root := ""
s, _, cacheMocks := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) {
gitClient.On("Init").Return(nil)
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
gitClient.On("Fetch", mock.Anything).Return(nil)
gitClient.On("Checkout", mock.Anything, mock.Anything).Once().Return(nil)
gitClient.On("LsRemote", "HEAD").Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
Expand Down Expand Up @@ -3653,6 +3683,7 @@ func TestUpdateRevisionForPaths(t *testing.T) {
{name: "ChangedFilesDoNothing", fields: func() fields {
s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) {
gitClient.On("Init").Return(nil)
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
gitClient.On("Fetch", mock.Anything).Return(nil)
gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil)
gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
Expand All @@ -3678,6 +3709,7 @@ func TestUpdateRevisionForPaths(t *testing.T) {
{name: "NoChangesUpdateCache", fields: func() fields {
s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) {
gitClient.On("Init").Return(nil)
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
gitClient.On("Fetch", mock.Anything).Return(nil)
gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil)
gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
Expand Down Expand Up @@ -3713,6 +3745,7 @@ func TestUpdateRevisionForPaths(t *testing.T) {
{name: "NoChangesHelmMultiSourceUpdateCache", fields: func() fields {
s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) {
gitClient.On("Init").Return(nil)
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
gitClient.On("Fetch", mock.Anything).Return(nil)
gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil)
gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
Expand Down
16 changes: 16 additions & 0 deletions util/git/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ type Client interface {
VerifyCommitSignature(string) (string, error)
IsAnnotatedTag(string) bool
ChangedFiles(revision string, targetRevision string) ([]string, error)
IsRevisionPresent(revision string) bool
}

type EventHandlers struct {
Expand Down Expand Up @@ -357,6 +358,21 @@ func (m *nativeGitClient) fetch(revision string) error {
return err
}

// IsRevisionPresent checks to see if the given revision already exists locally.
func (m *nativeGitClient) IsRevisionPresent(revision string) bool {
if revision == "" {
return false
}

cmd := exec.Command("git", "cat-file", "-t", revision)
out, err := m.runCmdOutput(cmd, runOpts{SkipErrorLogging: true})
if out == "commit" && err == nil {
return true
} else {
return false
}
}

// Fetch fetches latest updates from origin
func (m *nativeGitClient) Fetch(revision string) error {
if m.OnFetch != nil {
Expand Down
35 changes: 35 additions & 0 deletions util/git/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,38 @@ func TestNewClient_invalidSSHURL(t *testing.T) {
assert.Nil(t, client)
assert.ErrorIs(t, err, ErrInvalidRepoURL)
}

func Test_IsRevisionPresent(t *testing.T) {
tempDir := t.TempDir()

client, err := NewClientExt(fmt.Sprintf("file://%s", tempDir), tempDir, NopCreds{}, true, false, "")
require.NoError(t, err)

err = client.Init()
require.NoError(t, err)

p := path.Join(client.Root(), "README")
f, err := os.Create(p)
require.NoError(t, err)
_, err = f.WriteString("Hello.")
require.NoError(t, err)
err = f.Close()
require.NoError(t, err)

err = runCmd(client.Root(), "git", "add", "README")
require.NoError(t, err)

err = runCmd(client.Root(), "git", "commit", "-m", "Initial Commit", "-a")
require.NoError(t, err)

commitSHA, err := client.LsRemote("HEAD")
require.NoError(t, err)

// Ensure revision for HEAD is present locally.
revisionPresent := client.IsRevisionPresent(commitSHA)
assert.True(t, revisionPresent)

// Ensure invalid revision is not returned.
revisionPresent = client.IsRevisionPresent("invalid-revision")
assert.False(t, revisionPresent)
}
18 changes: 18 additions & 0 deletions util/git/mocks/Client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d028d50

Please sign in to comment.