Skip to content

Commit d3a9679

Browse files
committed
Add support for Git submodules with go-git
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
1 parent 5486321 commit d3a9679

File tree

8 files changed

+114
-19
lines changed

8 files changed

+114
-19
lines changed

api/v1beta1/gitrepository_types.go

+6
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ type GitRepositorySpec struct {
8181
// +kubebuilder:default:=go-git
8282
// +optional
8383
GitImplementation string `json:"gitImplementation,omitempty"`
84+
85+
// When enabled, after the clone is created, initializes all submodules within,
86+
// using their default settings.
87+
// This option is available only when using the 'go-git' GitImplementation.
88+
// +optional
89+
RecurseSubmodules bool `json:"recurseSubmodules,omitempty"`
8490
}
8591

8692
// GitRepositoryRef defines the Git ref used for pull and checkout operations.

config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ spec:
6666
interval:
6767
description: The interval at which to check for repository updates.
6868
type: string
69+
recurseSubmodules:
70+
description: When enabled, after the clone is created, initializes
71+
all submodules within, using their default settings. This option
72+
is available only when using the 'go-git' GitImplementation.
73+
type: boolean
6974
ref:
7075
description: The Git reference to checkout and monitor for changes,
7176
defaults to master branch.

controllers/gitrepository_controller.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,10 @@ func (r *GitRepositoryReconciler) reconcile(ctx context.Context, repository sour
207207
}
208208
}
209209

210-
checkoutStrategy, err := strategy.CheckoutStrategyForRef(repository.Spec.Reference, repository.Spec.GitImplementation)
210+
checkoutStrategy, err := strategy.CheckoutStrategyForRef(
211+
repository.Spec.Reference,
212+
repository.Spec.GitImplementation,
213+
repository.Spec.RecurseSubmodules)
211214
if err != nil {
212215
return sourcev1.GitRepositoryNotReady(repository, sourcev1.GitOperationFailedReason, err.Error()), err
213216
}

docs/api/source.md

+28
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,20 @@ string
400400
Defaults to go-git, valid values are (&lsquo;go-git&rsquo;, &lsquo;libgit2&rsquo;).</p>
401401
</td>
402402
</tr>
403+
<tr>
404+
<td>
405+
<code>recurseSubmodules</code><br>
406+
<em>
407+
bool
408+
</em>
409+
</td>
410+
<td>
411+
<em>(Optional)</em>
412+
<p>When enabled, after the clone is created, initializes all submodules within,
413+
using their default settings.
414+
This option is available only when using the &lsquo;go-git&rsquo; GitImplementation.</p>
415+
</td>
416+
</tr>
403417
</table>
404418
</td>
405419
</tr>
@@ -1246,6 +1260,20 @@ string
12461260
Defaults to go-git, valid values are (&lsquo;go-git&rsquo;, &lsquo;libgit2&rsquo;).</p>
12471261
</td>
12481262
</tr>
1263+
<tr>
1264+
<td>
1265+
<code>recurseSubmodules</code><br>
1266+
<em>
1267+
bool
1268+
</em>
1269+
</td>
1270+
<td>
1271+
<em>(Optional)</em>
1272+
<p>When enabled, after the clone is created, initializes all submodules within,
1273+
using their default settings.
1274+
This option is available only when using the &lsquo;go-git&rsquo; GitImplementation.</p>
1275+
</td>
1276+
</tr>
12491277
</tbody>
12501278
</table>
12511279
</div>

docs/spec/v1beta1/gitrepositories.md

+42
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ type GitRepositoryRef struct {
8080
// The Git commit SHA to checkout, if specified Tag filters will be ignored.
8181
// +optional
8282
Commit string `json:"commit,omitempty"`
83+
84+
// When enabled, after the clone is created, initializes all submodules within,
85+
// using their default settings.
86+
// This option is available only when using the 'go-git' GitImplementation.
87+
// +optional
88+
RecurseSubmodules bool `json:"recurseSubmodules,omitempty"`
8389
}
8490
```
8591

@@ -434,6 +440,42 @@ kubectl create secret generic pgp-public-keys \
434440
--from-file=author2.asc
435441
```
436442

443+
### Git submodules
444+
445+
With `spec.recurseSubmodules` you can configure the controller to
446+
clone a specific branch including its Git submodules:
447+
448+
```yaml
449+
apiVersion: source.toolkit.fluxcd.io/v1beta1
450+
kind: GitRepository
451+
metadata:
452+
name: repo-with-submodules
453+
namespace: default
454+
spec:
455+
interval: 1m
456+
url: https://github.com/<organization>/<repository>
457+
secretRef:
458+
name: https-credentials
459+
ref:
460+
branch: main
461+
recurseSubmodules: true
462+
---
463+
apiVersion: v1
464+
kind: Secret
465+
metadata:
466+
name: https-credentials
467+
namespace: default
468+
type: Opaque
469+
data:
470+
username: <GitHub Username>
471+
password: <GitHub Token>
472+
```
473+
474+
Note that deploy keys can't be used to pull submodules from private repositories
475+
as GitHub and GitLab doesn't allow a deploy key to be reused across repositories.
476+
You have to use either HTTPS token-based authentication, or an SSH key belonging
477+
to a user that has access to the main repository and all its submodules.
478+
437479
## Status examples
438480

439481
Successful sync:

pkg/git/gogit/checkout.go

+25-14
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,30 @@ import (
3333
"github.com/fluxcd/source-controller/pkg/git"
3434
)
3535

36-
func CheckoutStrategyForRef(ref *sourcev1.GitRepositoryRef) git.CheckoutStrategy {
36+
func CheckoutStrategyForRef(ref *sourcev1.GitRepositoryRef, recurseSubmodules bool) git.CheckoutStrategy {
3737
switch {
3838
case ref == nil:
3939
return &CheckoutBranch{branch: git.DefaultBranch}
4040
case ref.SemVer != "":
41-
return &CheckoutSemVer{semVer: ref.SemVer}
41+
return &CheckoutSemVer{semVer: ref.SemVer, recurseSubmodules: recurseSubmodules}
4242
case ref.Tag != "":
43-
return &CheckoutTag{tag: ref.Tag}
43+
return &CheckoutTag{tag: ref.Tag, recurseSubmodules: recurseSubmodules}
4444
case ref.Commit != "":
45-
strategy := &CheckoutCommit{branch: ref.Branch, commit: ref.Commit}
45+
strategy := &CheckoutCommit{branch: ref.Branch, commit: ref.Commit, recurseSubmodules: recurseSubmodules}
4646
if strategy.branch == "" {
4747
strategy.branch = git.DefaultBranch
4848
}
4949
return strategy
5050
case ref.Branch != "":
51-
return &CheckoutBranch{branch: ref.Branch}
51+
return &CheckoutBranch{branch: ref.Branch, recurseSubmodules: recurseSubmodules}
5252
default:
5353
return &CheckoutBranch{branch: git.DefaultBranch}
5454
}
5555
}
5656

5757
type CheckoutBranch struct {
58-
branch string
58+
branch string
59+
recurseSubmodules bool
5960
}
6061

6162
func (c *CheckoutBranch) Checkout(ctx context.Context, path, url string, auth *git.Auth) (git.Commit, string, error) {
@@ -67,7 +68,7 @@ func (c *CheckoutBranch) Checkout(ctx context.Context, path, url string, auth *g
6768
SingleBranch: true,
6869
NoCheckout: false,
6970
Depth: 1,
70-
RecurseSubmodules: extgogit.DefaultSubmoduleRecursionDepth,
71+
RecurseSubmodules: recurseSubmodules(c.recurseSubmodules),
7172
Progress: nil,
7273
Tags: extgogit.NoTags,
7374
CABundle: auth.CABundle,
@@ -87,7 +88,8 @@ func (c *CheckoutBranch) Checkout(ctx context.Context, path, url string, auth *g
8788
}
8889

8990
type CheckoutTag struct {
90-
tag string
91+
tag string
92+
recurseSubmodules bool
9193
}
9294

9395
func (c *CheckoutTag) Checkout(ctx context.Context, path, url string, auth *git.Auth) (git.Commit, string, error) {
@@ -99,7 +101,7 @@ func (c *CheckoutTag) Checkout(ctx context.Context, path, url string, auth *git.
99101
SingleBranch: true,
100102
NoCheckout: false,
101103
Depth: 1,
102-
RecurseSubmodules: extgogit.DefaultSubmoduleRecursionDepth,
104+
RecurseSubmodules: recurseSubmodules(c.recurseSubmodules),
103105
Progress: nil,
104106
Tags: extgogit.NoTags,
105107
CABundle: auth.CABundle,
@@ -119,8 +121,9 @@ func (c *CheckoutTag) Checkout(ctx context.Context, path, url string, auth *git.
119121
}
120122

121123
type CheckoutCommit struct {
122-
branch string
123-
commit string
124+
branch string
125+
commit string
126+
recurseSubmodules bool
124127
}
125128

126129
func (c *CheckoutCommit) Checkout(ctx context.Context, path, url string, auth *git.Auth) (git.Commit, string, error) {
@@ -131,7 +134,7 @@ func (c *CheckoutCommit) Checkout(ctx context.Context, path, url string, auth *g
131134
ReferenceName: plumbing.NewBranchReferenceName(c.branch),
132135
SingleBranch: true,
133136
NoCheckout: false,
134-
RecurseSubmodules: extgogit.DefaultSubmoduleRecursionDepth,
137+
RecurseSubmodules: recurseSubmodules(c.recurseSubmodules),
135138
Progress: nil,
136139
Tags: extgogit.NoTags,
137140
CABundle: auth.CABundle,
@@ -158,7 +161,8 @@ func (c *CheckoutCommit) Checkout(ctx context.Context, path, url string, auth *g
158161
}
159162

160163
type CheckoutSemVer struct {
161-
semVer string
164+
semVer string
165+
recurseSubmodules bool
162166
}
163167

164168
func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *git.Auth) (git.Commit, string, error) {
@@ -173,7 +177,7 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
173177
RemoteName: git.DefaultOrigin,
174178
NoCheckout: false,
175179
Depth: 1,
176-
RecurseSubmodules: extgogit.DefaultSubmoduleRecursionDepth,
180+
RecurseSubmodules: recurseSubmodules(c.recurseSubmodules),
177181
Progress: nil,
178182
Tags: extgogit.AllTags,
179183
CABundle: auth.CABundle,
@@ -262,3 +266,10 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
262266

263267
return &Commit{commit}, fmt.Sprintf("%s/%s", t, head.Hash().String()), nil
264268
}
269+
270+
func recurseSubmodules(recurse bool) extgogit.SubmoduleRescursivity {
271+
if recurse {
272+
return extgogit.DefaultSubmoduleRecursionDepth
273+
}
274+
return extgogit.NoRecurseSubmodules
275+
}

pkg/git/libgit2/checkout.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
"github.com/fluxcd/source-controller/pkg/git"
3030
)
3131

32-
func CheckoutStrategyForRef(ref *sourcev1.GitRepositoryRef) git.CheckoutStrategy {
32+
func CheckoutStrategyForRef(ref *sourcev1.GitRepositoryRef, recurseSubmodules bool) git.CheckoutStrategy {
3333
switch {
3434
case ref == nil:
3535
return &CheckoutBranch{branch: git.DefaultBranch}

pkg/git/strategy/strategy.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ import (
2525
"github.com/fluxcd/source-controller/pkg/git/libgit2"
2626
)
2727

28-
func CheckoutStrategyForRef(ref *sourcev1.GitRepositoryRef, gitImplementation string) (git.CheckoutStrategy, error) {
28+
func CheckoutStrategyForRef(ref *sourcev1.GitRepositoryRef, gitImplementation string, recurseSubmodules bool) (git.CheckoutStrategy, error) {
2929
switch gitImplementation {
3030
case sourcev1.GoGitImplementation:
31-
return gogit.CheckoutStrategyForRef(ref), nil
31+
return gogit.CheckoutStrategyForRef(ref, recurseSubmodules), nil
3232
case sourcev1.LibGit2Implementation:
33-
return libgit2.CheckoutStrategyForRef(ref), nil
33+
return libgit2.CheckoutStrategyForRef(ref, recurseSubmodules), nil
3434
default:
3535
return nil, fmt.Errorf("invalid git implementation %s", gitImplementation)
3636
}

0 commit comments

Comments
 (0)