diff --git a/pkg/controller/git/bare_repo.go b/pkg/controller/git/bare_repo.go index bc8b1248ee..bb4d99e070 100644 --- a/pkg/controller/git/bare_repo.go +++ b/pkg/controller/git/bare_repo.go @@ -7,7 +7,6 @@ import ( "os" "path/filepath" "slices" - "strings" libExec "github.com/akuity/kargo/pkg/exec" ) @@ -69,11 +68,12 @@ type BareCloneOptions struct { // should be ignored when cloning the repository. The setting will be // remembered for subsequent interactions with the remote repository. InsecureSkipTLSVerify bool - // Filter specifies a partial clone filter (e.g., "blob:none"). When combined - // with sparse checkout, this avoids downloading blobs for directories that - // won't be checked out, significantly reducing clone time and disk usage for - // large repositories. - Filter string + // Blobless enables blobless cloning (--filter=blob:none). When set, the + // initial clone downloads all commits and trees but defers blob downloads + // until checkout. Combine with sparse checkout to minimise disk usage on + // large repositories. The server must support partial clones; if it does + // not, the clone will fail. + Blobless bool } // CloneBare produces a local, bare clone of the remote Git repository at the @@ -117,14 +117,14 @@ func CloneBare( return b, nil } -func (b *bareRepo) clone(_ *BareCloneOptions) error { +func (b *bareRepo) clone(opts *BareCloneOptions) error { + if opts == nil { + opts = &BareCloneOptions{} + } args := []string{"clone", "--bare"} - // NOTE(hidde): Temporarily disabled until we figure out why this can result - // in "could not fetch from promisor remote" errors. - // - // if opts.Filter != "" { - // args = append(args, "--filter", opts.Filter) - // } + if opts.Blobless { + args = append(args, "--filter", "blob:none") + } args = append(args, b.accessURL, b.dir) cmd := b.buildGitCommand(args...) cmd.Dir = b.homeDir // Override the cmd.Dir that's set by r.buildGitCommand() @@ -133,7 +133,6 @@ func (b *bareRepo) clone(_ *BareCloneOptions) error { } return nil } - type LoadBareRepoOptions struct { Credentials *RepoCredentials } diff --git a/pkg/controller/git/repo.go b/pkg/controller/git/repo.go index 5e20422899..db0e5cd498 100644 --- a/pkg/controller/git/repo.go +++ b/pkg/controller/git/repo.go @@ -45,16 +45,12 @@ type CloneOptions struct { // Depth is the number of commits to fetch from the remote repository. If // zero, all commits will be fetched. This option is ignored if Bare is true. Depth uint - // Filter allows for partially cloning the repository by specifying a - // filter. When a filter is specified, the server will only send a - // subset of reachable objects according to a given object filter. - // - // For more information, see: - // - https://git-scm.com/docs/git-clone#Documentation/git-clone.txt-code--filtercodeemltfilter-specgtem - // - https://git-scm.com/docs/git-rev-list#Documentation/git-rev-list.txt---filterltfilter-specgt - // - https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/ - // - https://docs.gitlab.com/ee/topics/git/partial_clone.html - Filter string + // Blobless enables blobless cloning (--filter=blob:none). When set, the + // initial clone downloads all commits and trees but defers blob downloads + // until checkout. Combine with sparse checkout to minimise disk usage on + // large repositories. The server must support partial clones; if it does + // not, the clone will fail. + Blobless bool // SingleBranch indicates whether the clone should be a single-branch clone. // This option is ignored if Bare is true. SingleBranch bool @@ -119,12 +115,9 @@ func (r *repo) clone(opts *CloneOptions) error { if opts.Depth > 0 { args = append(args, "--depth", fmt.Sprint(opts.Depth)) } - // NOTE(hidde): Temporarily disabled until we figure out why this can result - // in "could not fetch from promisor remote" errors. - // - // if opts.Filter != "" { - // args = append(args, "--filter", opts.Filter) - // } + if opts.Blobless { + args = append(args, "--filter", "blob:none") + } args = append(args, r.accessURL, r.dir) cmd := r.buildGitCommand(args...) cmd.Dir = r.homeDir // Override the cmd.Dir that's set by r.buildGitCommand() @@ -133,7 +126,6 @@ func (r *repo) clone(opts *CloneOptions) error { } return nil } - type LoadRepoOptions struct { Credentials *RepoCredentials } diff --git a/pkg/promotion/runner/builtin/git_cloner.go b/pkg/promotion/runner/builtin/git_cloner.go index 2b62b171a7..1f1a5133d0 100644 --- a/pkg/promotion/runner/builtin/git_cloner.go +++ b/pkg/promotion/runner/builtin/git_cloner.go @@ -41,6 +41,21 @@ type gitCloner struct { schemaLoader gojsonschema.JSONLoader } +// gitUserFromEnv populates a git.User struct from environment variables. +func gitUserFromEnv() git.User { + cfg := struct { + Name string `envconfig:"GITCLIENT_NAME"` + Email string `envconfig:"GITCLIENT_EMAIL"` + SigningKeyType string `envconfig:"GITCLIENT_SIGNING_KEY_TYPE"` + SigningKeyPath string `envconfig:"GITCLIENT_SIGNING_KEY_PATH"` + }{} + envconfig.MustProcess("", &cfg) + return git.User{ + Name: cfg.Name, + Email: cfg.Email, + SigningKeyType: git.SigningKeyType(cfg.SigningKeyType), + SigningKeyPath: cfg.SigningKeyPath, + } // filterForCheckouts returns the clone filter to use based on checkout // configurations. Returns git.FilterBlobless if all checkouts specify sparse // patterns, returns empty string otherwise to avoid on-demand blob fetches for @@ -148,7 +163,7 @@ func (g *gitCloner) run( }, &git.BareCloneOptions{ BaseDir: stepCtx.WorkDir, - Filter: filterForCheckouts(cfg.Checkout), + Blobless: cfg.Blobless, }, ) if err != nil { diff --git a/pkg/promotion/runner/builtin/schemas/git-clone-config.json b/pkg/promotion/runner/builtin/schemas/git-clone-config.json index f92a0e672c..b27de897a1 100644 --- a/pkg/promotion/runner/builtin/schemas/git-clone-config.json +++ b/pkg/promotion/runner/builtin/schemas/git-clone-config.json @@ -13,6 +13,10 @@ "type": "boolean", "description": "Indicates whether to recursively clone submodules. Default is false. Note that any provided credentials must also be valid for the submodules." }, + "blobless": { + "type": "boolean", + "description": "Indicates whether to perform a blobless (--filter=blob:none) clone. Default is false." + }, "repoURL": { "type": "string", "description": "The URL of a remote Git repository to clone. Required. Deprecated: Support for SSH URLs (ssh:// and SCP-style git@host:path) is deprecated as of v1.10.0 and will be removed in v1.13.0. Use HTTPS URLs instead.", diff --git a/pkg/x/promotion/runner/builtin/zz_config_types.go b/pkg/x/promotion/runner/builtin/zz_config_types.go index 23556b3a0c..93287c7067 100644 --- a/pkg/x/promotion/runner/builtin/zz_config_types.go +++ b/pkg/x/promotion/runner/builtin/zz_config_types.go @@ -170,6 +170,8 @@ type GitCloneConfig struct { // provided, this overrides any system-level defaults. Note: Configuration of the // `git-commit` and `git-tag` steps can override this information. Author *GitCloneConfigAuthor `json:"author,omitempty"` + // Indicates whether to perform a blobless (--filter=blob:none) clone. Default is false. + Blobless bool `json:"blobless,omitempty"` // The commits, branches, or tags to check out from the repository and the paths where they // should be checked out. At least one must be specified. Checkout []Checkout `json:"checkout"`