Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions image/git-init/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type FetchSpec struct {
HTTPSProxy string
NOProxy string
SparseCheckoutDirectories string
LFS bool
}

// Fetch fetches the specified git repository at the revision into path, using the refspec to fetch if provided.
Expand Down Expand Up @@ -203,6 +204,14 @@ func Fetch(logger *zap.SugaredLogger, spec FetchSpec) error {
return err
}
}
// Pull Git LFS objects last, after the working tree (and any submodules)
// are checked out, so the LFS-tracked files are materialised into real
// content instead of being left as LFS pointer files.
if spec.LFS {
if err := lfsFetch(logger, spec); err != nil {
return err
}
}
return nil
}

Expand Down Expand Up @@ -240,6 +249,34 @@ func submoduleFetch(logger *zap.SugaredLogger, spec FetchSpec) error {
return nil
}

// lfsFetch downloads Git LFS objects for the checked-out revision and replaces
// the LFS pointer files in the working tree with their real content. It runs
// after the normal checkout so the repository, remote (origin) and credentials
// configured by Fetch are reused verbatim — LFS traffic goes to the same remote
// with the same auth and proxy settings. Requires the git-lfs binary to be
// present in the image; when absent the underlying git command fails loudly
// instead of silently leaving pointer files behind.
func lfsFetch(logger *zap.SugaredLogger, spec FetchSpec) error {
if spec.Path != "" {
if err := os.Chdir(spec.Path); err != nil {
return fmt.Errorf("failed to change directory with path %s; err: %w", spec.Path, err)
}
}
// Install the LFS smudge/process filters into this repository's local git
// config only (never --global / --system): the checkout is ephemeral and
// runs as a non-root user, so per-repo scope is both sufficient and safe.
if _, err := run(logger, "", "lfs", "install", "--local"); err != nil {
return fmt.Errorf("failed to install git-lfs (is the git-lfs binary available in the image?): %w", err)
}
// Fetch all LFS objects referenced by the current checkout and smudge them
// into the working tree.
if _, err := run(logger, "", "lfs", "pull"); err != nil {
return fmt.Errorf("failed to pull git-lfs objects: %w", err)
}
logger.Infof("Successfully pulled git-lfs objects in path %s", spec.Path)
return nil
}

// ensureHomeEnv works around an issue where ssh doesn't respect the HOME env variable. If HOME is set and
// different from the user's detected home directory then symlink .ssh from the home directory to the HOME env
// var. This way ssh will see the .ssh directory in the user's home directory even though it ignores
Expand Down
1 change: 1 addition & 0 deletions image/git-init/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func init() {
flag.BoolVar(&fetchSpec.SSLVerify, "sslVerify", true, "Enable/Disable SSL verification in the git config")
flag.BoolVar(&fetchSpec.Submodules, "submodules", true, "Initialize and fetch Git submodules")
flag.UintVar(&fetchSpec.Depth, "depth", 1, "Perform a shallow clone to this depth")
flag.BoolVar(&fetchSpec.LFS, "lfs", false, "Pull Git LFS objects after checkout (requires git-lfs in the image)")
flag.StringVar(&terminationMessagePath, "terminationMessagePath", "/tekton/termination", "Location of file containing termination message")
flag.StringVar(&fetchSpec.SparseCheckoutDirectories, "sparseCheckoutDirectories", "", "String of directory patterns separated by a comma")
}
Expand Down
Loading