github.com/argoproj/argo-cd/v3@v3.2.1/applicationset/services/scm_provider/utils.go (about)

     1  package scm_provider
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"regexp"
     7  	"strings"
     8  
     9  	argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    10  )
    11  
    12  func compileFilters(filters []argoprojiov1alpha1.SCMProviderGeneratorFilter) ([]*Filter, error) {
    13  	outFilters := make([]*Filter, 0, len(filters))
    14  	for _, filter := range filters {
    15  		outFilter := &Filter{}
    16  		var err error
    17  		if filter.RepositoryMatch != nil {
    18  			outFilter.RepositoryMatch, err = regexp.Compile(*filter.RepositoryMatch)
    19  			if err != nil {
    20  				return nil, fmt.Errorf("error compiling RepositoryMatch regexp %q: %w", *filter.RepositoryMatch, err)
    21  			}
    22  			outFilter.FilterType = FilterTypeRepo
    23  		}
    24  		if filter.LabelMatch != nil {
    25  			outFilter.LabelMatch, err = regexp.Compile(*filter.LabelMatch)
    26  			if err != nil {
    27  				return nil, fmt.Errorf("error compiling LabelMatch regexp %q: %w", *filter.LabelMatch, err)
    28  			}
    29  			outFilter.FilterType = FilterTypeRepo
    30  		}
    31  		if filter.PathsExist != nil {
    32  			outFilter.PathsExist = filter.PathsExist
    33  			outFilter.FilterType = FilterTypeBranch
    34  		}
    35  		if filter.PathsDoNotExist != nil {
    36  			outFilter.PathsDoNotExist = filter.PathsDoNotExist
    37  			outFilter.FilterType = FilterTypeBranch
    38  		}
    39  		if filter.BranchMatch != nil {
    40  			outFilter.BranchMatch, err = regexp.Compile(*filter.BranchMatch)
    41  			if err != nil {
    42  				return nil, fmt.Errorf("error compiling BranchMatch regexp %q: %w", *filter.BranchMatch, err)
    43  			}
    44  			outFilter.FilterType = FilterTypeBranch
    45  		}
    46  		outFilters = append(outFilters, outFilter)
    47  	}
    48  	return outFilters, nil
    49  }
    50  
    51  func matchFilter(ctx context.Context, provider SCMProviderService, repo *Repository, filter *Filter) (bool, error) {
    52  	if filter.RepositoryMatch != nil && !filter.RepositoryMatch.MatchString(repo.Repository) {
    53  		return false, nil
    54  	}
    55  
    56  	if filter.BranchMatch != nil && !filter.BranchMatch.MatchString(repo.Branch) {
    57  		return false, nil
    58  	}
    59  
    60  	if filter.LabelMatch != nil {
    61  		found := false
    62  		for _, label := range repo.Labels {
    63  			if filter.LabelMatch.MatchString(label) {
    64  				found = true
    65  				break
    66  			}
    67  		}
    68  		if !found {
    69  			return false, nil
    70  		}
    71  	}
    72  
    73  	if len(filter.PathsExist) != 0 {
    74  		for _, path := range filter.PathsExist {
    75  			path = strings.TrimRight(path, "/")
    76  			hasPath, err := provider.RepoHasPath(ctx, repo, path)
    77  			if err != nil {
    78  				return false, err
    79  			}
    80  			if !hasPath {
    81  				return false, nil
    82  			}
    83  		}
    84  	}
    85  	if len(filter.PathsDoNotExist) != 0 {
    86  		for _, path := range filter.PathsDoNotExist {
    87  			path = strings.TrimRight(path, "/")
    88  			hasPath, err := provider.RepoHasPath(ctx, repo, path)
    89  			if err != nil {
    90  				return false, err
    91  			}
    92  			if hasPath {
    93  				return false, nil
    94  			}
    95  		}
    96  	}
    97  
    98  	return true, nil
    99  }
   100  
   101  func ListRepos(ctx context.Context, provider SCMProviderService, filters []argoprojiov1alpha1.SCMProviderGeneratorFilter, cloneProtocol string) ([]*Repository, error) {
   102  	compiledFilters, err := compileFilters(filters)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	repos, err := provider.ListRepos(ctx, cloneProtocol)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	repoFilters := getApplicableFilters(compiledFilters)[FilterTypeRepo]
   111  	if len(repoFilters) == 0 {
   112  		repos, err := getBranches(ctx, provider, repos, compiledFilters)
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  		return repos, nil
   117  	}
   118  	filteredRepos := make([]*Repository, 0, len(repos))
   119  	for _, repo := range repos {
   120  		for _, filter := range repoFilters {
   121  			matches, err := matchFilter(ctx, provider, repo, filter)
   122  			if err != nil {
   123  				return nil, err
   124  			}
   125  			if matches {
   126  				filteredRepos = append(filteredRepos, repo)
   127  				break
   128  			}
   129  		}
   130  	}
   131  
   132  	repos, err = getBranches(ctx, provider, filteredRepos, compiledFilters)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	return repos, nil
   137  }
   138  
   139  func getBranches(ctx context.Context, provider SCMProviderService, repos []*Repository, compiledFilters []*Filter) ([]*Repository, error) {
   140  	reposWithBranches := []*Repository{}
   141  	for _, repo := range repos {
   142  		reposFilled, err := provider.GetBranches(ctx, repo)
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  		reposWithBranches = append(reposWithBranches, reposFilled...)
   147  	}
   148  	branchFilters := getApplicableFilters(compiledFilters)[FilterTypeBranch]
   149  	if len(branchFilters) == 0 {
   150  		return reposWithBranches, nil
   151  	}
   152  	filteredRepos := make([]*Repository, 0, len(reposWithBranches))
   153  	for _, repo := range reposWithBranches {
   154  		for _, filter := range branchFilters {
   155  			matches, err := matchFilter(ctx, provider, repo, filter)
   156  			if err != nil {
   157  				return nil, err
   158  			}
   159  			if matches {
   160  				filteredRepos = append(filteredRepos, repo)
   161  				break
   162  			}
   163  		}
   164  	}
   165  	return filteredRepos, nil
   166  }
   167  
   168  // getApplicableFilters returns a map of filters separated by type.
   169  func getApplicableFilters(filters []*Filter) map[FilterType][]*Filter {
   170  	filterMap := map[FilterType][]*Filter{
   171  		FilterTypeBranch: {},
   172  		FilterTypeRepo:   {},
   173  	}
   174  	for _, filter := range filters {
   175  		switch filter.FilterType {
   176  		case FilterTypeBranch:
   177  			filterMap[FilterTypeBranch] = append(filterMap[FilterTypeBranch], filter)
   178  		case FilterTypeRepo:
   179  			filterMap[FilterTypeRepo] = append(filterMap[FilterTypeRepo], filter)
   180  		}
   181  	}
   182  	return filterMap
   183  }