github.com/argoproj/argo-cd/v3@v3.2.1/reposerver/repository/utils.go (about)

     1  package repository
     2  
     3  import (
     4  	"path/filepath"
     5  	"strings"
     6  
     7  	securejoin "github.com/cyphar/filepath-securejoin"
     8  	log "github.com/sirupsen/logrus"
     9  
    10  	"github.com/argoproj/argo-cd/v3/reposerver/apiclient"
    11  	"github.com/argoproj/argo-cd/v3/util/io/files"
    12  )
    13  
    14  // getApplicationRootPath returns the common root path (shortest shared structure between all paths) among a
    15  // set of application-related paths for manifest generation. AppPath is the lower possible value
    16  func getApplicationRootPath(q *apiclient.ManifestRequest, appPath, repoPath string) string {
    17  	paths := getPaths(q, appPath, repoPath)
    18  
    19  	if len(paths) == 0 {
    20  		// backward compatibility, by default the root path is the repoPath
    21  		return repoPath
    22  	}
    23  
    24  	// the app path must be the lower possible value
    25  	commonParts := strings.Split(appPath, string(filepath.Separator))
    26  
    27  	var disjoint bool
    28  	for _, path := range paths {
    29  		parts := strings.Split(path, string(filepath.Separator))
    30  		// find the minimum length between the current common parts and the current path
    31  		minLen := func(a, b int) int {
    32  			if a < b {
    33  				return a
    34  			}
    35  			return b
    36  		}(len(commonParts), len(parts))
    37  
    38  		// check if diverge /disjoint in some point
    39  		for i := 0; i < minLen; i++ {
    40  			if commonParts[i] != parts[i] {
    41  				commonParts = commonParts[:i]
    42  				disjoint = true
    43  				break
    44  			}
    45  		}
    46  
    47  		// for non-disjoint paths
    48  		if !disjoint && minLen < len(commonParts) {
    49  			commonParts = commonParts[:minLen]
    50  		}
    51  	}
    52  	return string(filepath.Separator) + filepath.Join(commonParts...)
    53  }
    54  
    55  // getPaths retrieves all absolute paths associated with the generation of application manifests.
    56  func getPaths(q *apiclient.ManifestRequest, appPath, repoPath string) []string {
    57  	var paths []string
    58  	for _, annotationPath := range strings.Split(q.AnnotationManifestGeneratePaths, ";") {
    59  		if annotationPath == "" {
    60  			continue
    61  		}
    62  		var err error
    63  		var path, unsafePath string
    64  
    65  		if filepath.IsAbs(annotationPath) {
    66  			unsafePath = filepath.Clean(annotationPath)
    67  		} else {
    68  			appRelPath, err := files.RelativePath(appPath, repoPath)
    69  			if err != nil {
    70  				log.Errorf("error building app relative path: %v", err)
    71  				continue
    72  			}
    73  			unsafePath = filepath.Clean(filepath.Join(appRelPath, annotationPath))
    74  		}
    75  
    76  		path, err = securejoin.SecureJoin(repoPath, unsafePath)
    77  		if err != nil {
    78  			log.Errorf("error joining repoPath %q and absolute unsafePath %q: %v", repoPath, unsafePath, err)
    79  			continue
    80  		}
    81  
    82  		paths = append(paths, path)
    83  	}
    84  	return paths
    85  }