diff --git a/frontend/dockerfile/builder/build.go b/frontend/dockerfile/builder/build.go index fbd4c42b0a93..84e45eff9e09 100644 --- a/frontend/dockerfile/builder/build.go +++ b/frontend/dockerfile/builder/build.go @@ -49,6 +49,7 @@ const ( keyNameContext = "contextkey" keyNameDockerfile = "dockerfilekey" keyContextSubDir = "contextsubdir" + keyContextKeepGitDir = "build-arg:BUILDKIT_CONTEXT_KEEP_GIT_DIR" ) var httpPrefix = regexp.MustCompile(`^https?://`) @@ -129,7 +130,7 @@ func Build(ctx context.Context, c client.Client) (*client.Result, error) { var buildContext *llb.State isScratchContext := false - if st, ok := detectGitContext(opts[localNameContext]); ok { + if st, ok := detectGitContext(opts[localNameContext], opts[keyContextKeepGitDir]); ok { if !forceLocalDockerfile { src = *st } @@ -451,12 +452,19 @@ func filter(opt map[string]string, key string) map[string]string { return m } -func detectGitContext(ref string) (*llb.State, bool) { +func detectGitContext(ref, gitContext string) (*llb.State, bool) { found := false if httpPrefix.MatchString(ref) && gitUrlPathWithFragmentSuffix.MatchString(ref) { found = true } + keepGit := false + if gitContext != "" { + if v, err := strconv.ParseBool(gitContext); err == nil { + keepGit = v + } + } + for _, prefix := range []string{"git://", "github.com/", "git@"} { if strings.HasPrefix(ref, prefix) { found = true @@ -472,7 +480,12 @@ func detectGitContext(ref string) (*llb.State, bool) { if len(parts) > 1 { branch = parts[1] } - st := llb.Git(parts[0], branch, dockerfile2llb.WithInternalName("load git source "+ref)) + gitOpts := []llb.GitOption{dockerfile2llb.WithInternalName("load git source " + ref)} + if keepGit { + gitOpts = append(gitOpts, llb.KeepGitDir()) + } + + st := llb.Git(parts[0], branch, gitOpts...) return &st, true } diff --git a/source/git/gitsource.go b/source/git/gitsource.go index b2a3eacfe4b7..d2d6ff76179e 100644 --- a/source/git/gitsource.go +++ b/source/git/gitsource.go @@ -153,6 +153,14 @@ type gitSourceHandler struct { cacheKey string } +func (gs *gitSourceHandler) shaToCacheKey(sha string) string { + key := sha + if gs.src.KeepGitDir { + key += ".git" + } + return key +} + func (gs *gitSource) Resolve(ctx context.Context, id source.Identifier, _ *session.Manager) (source.SourceInstance, error) { gitIdentifier, ok := id.(*source.GitIdentifier) if !ok { @@ -175,6 +183,7 @@ func (gs *gitSourceHandler) CacheKey(ctx context.Context, index int) (string, bo defer gs.locker.Unlock(remote) if isCommitSHA(ref) { + ref = gs.shaToCacheKey(ref) gs.cacheKey = ref return ref, true, nil } @@ -201,6 +210,7 @@ func (gs *gitSourceHandler) CacheKey(ctx context.Context, index int) (string, bo if !isCommitSHA(sha) { return "", false, errors.Errorf("invalid commit sha %q", sha) } + sha = gs.shaToCacheKey(sha) gs.cacheKey = sha return sha, true, nil } @@ -298,11 +308,15 @@ func (gs *gitSourceHandler) Snapshot(ctx context.Context) (out cache.ImmutableRe }() if gs.src.KeepGitDir { - _, err = gitWithinDir(ctx, checkoutDir, "", "init") + checkoutDirGit := filepath.Join(checkoutDir, ".git") + if err := os.MkdirAll(checkoutDir, 0711); err != nil { + return nil, err + } + _, err = gitWithinDir(ctx, checkoutDirGit, "", "init") if err != nil { return nil, err } - _, err = gitWithinDir(ctx, checkoutDir, "", "remote", "add", "origin", gitDir) + _, err = gitWithinDir(ctx, checkoutDirGit, "", "remote", "add", "origin", gitDir) if err != nil { return nil, err } @@ -313,16 +327,18 @@ func (gs *gitSourceHandler) Snapshot(ctx context.Context) (out cache.ImmutableRe if err != nil { return nil, err } + } else { + pullref += ":" + pullref } - _, err = gitWithinDir(ctx, checkoutDir, "", "fetch", "--depth=1", "origin", pullref) + _, err = gitWithinDir(ctx, checkoutDirGit, "", "fetch", "-u", "--depth=1", "origin", pullref) if err != nil { return nil, err } - _, err = gitWithinDir(ctx, checkoutDir, checkoutDir, "checkout", "FETCH_HEAD") + _, err = gitWithinDir(ctx, checkoutDirGit, checkoutDir, "checkout", "FETCH_HEAD") if err != nil { return nil, errors.Wrapf(err, "failed to checkout remote %s", gs.src.Remote) } - gitDir = checkoutDir + gitDir = checkoutDirGit } else { _, err = gitWithinDir(ctx, gitDir, checkoutDir, "checkout", ref, "--", ".") if err != nil { diff --git a/source/git/gitsource_test.go b/source/git/gitsource_test.go index 5bcaac72cd7a..f5cf1ac7bf42 100644 --- a/source/git/gitsource_test.go +++ b/source/git/gitsource_test.go @@ -53,7 +53,12 @@ func testRepeatedFetch(t *testing.T, keepGitDir bool) { require.NoError(t, err) require.True(t, done) - require.Equal(t, 40, len(key1)) + expLen := 40 + if keepGitDir { + expLen += 4 + } + + require.Equal(t, expLen, len(key1)) ref1, err := g.Snapshot(ctx) require.NoError(t, err) @@ -171,7 +176,12 @@ func testFetchBySHA(t *testing.T, keepGitDir bool) { require.NoError(t, err) require.True(t, done) - require.Equal(t, 40, len(key1)) + expLen := 40 + if keepGitDir { + expLen += 4 + } + + require.Equal(t, expLen, len(key1)) ref1, err := g.Snapshot(ctx) require.NoError(t, err) @@ -244,13 +254,18 @@ func testMultipleRepos(t *testing.T, keepGitDir bool) { g2, err := gs.Resolve(ctx, id2, nil) require.NoError(t, err) + expLen := 40 + if keepGitDir { + expLen += 4 + } + key1, _, err := g.CacheKey(ctx, 0) require.NoError(t, err) - require.Equal(t, 40, len(key1)) + require.Equal(t, expLen, len(key1)) key2, _, err := g2.CacheKey(ctx, 0) require.NoError(t, err) - require.Equal(t, 40, len(key2)) + require.Equal(t, expLen, len(key2)) require.NotEqual(t, key1, key2)