Skip to content

Commit 49a4b7f

Browse files
fix: fetch syncedRevision in UpdateRevisionForPaths (argoproj#21014) (argoproj#21015)
Signed-off-by: toyamagu2021 <toyamagu2021@gmail.com> Signed-off-by: toyamagu-2021 <toyamagu2021@gmail.com>
1 parent 644af54 commit 49a4b7f

2 files changed

Lines changed: 121 additions & 7 deletions

File tree

reposerver/repository/repository.go

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

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

2894+
if err := s.fetch(gitClient, []string{syncedRevision}); err != nil {
2895+
return nil, status.Errorf(codes.Internal, "unable to fetch git repo %s with syncedRevisions %s: %v", repo.Repo, syncedRevision, err)
2896+
}
2897+
28502898
files, err := gitClient.ChangedFiles(syncedRevision, revision)
28512899
if err != nil {
28522900
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
@@ -3071,6 +3071,61 @@ func TestCheckoutRevisionNotPresentCallFetch(t *testing.T) {
30713071
require.NoError(t, err)
30723072
}
30733073

3074+
func TestFetch(t *testing.T) {
3075+
revision1 := "0123456789012345678901234567890123456789"
3076+
revision2 := "abcdefabcdefabcdefabcdefabcdefabcdefabcd"
3077+
3078+
gitClient := &gitmocks.Client{}
3079+
gitClient.On("Init").Return(nil)
3080+
gitClient.On("IsRevisionPresent", revision1).Once().Return(true)
3081+
gitClient.On("IsRevisionPresent", revision2).Once().Return(false)
3082+
gitClient.On("Fetch", "").Return(nil)
3083+
gitClient.On("IsRevisionPresent", revision1).Once().Return(true)
3084+
gitClient.On("IsRevisionPresent", revision2).Once().Return(true)
3085+
3086+
err := fetch(gitClient, []string{revision1, revision2})
3087+
require.NoError(t, err)
3088+
}
3089+
3090+
// TestFetchRevisionCanGetNonstandardRefs shows that we can fetch a revision that points to a non-standard ref. In
3091+
func TestFetchRevisionCanGetNonstandardRefs(t *testing.T) {
3092+
rootPath := t.TempDir()
3093+
3094+
sourceRepoPath, err := os.MkdirTemp(rootPath, "")
3095+
require.NoError(t, err)
3096+
3097+
// Create a repo such that one commit is on a non-standard ref _and nowhere else_. This is meant to simulate, for
3098+
// example, a GitHub ref for a pull into one repo from a fork of that repo.
3099+
runGit(t, sourceRepoPath, "init")
3100+
runGit(t, sourceRepoPath, "checkout", "-b", "main") // make sure there's a main branch to switch back to
3101+
runGit(t, sourceRepoPath, "commit", "-m", "empty", "--allow-empty")
3102+
runGit(t, sourceRepoPath, "checkout", "-b", "branch")
3103+
runGit(t, sourceRepoPath, "commit", "-m", "empty", "--allow-empty")
3104+
sha := runGit(t, sourceRepoPath, "rev-parse", "HEAD")
3105+
runGit(t, sourceRepoPath, "update-ref", "refs/pull/123/head", strings.TrimSuffix(sha, "\n"))
3106+
runGit(t, sourceRepoPath, "checkout", "main")
3107+
runGit(t, sourceRepoPath, "branch", "-D", "branch")
3108+
3109+
destRepoPath, err := os.MkdirTemp(rootPath, "")
3110+
require.NoError(t, err)
3111+
3112+
gitClient, err := git.NewClientExt("file://"+sourceRepoPath, destRepoPath, &git.NopCreds{}, true, false, "", "")
3113+
require.NoError(t, err)
3114+
3115+
// We should initialize repository
3116+
err = gitClient.Init()
3117+
require.NoError(t, err)
3118+
3119+
pullSha, err := gitClient.LsRemote("refs/pull/123/head")
3120+
require.NoError(t, err)
3121+
3122+
err = fetch(gitClient, []string{"does-not-exist"})
3123+
require.Error(t, err)
3124+
3125+
err = fetch(gitClient, []string{pullSha})
3126+
require.NoError(t, err)
3127+
}
3128+
30743129
// runGit runs a git command in the given working directory. If the command succeeds, it returns the combined standard
30753130
// and error output. If it fails, it stops the test with a failure message.
30763131
func runGit(t *testing.T, workDir string, args ...string) string {
@@ -3746,9 +3801,13 @@ func TestUpdateRevisionForPaths(t *testing.T) {
37463801
{name: "ChangedFilesDoNothing", fields: func() fields {
37473802
s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, _ *helmmocks.Client, paths *iomocks.TempPaths) {
37483803
gitClient.On("Init").Return(nil)
3749-
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
3750-
gitClient.On("Fetch", mock.Anything).Return(nil)
3751-
gitClient.On("Checkout", mock.Anything, mock.Anything).Return("", nil)
3804+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
3805+
gitClient.On("IsRevisionPresent", "632039659e542ed7de0c170a4fcc1c571b288fc0").Once().Return(false)
3806+
gitClient.On("Checkout", "632039659e542ed7de0c170a4fcc1c571b288fc0", mock.Anything).Once().Return("", nil)
3807+
// fetch
3808+
gitClient.On("IsRevisionPresent", "1e67a504d03def3a6a1125d934cb511680f72555").Once().Return(false)
3809+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
3810+
gitClient.On("IsRevisionPresent", "1e67a504d03def3a6a1125d934cb511680f72555").Once().Return(true)
37523811
gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
37533812
gitClient.On("LsRemote", "SYNCEDHEAD").Once().Return("1e67a504d03def3a6a1125d934cb511680f72555", nil)
37543813
paths.On("GetPath", mock.Anything).Return(".", nil)
@@ -3775,9 +3834,13 @@ func TestUpdateRevisionForPaths(t *testing.T) {
37753834
{name: "NoChangesUpdateCache", fields: func() fields {
37763835
s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, _ *helmmocks.Client, paths *iomocks.TempPaths) {
37773836
gitClient.On("Init").Return(nil)
3778-
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
3779-
gitClient.On("Fetch", mock.Anything).Return(nil)
3837+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
3838+
gitClient.On("IsRevisionPresent", "632039659e542ed7de0c170a4fcc1c571b288fc0").Once().Return(false)
37803839
gitClient.On("Checkout", mock.Anything, mock.Anything).Return("", nil)
3840+
gitClient.On("IsRevisionPresent", "1e67a504d03def3a6a1125d934cb511680f72555").Once().Return(false)
3841+
// fetch
3842+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
3843+
gitClient.On("IsRevisionPresent", "1e67a504d03def3a6a1125d934cb511680f72555").Once().Return(true)
37813844
gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
37823845
gitClient.On("LsRemote", "SYNCEDHEAD").Once().Return("1e67a504d03def3a6a1125d934cb511680f72555", nil)
37833846
paths.On("GetPath", mock.Anything).Return(".", nil)
@@ -3813,9 +3876,12 @@ func TestUpdateRevisionForPaths(t *testing.T) {
38133876
{name: "NoChangesHelmMultiSourceUpdateCache", fields: func() fields {
38143877
s, _, c := newServiceWithOpt(t, func(gitClient *gitmocks.Client, _ *helmmocks.Client, paths *iomocks.TempPaths) {
38153878
gitClient.On("Init").Return(nil)
3816-
gitClient.On("IsRevisionPresent", mock.Anything).Return(false)
3817-
gitClient.On("Fetch", mock.Anything).Return(nil)
3879+
gitClient.On("IsRevisionPresent", "632039659e542ed7de0c170a4fcc1c571b288fc0").Once().Return(false)
3880+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
38183881
gitClient.On("Checkout", mock.Anything, mock.Anything).Return("", nil)
3882+
// fetch
3883+
gitClient.On("IsRevisionPresent", "1e67a504d03def3a6a1125d934cb511680f72555").Once().Return(true)
3884+
gitClient.On("Fetch", mock.Anything).Once().Return(nil)
38193885
gitClient.On("LsRemote", "HEAD").Once().Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil)
38203886
gitClient.On("LsRemote", "SYNCEDHEAD").Once().Return("1e67a504d03def3a6a1125d934cb511680f72555", nil)
38213887
paths.On("GetPath", mock.Anything).Return(".", nil)

0 commit comments

Comments
 (0)