gitutil: migrate to BuildKit GitCLI API

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax
2026-04-09 14:57:38 +02:00
parent 3124b3c839
commit 720f91fdd4
10 changed files with 135 additions and 157 deletions
+8 -6
View File
@@ -12,6 +12,7 @@ import (
"github.com/docker/buildx/util/gitutil" "github.com/docker/buildx/util/gitutil"
"github.com/docker/buildx/util/osutil" "github.com/docker/buildx/util/osutil"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
bkgitutil "github.com/moby/buildkit/util/gitutil"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1" ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -61,7 +62,7 @@ func getGitAttributes(ctx context.Context, contextPath, dockerfilePath string) (
} }
wd = osutil.SanitizePath(wd) wd = osutil.SanitizePath(wd)
gitc, err := gitutil.New(gitutil.WithContext(ctx), gitutil.WithWorkingDir(wd)) gitc, err := gitutil.New(bkgitutil.WithDir(wd))
if err != nil { if err != nil {
if st, err1 := os.Stat(path.Join(wd, ".git")); err1 == nil && st.IsDir() { if st, err1 := os.Stat(path.Join(wd, ".git")); err1 == nil && st.IsDir() {
return nil, errors.Wrap(err, "git was not found in the system") return nil, errors.Wrap(err, "git was not found in the system")
@@ -69,21 +70,22 @@ func getGitAttributes(ctx context.Context, contextPath, dockerfilePath string) (
return nil, nil return nil, nil
} }
if !gitc.IsInsideWorkTree() { if !gitc.IsInsideWorkTree(ctx) {
if st, err := os.Stat(path.Join(wd, ".git")); err == nil && st.IsDir() { if st, err := os.Stat(path.Join(wd, ".git")); err == nil && st.IsDir() {
return nil, errors.New("failed to read current commit information with git rev-parse --is-inside-work-tree") return nil, errors.New("failed to read current commit information with git rev-parse --is-inside-work-tree")
} }
return nil, nil return nil, nil
} }
root, err := gitc.RootDir() root, err := gitc.WorkTree(ctx)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to get git root dir") return nil, errors.Wrap(err, "failed to get git root dir")
} }
root = osutil.SanitizePath(root)
res := make(map[string]string) res := make(map[string]string)
if sha, err := gitc.FullCommit(); err != nil && !gitutil.IsUnknownRevision(err) { if sha, err := gitc.FullCommit(ctx); err != nil && !gitutil.IsUnknownRevision(err) {
return nil, errors.Wrap(err, "failed to get git commit") return nil, errors.Wrap(err, "failed to get git commit")
} else if sha != "" { } else if sha != "" {
checkDirty := false checkDirty := false
@@ -92,7 +94,7 @@ func getGitAttributes(ctx context.Context, contextPath, dockerfilePath string) (
checkDirty = v checkDirty = v
} }
} }
if checkDirty && gitc.IsDirty() { if checkDirty && gitc.IsDirty(ctx) {
sha += "-dirty" sha += "-dirty"
} }
if setGitLabels { if setGitLabels {
@@ -103,7 +105,7 @@ func getGitAttributes(ctx context.Context, contextPath, dockerfilePath string) (
} }
} }
if rurl, err := gitc.RemoteURL(); err == nil && rurl != "" { if rurl, err := gitc.RemoteURL(ctx); err == nil && rurl != "" {
if setGitLabels { if setGitLabels {
res["label:"+ocispecs.AnnotationSource] = rurl res["label:"+ocispecs.AnnotationSource] = rurl
} }
+3 -2
View File
@@ -20,6 +20,7 @@ import (
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/cli/command/formatter"
"github.com/docker/go-units" "github.com/docker/go-units"
bkgitutil "github.com/moby/buildkit/util/gitutil"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -59,14 +60,14 @@ func runLs(ctx context.Context, dockerCli command.Cli, opts lsOptions) error {
if err != nil { if err != nil {
return err return err
} }
gitc, err := gitutil.New(gitutil.WithContext(ctx), gitutil.WithWorkingDir(wd)) gitc, err := gitutil.New(bkgitutil.WithDir(wd))
if err != nil { if err != nil {
if st, err1 := os.Stat(path.Join(wd, ".git")); err1 == nil && st.IsDir() { if st, err1 := os.Stat(path.Join(wd, ".git")); err1 == nil && st.IsDir() {
return errors.Wrap(err, "git was not found in the system") return errors.Wrap(err, "git was not found in the system")
} }
return errors.Wrapf(err, "could not find git repository for local filter") return errors.Wrapf(err, "could not find git repository for local filter")
} }
remote, err := gitc.RemoteURL() remote, err := gitc.RemoteURL(ctx)
if err != nil { if err != nil {
return errors.Wrapf(err, "could not get remote URL for local filter") return errors.Wrapf(err, "could not get remote URL for local filter")
} }
+15 -14
View File
@@ -23,6 +23,7 @@ import (
"github.com/moby/buildkit/identity" "github.com/moby/buildkit/identity"
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types" provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
"github.com/moby/buildkit/util/contentutil" "github.com/moby/buildkit/util/contentutil"
bkgitutil "github.com/moby/buildkit/util/gitutil"
"github.com/moby/buildkit/util/testutil" "github.com/moby/buildkit/util/testutil"
"github.com/moby/buildkit/util/testutil/integration" "github.com/moby/buildkit/util/testutil/integration"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -551,7 +552,7 @@ COPY super-cool.txt /
fstest.CreateFile("bar/super-cool.txt", []byte("super cool"), 0600), fstest.CreateFile("bar/super-cool.txt", []byte("super cool"), 0600),
) )
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
gittestutil.GitAdd(git, t, "docker-bake.hcl", "bar") gittestutil.GitAdd(git, t, "docker-bake.hcl", "bar")
@@ -688,7 +689,7 @@ EOT
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -718,7 +719,7 @@ EOT
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -765,7 +766,7 @@ EOT
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) git, err := gitutil.New(bkgitutil.WithDir(dirSpec))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -834,7 +835,7 @@ EOT
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) git, err := gitutil.New(bkgitutil.WithDir(dirSpec))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -868,14 +869,14 @@ EOT
) )
dirDest := t.TempDir() dirDest := t.TempDir()
gitSpec, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) gitSpec, err := gitutil.New(bkgitutil.WithDir(dirSpec))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(gitSpec, t) gittestutil.GitInit(gitSpec, t)
gittestutil.GitAdd(gitSpec, t, "docker-bake.hcl") gittestutil.GitAdd(gitSpec, t, "docker-bake.hcl")
gittestutil.GitCommit(gitSpec, t, "initial commit") gittestutil.GitCommit(gitSpec, t, "initial commit")
addrSpec := gittestutil.GitServeHTTP(gitSpec, t) addrSpec := gittestutil.GitServeHTTP(gitSpec, t)
gitSrc, err := gitutil.New(gitutil.WithWorkingDir(dirSrc)) gitSrc, err := gitutil.New(bkgitutil.WithDir(dirSrc))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(gitSrc, t) gittestutil.GitInit(gitSrc, t)
gittestutil.GitAdd(gitSrc, t, "foo") gittestutil.GitAdd(gitSrc, t, "foo")
@@ -909,7 +910,7 @@ COPY super-cool.txt /
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
gittestutil.GitAdd(git, t, "docker-bake.hcl", "bar") gittestutil.GitAdd(git, t, "docker-bake.hcl", "bar")
@@ -948,7 +949,7 @@ COPY --from=files file.txt /file.txt
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
gittestutil.GitAdd(git, t, "docker-bake.hcl", "build", "files-src") gittestutil.GitAdd(git, t, "docker-bake.hcl", "build", "files-src")
@@ -984,7 +985,7 @@ COPY --from=files marker.txt /marker.txt
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
gittestutil.GitAdd(git, t, "docker-bake.hcl", "build", "marker.txt") gittestutil.GitAdd(git, t, "docker-bake.hcl", "build", "marker.txt")
@@ -1023,7 +1024,7 @@ EOT
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) git, err := gitutil.New(bkgitutil.WithDir(dirSpec))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -1071,7 +1072,7 @@ EOT
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) git, err := gitutil.New(bkgitutil.WithDir(dirSpec))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -1127,7 +1128,7 @@ COPY foo /foo
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) git, err := gitutil.New(bkgitutil.WithDir(dirSpec))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -1179,7 +1180,7 @@ COPY foo /foo
fstest.CreateFile("foo", []byte("foo"), 0600), fstest.CreateFile("foo", []byte("foo"), 0600),
) )
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) git, err := gitutil.New(bkgitutil.WithDir(dirSpec))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
+8 -7
View File
@@ -28,6 +28,7 @@ import (
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types" provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
"github.com/moby/buildkit/util/appdefaults" "github.com/moby/buildkit/util/appdefaults"
"github.com/moby/buildkit/util/contentutil" "github.com/moby/buildkit/util/contentutil"
bkgitutil "github.com/moby/buildkit/util/gitutil"
"github.com/moby/buildkit/util/testutil" "github.com/moby/buildkit/util/testutil"
"github.com/moby/buildkit/util/testutil/integration" "github.com/moby/buildkit/util/testutil/integration"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
@@ -130,7 +131,7 @@ COPY foo /foo
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -155,7 +156,7 @@ COPY foo /foo
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -182,7 +183,7 @@ COPY foo /foo
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -215,7 +216,7 @@ COPY foo /foo
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -248,7 +249,7 @@ COPY foo /foo
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -281,7 +282,7 @@ COPY foo /foo
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -403,7 +404,7 @@ COPY foo /foo
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
+3 -2
View File
@@ -12,6 +12,7 @@ import (
"github.com/containerd/continuity/fs/fstest" "github.com/containerd/continuity/fs/fstest"
"github.com/docker/buildx/util/gitutil" "github.com/docker/buildx/util/gitutil"
"github.com/docker/buildx/util/gitutil/gittestutil" "github.com/docker/buildx/util/gitutil/gittestutil"
bkgitutil "github.com/moby/buildkit/util/gitutil"
"github.com/moby/buildkit/util/testutil/integration" "github.com/moby/buildkit/util/testutil/integration"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -196,7 +197,7 @@ COPY foo /foo
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -245,7 +246,7 @@ EOT
) )
dirDest := t.TempDir() dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir)) git, err := gitutil.New(bkgitutil.WithDir(dir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
+11 -6
View File
@@ -3,6 +3,7 @@ package tests
import ( import (
"archive/tar" "archive/tar"
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -20,6 +21,7 @@ import (
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/moby/buildkit/identity" "github.com/moby/buildkit/identity"
"github.com/moby/buildkit/util/contentutil" "github.com/moby/buildkit/util/contentutil"
bkgitutil "github.com/moby/buildkit/util/gitutil"
"github.com/moby/buildkit/util/testutil" "github.com/moby/buildkit/util/testutil"
"github.com/moby/buildkit/util/testutil/httpserver" "github.com/moby/buildkit/util/testutil/httpserver"
"github.com/moby/buildkit/util/testutil/integration" "github.com/moby/buildkit/util/testutil/integration"
@@ -822,7 +824,7 @@ func testBuildPolicyGit(t *testing.T, sb integration.Sandbox) {
require.NoError(t, os.WriteFile(filepath.Join(gitDir, "Dockerfile"), []byte("FROM busybox:latest\nRUN echo git\n"), 0600)) require.NoError(t, os.WriteFile(filepath.Join(gitDir, "Dockerfile"), []byte("FROM busybox:latest\nRUN echo git\n"), 0600))
require.NoError(t, os.WriteFile(filepath.Join(gitDir, "a"), []byte("a"), 0600)) require.NoError(t, os.WriteFile(filepath.Join(gitDir, "a"), []byte("a"), 0600))
git, err := gitutil.New(gitutil.WithWorkingDir(gitDir)) git, err := gitutil.New(bkgitutil.WithDir(gitDir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
@@ -834,15 +836,18 @@ func testBuildPolicyGit(t *testing.T, sb integration.Sandbox) {
require.NoError(t, os.WriteFile(filepath.Join(gitDir, "b"), []byte("b"), 0600)) require.NoError(t, os.WriteFile(filepath.Join(gitDir, "b"), []byte("b"), 0600))
gittestutil.GitAdd(git, t, "b") gittestutil.GitAdd(git, t, "b")
gittestutil.GitCommit(git, t, "b") gittestutil.GitCommit(git, t, "b")
_, err = git.Run("checkout", "-B", "v2") _, err = git.Run(context.TODO(), "checkout", "-B", "v2")
require.NoError(t, err) require.NoError(t, err)
commitHead, err := git.Run("rev-parse", "HEAD") commitHeadB, err := git.Run(context.TODO(), "rev-parse", "HEAD")
require.NoError(t, err) require.NoError(t, err)
commitTag, err := git.Run("rev-parse", "v0.1") commitHead := strings.TrimSpace(string(commitHeadB))
commitTagB, err := git.Run(context.TODO(), "rev-parse", "v0.1")
require.NoError(t, err) require.NoError(t, err)
commitTagCommit, err := git.Run("rev-parse", "v0.1^{commit}") commitTag := strings.TrimSpace(string(commitTagB))
commitTagCommitB, err := git.Run(context.TODO(), "rev-parse", "v0.1^{commit}")
require.NoError(t, err) require.NoError(t, err)
commitTagCommit := strings.TrimSpace(string(commitTagCommitB))
baseURL := gittestutil.GitServeHTTP(git, t) baseURL := gittestutil.GitServeHTTP(git, t)
tagURL := baseURL + "#v0.1" tagURL := baseURL + "#v0.1"
branchURL := baseURL + "#v2" branchURL := baseURL + "#v2"
@@ -1318,7 +1323,7 @@ default allow = true
decision := {"allow": allow} decision := {"allow": allow}
`), 0600)) `), 0600))
git, err := gitutil.New(gitutil.WithWorkingDir(gitDir)) git, err := gitutil.New(bkgitutil.WithDir(gitDir))
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(git, t) gittestutil.GitInit(git, t)
gittestutil.GitAdd(git, t, ".") gittestutil.GitAdd(git, t, ".")
+20 -10
View File
@@ -1,15 +1,17 @@
package gittestutil package gittestutil
import ( import (
"context"
"os" "os"
"strings" "strings"
"testing" "testing"
"github.com/docker/buildx/util/gitutil" "github.com/docker/buildx/util/gitutil"
"github.com/pkg/errors"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func GitInit(c *gitutil.Git, tb testing.TB) { func GitInit(c *gitutil.GitCLI, tb testing.TB) {
tb.Helper() tb.Helper()
out, err := fakeGit(c, "init") out, err := fakeGit(c, "init")
require.NoError(tb, err) require.NoError(tb, err)
@@ -19,48 +21,48 @@ func GitInit(c *gitutil.Git, tb testing.TB) {
_, _ = fakeGit(c, "branch", "-D", "master") _, _ = fakeGit(c, "branch", "-D", "master")
} }
func GitCommit(c *gitutil.Git, tb testing.TB, msg string) { func GitCommit(c *gitutil.GitCLI, tb testing.TB, msg string) {
tb.Helper() tb.Helper()
out, err := fakeGit(c, "commit", "--allow-empty", "-m", msg) out, err := fakeGit(c, "commit", "--allow-empty", "-m", msg)
require.NoError(tb, err) require.NoError(tb, err)
require.Contains(tb, out, "main", msg) require.Contains(tb, out, "main", msg)
} }
func GitTag(c *gitutil.Git, tb testing.TB, tag string) { func GitTag(c *gitutil.GitCLI, tb testing.TB, tag string) {
tb.Helper() tb.Helper()
out, err := fakeGit(c, "tag", tag) out, err := fakeGit(c, "tag", tag)
require.NoError(tb, err) require.NoError(tb, err)
require.Empty(tb, out) require.Empty(tb, out)
} }
func GitTagAnnotated(c *gitutil.Git, tb testing.TB, tag, message string) { func GitTagAnnotated(c *gitutil.GitCLI, tb testing.TB, tag, message string) {
tb.Helper() tb.Helper()
out, err := fakeGit(c, "tag", "-a", tag, "-m", message) out, err := fakeGit(c, "tag", "-a", tag, "-m", message)
require.NoError(tb, err) require.NoError(tb, err)
require.Empty(tb, out) require.Empty(tb, out)
} }
func GitCheckoutBranch(c *gitutil.Git, tb testing.TB, name string) { func GitCheckoutBranch(c *gitutil.GitCLI, tb testing.TB, name string) {
tb.Helper() tb.Helper()
out, err := fakeGit(c, "checkout", "-b", name) out, err := fakeGit(c, "checkout", "-b", name)
require.NoError(tb, err) require.NoError(tb, err)
require.Empty(tb, out) require.Empty(tb, out)
} }
func GitAdd(c *gitutil.Git, tb testing.TB, files ...string) { func GitAdd(c *gitutil.GitCLI, tb testing.TB, files ...string) {
tb.Helper() tb.Helper()
args := append([]string{"add"}, files...) args := append([]string{"add"}, files...)
_, err := fakeGit(c, args...) _, err := fakeGit(c, args...)
require.NoError(tb, err) require.NoError(tb, err)
} }
func GitSetRemote(c *gitutil.Git, tb testing.TB, name string, url string) { func GitSetRemote(c *gitutil.GitCLI, tb testing.TB, name string, url string) {
tb.Helper() tb.Helper()
_, err := fakeGit(c, "remote", "add", name, url) _, err := fakeGit(c, "remote", "add", name, url)
require.NoError(tb, err) require.NoError(tb, err)
} }
func GitSetMainUpstream(c *gitutil.Git, tb testing.TB, remote, target string) { func GitSetMainUpstream(c *gitutil.GitCLI, tb testing.TB, remote, target string) {
tb.Helper() tb.Helper()
_, err := fakeGit(c, "fetch", "--depth", "1", remote, target) _, err := fakeGit(c, "fetch", "--depth", "1", remote, target)
require.NoError(tb, err) require.NoError(tb, err)
@@ -81,7 +83,7 @@ func Mktmp(tb testing.TB) string {
return folder return folder
} }
func fakeGit(c *gitutil.Git, args ...string) (string, error) { func fakeGit(c *gitutil.GitCLI, args ...string) (string, error) {
allArgs := []string{ allArgs := []string{
"-c", "user.name=buildx", "-c", "user.name=buildx",
"-c", "user.email=buildx@docker.com", "-c", "user.email=buildx@docker.com",
@@ -90,7 +92,15 @@ func fakeGit(c *gitutil.Git, args ...string) (string, error) {
"-c", "log.showSignature=false", "-c", "log.showSignature=false",
} }
allArgs = append(allArgs, args...) allArgs = append(allArgs, args...)
return c.Run(allArgs...) return clean(c.Run(context.TODO(), allArgs...))
}
func clean(dt []byte, err error) (string, error) {
out := strings.ReplaceAll(strings.Split(string(dt), "\n")[0], "'", "")
if err != nil {
err = errors.New(strings.TrimSuffix(err.Error(), "\n"))
}
return out, err
} }
func IsAmbiguousArgument(err error) bool { func IsAmbiguousArgument(err error) bool {
+3 -3
View File
@@ -24,7 +24,7 @@ func WithAccessToken(token string) GitServeOpt {
} }
} }
func GitServeHTTP(c *gitutil.Git, t testing.TB, opts ...GitServeOpt) (url string) { func GitServeHTTP(c *gitutil.GitCLI, t testing.TB, opts ...GitServeOpt) (url string) {
t.Helper() t.Helper()
gitUpdateServerInfo(c, t) gitUpdateServerInfo(c, t)
ctx, cancel := context.WithCancelCause(context.TODO()) ctx, cancel := context.WithCancelCause(context.TODO())
@@ -38,7 +38,7 @@ func GitServeHTTP(c *gitutil.Git, t testing.TB, opts ...GitServeOpt) (url string
done := make(chan struct{}) done := make(chan struct{})
name := "test.git" name := "test.git"
dir, err := c.GitDir() dir, err := c.GitDir(context.TODO())
if err != nil { if err != nil {
cancel(err) cancel(err)
} }
@@ -93,7 +93,7 @@ func GitServeHTTP(c *gitutil.Git, t testing.TB, opts ...GitServeOpt) (url string
return fmt.Sprintf("http://%s/%s", addr, name) return fmt.Sprintf("http://%s/%s", addr, name)
} }
func gitUpdateServerInfo(c *gitutil.Git, tb testing.TB) { func gitUpdateServerInfo(c *gitutil.GitCLI, tb testing.TB) {
tb.Helper() tb.Helper()
_, err := fakeGit(c, "update-server-info") _, err := fakeGit(c, "update-server-info")
require.NoError(tb, err) require.NoError(tb, err)
+42 -95
View File
@@ -1,122 +1,98 @@
package gitutil package gitutil
import ( import (
"bytes"
"context" "context"
"net/url" "net/url"
"os"
"os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/docker/buildx/util/osutil" "github.com/docker/buildx/util/osutil"
bkgitutil "github.com/moby/buildkit/util/gitutil"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// Git represents an active git object // GitCLI represents an active git object.
type Git struct { type GitCLI struct {
ctx context.Context bkgitutil.GitCLI
wd string
gitpath string
} }
// Option provides a variadic option for configuring the git client. // New initializes a new git client.
type Option func(b *Git) func New(opts ...bkgitutil.Option) (*GitCLI, error) {
cli := bkgitutil.NewGitCLI(append(opts, bkgitutil.WithHostGitConfig())...)
// WithContext sets context. gitpath, err := gitPath(cli.Dir())
func WithContext(ctx context.Context) Option {
return func(b *Git) {
b.ctx = ctx
}
}
// WithWorkingDir sets working directory.
func WithWorkingDir(wd string) Option {
return func(b *Git) {
b.wd = wd
}
}
// New initializes a new git client
func New(opts ...Option) (*Git, error) {
var err error
c := &Git{
ctx: context.Background(),
}
for _, opt := range opts {
opt(c)
}
c.gitpath, err = gitPath(c.wd)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return c, nil cli = cli.New(
bkgitutil.WithGitBinary(gitpath),
bkgitutil.WithArgs("-c", "log.showSignature=false"),
)
return &GitCLI{*cli}, nil
} }
func (c *Git) IsInsideWorkTree() bool { func (cli *GitCLI) IsInsideWorkTree(ctx context.Context) bool {
out, err := c.Run("rev-parse", "--is-inside-work-tree") out, err := cli.clean(cli.Run(ctx, "rev-parse", "--is-inside-work-tree"))
return out == "true" && err == nil return out == "true" && err == nil
} }
func (c *Git) IsDirty() bool { func (cli *GitCLI) IsDirty(ctx context.Context) bool {
out, err := c.Run("status", "--porcelain", "--ignored") out, err := cli.Run(ctx, "status", "--porcelain", "--ignored")
return strings.TrimSpace(out) != "" || err != nil return strings.TrimSpace(string(out)) != "" || err != nil
} }
func (c *Git) RootDir() (string, error) { func (cli *GitCLI) WorkTree(ctx context.Context) (string, error) {
root, err := c.Run("rev-parse", "--show-toplevel") root, err := cli.GitCLI.WorkTree(ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
return osutil.SanitizePath(root), nil return osutil.SanitizePath(root), nil
} }
func (c *Git) GitDir() (string, error) { func (cli *GitCLI) GitDir(ctx context.Context) (string, error) {
dir, err := c.RootDir() dir, err := cli.WorkTree(ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
return filepath.Join(dir, ".git"), nil return filepath.Join(dir, ".git"), nil
} }
func (c *Git) RemoteURL() (string, error) { func (cli *GitCLI) RemoteURL(ctx context.Context) (string, error) {
// Try default remote based on remote tracking branch // Try default remote based on remote tracking branch.
if remote, err := c.currentRemote(); err == nil && remote != "" { if remote, err := cli.currentRemote(ctx); err == nil && remote != "" {
if ru, err := c.clean(c.run("remote", "get-url", remote)); err == nil && ru != "" { if ru, err := cli.clean(cli.Run(ctx, "remote", "get-url", remote)); err == nil && ru != "" {
return stripCredentials(ru), nil return stripCredentials(ru), nil
} }
} }
// Next try to get the remote URL from the origin remote first // Next try to get the remote URL from the origin remote first.
if ru, err := c.clean(c.run("remote", "get-url", "origin")); err == nil && ru != "" { if ru, err := cli.clean(cli.Run(ctx, "remote", "get-url", "origin")); err == nil && ru != "" {
return stripCredentials(ru), nil return stripCredentials(ru), nil
} }
// If that fails, try to get the remote URL from the upstream remote // If that fails, try to get the remote URL from the upstream remote.
if ru, err := c.clean(c.run("remote", "get-url", "upstream")); err == nil && ru != "" { if ru, err := cli.clean(cli.Run(ctx, "remote", "get-url", "upstream")); err == nil && ru != "" {
return stripCredentials(ru), nil return stripCredentials(ru), nil
} }
return "", errors.New("no remote URL found for either origin or upstream") return "", errors.New("no remote URL found for either origin or upstream")
} }
func (c *Git) FullCommit() (string, error) { func (cli *GitCLI) FullCommit(ctx context.Context) (string, error) {
return c.clean(c.run("show", "--format=%H", "HEAD", "--quiet", "--")) return cli.clean(cli.Run(ctx, "show", "--format=%H", "HEAD", "--quiet", "--"))
} }
func (c *Git) ShortCommit() (string, error) { func (cli *GitCLI) ShortCommit(ctx context.Context) (string, error) {
return c.clean(c.run("show", "--format=%h", "HEAD", "--quiet", "--")) return cli.clean(cli.Run(ctx, "show", "--format=%h", "HEAD", "--quiet", "--"))
} }
func (c *Git) Tag() (string, error) { func (cli *GitCLI) Tag(ctx context.Context) (string, error) {
var tag string var tag string
var err error var err error
for _, fn := range []func() (string, error){ for _, fn := range []func() (string, error){
func() (string, error) { func() (string, error) {
return c.clean(c.run("tag", "--points-at", "HEAD", "--sort", "-version:creatordate")) return cli.clean(cli.Run(ctx, "tag", "--points-at", "HEAD", "--sort", "-version:creatordate"))
}, },
func() (string, error) { func() (string, error) {
return c.clean(c.run("describe", "--tags", "--abbrev=0")) return cli.clean(cli.Run(ctx, "describe", "--tags", "--abbrev=0"))
}, },
} { } {
tag, err = fn() tag, err = fn()
@@ -127,36 +103,8 @@ func (c *Git) Tag() (string, error) {
return tag, err return tag, err
} }
func (c *Git) Run(args ...string) (string, error) { func (cli *GitCLI) clean(dt []byte, err error) (string, error) {
return c.clean(c.run(args...)) out := string(dt)
}
func (c *Git) run(args ...string) (string, error) {
var extraArgs = []string{
"-c", "log.showSignature=false",
}
args = append(extraArgs, args...)
cmd := exec.CommandContext(c.ctx, c.gitpath, args...)
if c.wd != "" {
cmd.Dir = c.wd
}
// Override the locale to ensure consistent output
cmd.Env = append(os.Environ(), "LC_ALL=C")
stdout := bytes.Buffer{}
stderr := bytes.Buffer{}
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return "", errors.New(stderr.String())
}
return stdout.String(), nil
}
func (c *Git) clean(out string, err error) (string, error) {
out = strings.ReplaceAll(strings.Split(out, "\n")[0], "'", "") out = strings.ReplaceAll(strings.Split(out, "\n")[0], "'", "")
if err != nil { if err != nil {
err = errors.New(strings.TrimSuffix(err.Error(), "\n")) err = errors.New(strings.TrimSuffix(err.Error(), "\n"))
@@ -164,16 +112,15 @@ func (c *Git) clean(out string, err error) (string, error) {
return out, err return out, err
} }
func (c *Git) currentRemote() (string, error) { func (cli *GitCLI) currentRemote(ctx context.Context) (string, error) {
symref, err := c.Run("symbolic-ref", "-q", "HEAD") symref, err := cli.clean(cli.Run(ctx, "symbolic-ref", "-q", "HEAD"))
if err != nil { if err != nil {
return "", err return "", err
} }
if symref == "" { if symref == "" {
return "", nil return "", nil
} }
// git for-each-ref --format='%(upstream:remotename)' remote, err := cli.clean(cli.Run(ctx, "for-each-ref", "--format=%(upstream:remotename)", symref))
remote, err := c.Run("for-each-ref", "--format=%(upstream:remotename)", symref)
if err != nil { if err != nil {
return "", err return "", err
} }
+22 -12
View File
@@ -1,6 +1,8 @@
package gitutil_test package gitutil_test
import ( import (
"context"
"strings"
"testing" "testing"
"github.com/docker/buildx/util/gitutil" "github.com/docker/buildx/util/gitutil"
@@ -9,6 +11,7 @@ import (
) )
func TestGit(t *testing.T) { func TestGit(t *testing.T) {
ctx := context.TODO()
gittestutil.Mktmp(t) gittestutil.Mktmp(t)
c, err := gitutil.New() c, err := gitutil.New()
require.NoError(t, err) require.NoError(t, err)
@@ -16,17 +19,18 @@ func TestGit(t *testing.T) {
gittestutil.GitInit(c, t) gittestutil.GitInit(c, t)
gittestutil.GitCommit(c, t, "bar") gittestutil.GitCommit(c, t, "bar")
out, err := c.Run("status") out, err := c.Run(ctx, "status")
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, out) require.NotEmpty(t, out)
out, err = c.Run("not-exist") out, err = c.Run(ctx, "not-exist")
require.Error(t, err) require.Error(t, err)
require.Empty(t, out) require.Empty(t, out)
require.Equal(t, "git: 'not-exist' is not a git command. See 'git --help'.", err.Error()) require.Contains(t, err.Error(), "git: 'not-exist' is not a git command. See 'git --help'.")
} }
func TestGitFullCommit(t *testing.T) { func TestGitFullCommit(t *testing.T) {
ctx := context.TODO()
gittestutil.Mktmp(t) gittestutil.Mktmp(t)
c, err := gitutil.New() c, err := gitutil.New()
require.NoError(t, err) require.NoError(t, err)
@@ -34,12 +38,13 @@ func TestGitFullCommit(t *testing.T) {
gittestutil.GitInit(c, t) gittestutil.GitInit(c, t)
gittestutil.GitCommit(c, t, "bar") gittestutil.GitCommit(c, t, "bar")
out, err := c.FullCommit() out, err := c.FullCommit(ctx)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 40, len(out)) require.Equal(t, 40, len(out))
} }
func TestGitShortCommit(t *testing.T) { func TestGitShortCommit(t *testing.T) {
ctx := context.TODO()
gittestutil.Mktmp(t) gittestutil.Mktmp(t)
c, err := gitutil.New() c, err := gitutil.New()
require.NoError(t, err) require.NoError(t, err)
@@ -47,38 +52,41 @@ func TestGitShortCommit(t *testing.T) {
gittestutil.GitInit(c, t) gittestutil.GitInit(c, t)
gittestutil.GitCommit(c, t, "bar") gittestutil.GitCommit(c, t, "bar")
out, err := c.ShortCommit() out, err := c.ShortCommit(ctx)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 7, len(out)) require.Equal(t, 7, len(out))
} }
func TestGitFullCommitErr(t *testing.T) { func TestGitFullCommitErr(t *testing.T) {
ctx := context.TODO()
gittestutil.Mktmp(t) gittestutil.Mktmp(t)
c, err := gitutil.New() c, err := gitutil.New()
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(c, t) gittestutil.GitInit(c, t)
_, err = c.FullCommit() _, err = c.FullCommit(ctx)
require.Error(t, err) require.Error(t, err)
require.True(t, gitutil.IsUnknownRevision(err)) require.True(t, gitutil.IsUnknownRevision(err))
require.False(t, gittestutil.IsAmbiguousArgument(err)) require.False(t, gittestutil.IsAmbiguousArgument(err))
} }
func TestGitShortCommitErr(t *testing.T) { func TestGitShortCommitErr(t *testing.T) {
ctx := context.TODO()
gittestutil.Mktmp(t) gittestutil.Mktmp(t)
c, err := gitutil.New() c, err := gitutil.New()
require.NoError(t, err) require.NoError(t, err)
gittestutil.GitInit(c, t) gittestutil.GitInit(c, t)
_, err = c.ShortCommit() _, err = c.ShortCommit(ctx)
require.Error(t, err) require.Error(t, err)
require.True(t, gitutil.IsUnknownRevision(err)) require.True(t, gitutil.IsUnknownRevision(err))
require.False(t, gittestutil.IsAmbiguousArgument(err)) require.False(t, gittestutil.IsAmbiguousArgument(err))
} }
func TestGitTagsPointsAt(t *testing.T) { func TestGitTagsPointsAt(t *testing.T) {
ctx := context.TODO()
gittestutil.Mktmp(t) gittestutil.Mktmp(t)
c, err := gitutil.New() c, err := gitutil.New()
require.NoError(t, err) require.NoError(t, err)
@@ -89,12 +97,13 @@ func TestGitTagsPointsAt(t *testing.T) {
gittestutil.GitCommit(c, t, "foo") gittestutil.GitCommit(c, t, "foo")
gittestutil.GitTag(c, t, "v0.9.0") gittestutil.GitTag(c, t, "v0.9.0")
out, err := c.Run("tag", "--points-at", "HEAD", "--sort", "-version:creatordate") out, err := c.Run(ctx, "tag", "--points-at", "HEAD", "--sort", "-version:creatordate")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "v0.9.0", out) require.Equal(t, "v0.9.0", strings.TrimSpace(string(out)))
} }
func TestGitDescribeTags(t *testing.T) { func TestGitDescribeTags(t *testing.T) {
ctx := context.TODO()
gittestutil.Mktmp(t) gittestutil.Mktmp(t)
c, err := gitutil.New() c, err := gitutil.New()
require.NoError(t, err) require.NoError(t, err)
@@ -105,12 +114,13 @@ func TestGitDescribeTags(t *testing.T) {
gittestutil.GitCommit(c, t, "foo") gittestutil.GitCommit(c, t, "foo")
gittestutil.GitTag(c, t, "v0.9.0") gittestutil.GitTag(c, t, "v0.9.0")
out, err := c.Run("describe", "--tags", "--abbrev=0") out, err := c.Run(ctx, "describe", "--tags", "--abbrev=0")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "v0.9.0", out) require.Equal(t, "v0.9.0", strings.TrimSpace(string(out)))
} }
func TestGitRemoteURL(t *testing.T) { func TestGitRemoteURL(t *testing.T) {
ctx := context.TODO()
type remote struct { type remote struct {
name string name string
url string url string
@@ -218,7 +228,7 @@ func TestGitRemoteURL(t *testing.T) {
} }
} }
ru, err := c.RemoteURL() ru, err := c.RemoteURL(ctx)
if tt.fail { if tt.fail {
require.Error(t, err) require.Error(t, err)
return return