@@ -590,6 +590,16 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
590590 ctrl .LoggerFrom (ctx ).V (logger .DebugLevel ).Info ("git repository checked out" , "url" , obj .Spec .URL , "revision" , commitReference (obj , commit ))
591591 conditions .Delete (obj , sourcev1 .FetchFailedCondition )
592592
593+ // Validate sparse checkout paths after successful checkout.
594+ if err := r .validateSparseCheckoutPaths (ctx , obj , dir ); err != nil {
595+ e := serror .NewStalling (
596+ fmt .Errorf ("failed to sparse checkout directories : %w" , err ),
597+ sourcev1 .GitOperationFailedReason ,
598+ )
599+ conditions .MarkTrue (obj , sourcev1 .FetchFailedCondition , e .Reason , "%s" , e )
600+ return sreconcile .ResultEmpty , e
601+ }
602+
593603 // Verify commit signature
594604 if result , err := r .verifySignature (ctx , obj , * commit ); err != nil || result == sreconcile .ResultEmpty {
595605 return result , err
@@ -812,6 +822,7 @@ func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context, sp *pat
812822 obj .Status .ObservedIgnore = obj .Spec .Ignore
813823 obj .Status .ObservedRecurseSubmodules = obj .Spec .RecurseSubmodules
814824 obj .Status .ObservedInclude = obj .Spec .Include
825+ obj .Status .ObservedSparseCheckout = obj .Spec .SparseCheckout
815826
816827 // Remove the deprecated symlink.
817828 // TODO(hidde): remove 2 minor versions from introduction of v1.
@@ -884,6 +895,7 @@ func (r *GitRepositoryReconciler) reconcileInclude(ctx context.Context, sp *patc
884895// performs a git checkout.
885896func (r * GitRepositoryReconciler ) gitCheckout (ctx context.Context , obj * sourcev1.GitRepository ,
886897 authOpts * git.AuthOptions , proxyOpts * transport.ProxyOptions , dir string , optimized bool ) (* git.Commit , error ) {
898+
887899 // Configure checkout strategy.
888900 cloneOpts := repository.CloneConfig {
889901 RecurseSubmodules : obj .Spec .RecurseSubmodules ,
@@ -896,7 +908,14 @@ func (r *GitRepositoryReconciler) gitCheckout(ctx context.Context, obj *sourcev1
896908 cloneOpts .SemVer = ref .SemVer
897909 cloneOpts .RefName = ref .Name
898910 }
899-
911+ if obj .Spec .SparseCheckout != nil {
912+ // Trim any leading "./" in the directory paths since underlying go-git API does not honor them.
913+ sparseCheckoutDirs := make ([]string , len (obj .Spec .SparseCheckout ))
914+ for i , path := range obj .Spec .SparseCheckout {
915+ sparseCheckoutDirs [i ] = strings .TrimPrefix (path , "./" )
916+ }
917+ cloneOpts .SparseCheckoutDirectories = sparseCheckoutDirs
918+ }
900919 // Only if the object has an existing artifact in storage, attempt to
901920 // short-circuit clone operation. reconcileStorage has already verified
902921 // that the artifact exists.
@@ -1172,6 +1191,14 @@ func gitContentConfigChanged(obj *sourcev1.GitRepository, includes *artifactSet)
11721191 if requiresVerification (obj ) {
11731192 return true
11741193 }
1194+ if len (obj .Spec .SparseCheckout ) != len (obj .Status .ObservedSparseCheckout ) {
1195+ return true
1196+ }
1197+ for index , dir := range obj .Spec .SparseCheckout {
1198+ if dir != obj .Status .ObservedSparseCheckout [index ] {
1199+ return true
1200+ }
1201+ }
11751202
11761203 // Convert artifactSet to index addressable artifacts and ensure that it and
11771204 // the included artifacts include all the include from the spec.
@@ -1206,6 +1233,19 @@ func gitContentConfigChanged(obj *sourcev1.GitRepository, includes *artifactSet)
12061233 return false
12071234}
12081235
1236+ // validateSparseCheckoutPaths checks if the sparse checkout paths exist in the cloned repository.
1237+ func (r * GitRepositoryReconciler ) validateSparseCheckoutPaths (ctx context.Context , obj * sourcev1.GitRepository , dir string ) error {
1238+ if obj .Spec .SparseCheckout != nil {
1239+ for _ , path := range obj .Spec .SparseCheckout {
1240+ fullPath := filepath .Join (dir , path )
1241+ if _ , err := os .Lstat (fullPath ); err != nil {
1242+ return fmt .Errorf ("sparse checkout dir '%s' does not exist in repository: %w" , path , err )
1243+ }
1244+ }
1245+ }
1246+ return nil
1247+ }
1248+
12091249// Returns true if both GitRepositoryIncludes are equal.
12101250func gitRepositoryIncludeEqual (a , b sourcev1.GitRepositoryInclude ) bool {
12111251 if a .GitRepositoryRef != b .GitRepositoryRef {
0 commit comments