Skip to content

Commit 8f925c6

Browse files
fix: fetch syncedRevision in UpdateRevisionForPaths (#21014) (cherry-pick #21015) (#22011)
Signed-off-by: toyamagu2021 <[email protected]> Signed-off-by: toyamagu-2021 <[email protected]> Co-authored-by: gussan <[email protected]>
1 parent b5be1df commit 8f925c6

File tree

2 files changed

+121
-7
lines changed

2 files changed

+121
-7
lines changed

reposerver/repository/repository.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,6 +2511,50 @@ func (s *Service) checkoutRevision(gitClient git.Client, revision string, submod
25112511
return closer, err
25122512
}
25132513

2514+
// fetch is a convenience function to fetch revisions
2515+
// We assumed that the caller has already initialized the git repo, i.e. gitClient.Init() has been called
2516+
func (s *Service) fetch(gitClient git.Client, targetRevisions []string) error {
2517+
err := fetch(gitClient, targetRevisions)
2518+
if err != nil {
2519+
for _, revision := range targetRevisions {
2520+
s.metricsServer.IncGitFetchFail(gitClient.Root(), revision)
2521+
}
2522+
}
2523+
return err
2524+
}
2525+
2526+
func fetch(gitClient git.Client, targetRevisions []string) error {
2527+
revisionPresent := true
2528+
for _, revision := range targetRevisions {
2529+
revisionPresent = gitClient.IsRevisionPresent(revision)
2530+
if !revisionPresent {
2531+
break
2532+
}
2533+
}
2534+
// Fetching can be skipped if the revision is already present locally.
2535+
if revisionPresent {
2536+
return nil
2537+
}
2538+
// Fetching with no revision first. Fetching with an explicit version can cause repo bloat. https://github.com/argoproj/argo-cd/issues/8845
2539+
err := gitClient.Fetch("")
2540+
if err != nil {
2541+
return err
2542+
}
2543+
for _, revision := range targetRevisions {
2544+
if !gitClient.IsRevisionPresent(revision) {
2545+
// When fetching with no revision, only refs/heads/* and refs/remotes/origin/* are fetched. If fetch fails
2546+
// for the given revision, try explicitly fetching it.
2547+
log.Infof("Failed to fetch revision %s: %v", revision, err)
2548+
log.Infof("Fallback to fetching specific revision %s. ref might not have been in the default refspec fetched.", revision)
2549+
2550+
if err := gitClient.Fetch(revision); err != nil {
2551+
return status.Errorf(codes.Internal, "Failed to fetch revision %s: %v", revision, err)
2552+
}
2553+
}
2554+
}
2555+
return nil
2556+
}
2557+
25142558
func checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bool) error {
25152559
err := gitClient.Init()
25162560
if err != nil {
@@ -2855,6 +2899,10 @@ func (s *Service) UpdateRevisionForPaths(_ context.Context, request *apiclient.U
28552899
}
28562900
defer io.Close(closer)
28572901

2902+
if err := s.fetch(gitClient, []string{syncedRevision}); err != nil {
2903+
return nil, status.Errorf(codes.Internal, "unable to fetch git repo %s with syncedRevisions %s: %v", repo.Repo, syncedRevision, err)
2904+
}
2905+
28582906
files, err := gitClient.ChangedFiles(syncedRevision, revision)
28592907
if err != nil {
28602908
return nil, status.Errorf(codes.Internal, "unable to get changed files for repo %s with revision %s: %v", repo.Repo, revision, err)

reposerver/repository/repository_test.go

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3090,6 +3090,61 @@ func TestCheckoutRevisionNotPresentCallFetch(t *testing.T) {
30903090
require.NoError(t, err)
30913091
}
30923092

3093+
func TestFetch(t *testing.T) {
3094+
revision1 := "0123456789012345678901234567890123456789"
3095+
revision2 := "abcdefabcdefabcdefabcdefabcdefabcdefabcd"
3096+
3097+
gitClient := &gitmocks.Client{}
3098+
gitClient.On("Init").Return(nil)
3099+
gitClient.On("IsRevisionPresent", revision1).Once().Return(true)
3100+
gitClient.On("IsRevisionPresent", revision2).Once().Return(false)
3101+
gitClient.On("Fetch", "").Return(nil)
3102+
gitClient.On("IsRevisionPresent", revision1).Once().Return(true)
3103+
gitClient.On("IsRevisionPresent", revision2).Once().Return(true)
3104+
3105+
err := fetch(gitClient, []string{revision1, revision2})
3106+
require.NoError(t, err)
3107+
}
3108+
3109+
// TestFetchRevisionCanGetNonstandardRefs shows that we can fetch a revision that points to a non-standard ref. In
3110+
func TestFetchRevisionCanGetNonstandardRefs(t *testing.T) {
3111+
rootPath := t.TempDir()
3112+
3113+
sourceRepoPath, err := os.MkdirTemp(rootPath, "")
3114+
require.NoError(t, err)
3115+
3116+
// Create a repo such that one commit is on a non-standard ref _and nowhere else_. This is meant to simulate, for
3117+
// example, a GitHub ref for a pull into one repo from a fork of that repo.
3118+
runGit(t, sourceRepoPath, "init")
3119+
runGit(t, sourceRepoPath, "checkout", "-b", "main") // make sure there's a main branch to switch back to
3120+
runGit(t, sourceRepoPath, "commit", "-m", "empty", "--allow-empty")
3121+
runGit(t, sourceRepoPath, "checkout", "-b", "branch")
3122+
runGit(t, sourceRepoPath, "commit", "-m", "empty", "--allow-empty")
3123+
sha := runGit(t, sourceRepoPath, "rev-parse", "HEAD")
3124+
runGit(t, sourceRepoPath, "update-ref", "refs/pull/123/head", strings.TrimSuffix(sha, "\n"))
3125+
runGit(t, sourceRepoPath, "checkout", "main")
3126+
runGit(t, sourceRepoPath, "branch", "-D", "branch")
3127+
3128+
destRepoPath, err := os.MkdirTemp(rootPath, "")
3129+
require.NoError(t, err)
3130+
3131+
gitClient, err := git.NewClientExt("file://"+sourceRepoPath, destRepoPath, &git.NopCreds{}, true, false, "", "")
3132+
require.NoError(t, err)
3133+
3134+
// We should initialize repository
3135+
err = gitClient.Init()
3136+
require.NoError(t, err)
3137+
3138+
pullSha, err := gitClient.LsRemote("refs/pull/123/head")
3139+
require.NoError(t, err)
3140+
3141+
err = fetch(gitClient, []string{"does-not-exist"})
3142+
require.Error(t, err)
3143+
3144+
err = fetch(gitClient, []string{pullSha})
3145+
require.NoError(t, err)
3146+
}
3147+
30933148
// runGit runs a git command in the given working directory. If the command succeeds, it returns the combined standard
30943149
// and error output. If it fails, it stops the test with a failure message.
30953150
func runGit(t *testing.T, workDir string, args ...string) string {
@@ -3765,9 +3820,13 @@ func TestUpdateRevisionForPaths(t *testing.T) {
37653820
{name: "ChangedFilesDoNothing", fields: func() fields {
37663821
s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) {
37673822
gitClient.On("Init").Return(nil)
3768-
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
3769-
gitClient.On("Fetch", mock.Anything).Return(nil)
3770-
gitClient.On("Checkout", mock.Anything, mock.Anything).Return("", nil)
3823+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
3824+
gitClient.On("IsRevisionPresent", "632039659e542ed7de0c170a4fcc1c571b288fc0").Once().Return(false)
3825+
gitClient.On("Checkout", "632039659e542ed7de0c170a4fcc1c571b288fc0", mock.Anything).Once().Return("", nil)
3826+
// fetch
3827+
gitClient.On("IsRevisionPresent", "1e67a504d03def3a6a1125d934cb511680f72555").Once().Return(false)
3828+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
3829+
gitClient.On("IsRevisionPresent", "1e67a504d03def3a6a1125d934cb511680f72555").Once().Return(true)
37713830
gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
37723831
gitClient.On("LsRemote", "SYNCEDHEAD").Once().Return("1e67a504d03def3a6a1125d934cb511680f72555", nil)
37733832
paths.On("GetPath", mock.Anything).Return(".", nil)
@@ -3794,9 +3853,13 @@ func TestUpdateRevisionForPaths(t *testing.T) {
37943853
{name: "NoChangesUpdateCache", fields: func() fields {
37953854
s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) {
37963855
gitClient.On("Init").Return(nil)
3797-
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
3798-
gitClient.On("Fetch", mock.Anything).Return(nil)
3856+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
3857+
gitClient.On("IsRevisionPresent", "632039659e542ed7de0c170a4fcc1c571b288fc0").Once().Return(false)
37993858
gitClient.On("Checkout", mock.Anything, mock.Anything).Return("", nil)
3859+
gitClient.On("IsRevisionPresent", "1e67a504d03def3a6a1125d934cb511680f72555").Once().Return(false)
3860+
// fetch
3861+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
3862+
gitClient.On("IsRevisionPresent", "1e67a504d03def3a6a1125d934cb511680f72555").Once().Return(true)
38003863
gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
38013864
gitClient.On("LsRemote", "SYNCEDHEAD").Once().Return("1e67a504d03def3a6a1125d934cb511680f72555", nil)
38023865
paths.On("GetPath", mock.Anything).Return(".", nil)
@@ -3832,9 +3895,12 @@ func TestUpdateRevisionForPaths(t *testing.T) {
38323895
{name: "NoChangesHelmMultiSourceUpdateCache", fields: func() fields {
38333896
s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) {
38343897
gitClient.On("Init").Return(nil)
3835-
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
3836-
gitClient.On("Fetch", mock.Anything).Return(nil)
3898+
gitClient.On("IsRevisionPresent", "632039659e542ed7de0c170a4fcc1c571b288fc0").Once().Return(false)
3899+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
38373900
gitClient.On("Checkout", mock.Anything, mock.Anything).Return("", nil)
3901+
// fetch
3902+
gitClient.On("IsRevisionPresent", "1e67a504d03def3a6a1125d934cb511680f72555").Once().Return(true)
3903+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
38383904
gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
38393905
gitClient.On("LsRemote", "SYNCEDHEAD").Once().Return("1e67a504d03def3a6a1125d934cb511680f72555", nil)
38403906
paths.On("GetPath", mock.Anything).Return(".", nil)

0 commit comments

Comments
 (0)