From 7f1c0be07bba2a5d2f4aabdb0875556c88d404af Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:41:38 +0100 Subject: [PATCH] gitutil: migrate to BuildKit GitCLI API Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- build/git.go | 14 ++- commands/history/ls.go | 5 +- tests/bake.go | 29 ++--- tests/build.go | 15 +-- tests/history.go | 5 +- tests/policy_build.go | 17 ++- util/gitutil/gittestutil/testutil.go | 30 +++-- util/gitutil/gittestutil/testutilserve.go | 6 +- util/gitutil/gitutil.go | 147 ++++++---------------- util/gitutil/gitutil_test.go | 34 +++-- 10 files changed, 131 insertions(+), 171 deletions(-) diff --git a/build/git.go b/build/git.go index ac79b0689808..ae0a28863780 100644 --- a/build/git.go +++ b/build/git.go @@ -12,6 +12,7 @@ import ( "github.com/docker/buildx/util/gitutil" "github.com/docker/buildx/util/osutil" "github.com/moby/buildkit/client" + bkgitutil "github.com/moby/buildkit/util/gitutil" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -61,7 +62,7 @@ func getGitAttributes(ctx context.Context, contextPath, dockerfilePath string) ( } 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 st, err1 := os.Stat(path.Join(wd, ".git")); err1 == nil && st.IsDir() { 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 } - if !gitc.IsInsideWorkTree() { + if !gitc.IsInsideWorkTree(ctx) { 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, nil } - root, err := gitc.RootDir() + root, err := gitc.WorkTree(ctx) if err != nil { return nil, errors.Wrap(err, "failed to get git root dir") } + root = osutil.SanitizePath(root) 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") } else if sha != "" { checkDirty := false @@ -92,7 +94,7 @@ func getGitAttributes(ctx context.Context, contextPath, dockerfilePath string) ( checkDirty = v } } - if checkDirty && gitc.IsDirty() { + if checkDirty && gitc.IsDirty(ctx) { sha += "-dirty" } 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 { res["label:"+ocispecs.AnnotationSource] = rurl } diff --git a/commands/history/ls.go b/commands/history/ls.go index 9153d8fca094..456a01fee272 100644 --- a/commands/history/ls.go +++ b/commands/history/ls.go @@ -19,6 +19,7 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/formatter" "github.com/docker/go-units" + bkgitutil "github.com/moby/buildkit/util/gitutil" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -58,14 +59,14 @@ func runLs(ctx context.Context, dockerCli command.Cli, opts lsOptions) error { if err != nil { return err } - gitc, err := gitutil.New(gitutil.WithContext(ctx), gitutil.WithWorkingDir(wd)) + gitc, err := gitutil.New(bkgitutil.WithDir(wd)) if err != nil { 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.Wrapf(err, "could not find git repository for local filter") } - remote, err := gitc.RemoteURL() + remote, err := gitc.RemoteURL(ctx) if err != nil { return errors.Wrapf(err, "could not get remote URL for local filter") } diff --git a/tests/bake.go b/tests/bake.go index 4daa0515f6bc..791fc4a8aca2 100644 --- a/tests/bake.go +++ b/tests/bake.go @@ -23,6 +23,7 @@ import ( "github.com/moby/buildkit/identity" provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types" "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/integration" "github.com/pkg/errors" @@ -551,7 +552,7 @@ COPY super-cool.txt / 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) gittestutil.GitInit(git, t) gittestutil.GitAdd(git, t, "docker-bake.hcl", "bar") @@ -688,7 +689,7 @@ EOT ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -718,7 +719,7 @@ EOT ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -765,7 +766,7 @@ EOT ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) + git, err := gitutil.New(bkgitutil.WithDir(dirSpec)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -834,7 +835,7 @@ EOT ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) + git, err := gitutil.New(bkgitutil.WithDir(dirSpec)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -868,14 +869,14 @@ EOT ) dirDest := t.TempDir() - gitSpec, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) + gitSpec, err := gitutil.New(bkgitutil.WithDir(dirSpec)) require.NoError(t, err) gittestutil.GitInit(gitSpec, t) gittestutil.GitAdd(gitSpec, t, "docker-bake.hcl") gittestutil.GitCommit(gitSpec, t, "initial commit") addrSpec := gittestutil.GitServeHTTP(gitSpec, t) - gitSrc, err := gitutil.New(gitutil.WithWorkingDir(dirSrc)) + gitSrc, err := gitutil.New(bkgitutil.WithDir(dirSrc)) require.NoError(t, err) gittestutil.GitInit(gitSrc, t) gittestutil.GitAdd(gitSrc, t, "foo") @@ -909,7 +910,7 @@ COPY super-cool.txt / ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) gittestutil.GitAdd(git, t, "docker-bake.hcl", "bar") @@ -948,7 +949,7 @@ COPY --from=files file.txt /file.txt ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) gittestutil.GitAdd(git, t, "docker-bake.hcl", "build", "files-src") @@ -984,7 +985,7 @@ COPY --from=files marker.txt /marker.txt ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) gittestutil.GitAdd(git, t, "docker-bake.hcl", "build", "marker.txt") @@ -1023,7 +1024,7 @@ EOT ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) + git, err := gitutil.New(bkgitutil.WithDir(dirSpec)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -1071,7 +1072,7 @@ EOT ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) + git, err := gitutil.New(bkgitutil.WithDir(dirSpec)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -1127,7 +1128,7 @@ COPY foo /foo ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) + git, err := gitutil.New(bkgitutil.WithDir(dirSpec)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -1179,7 +1180,7 @@ COPY foo /foo fstest.CreateFile("foo", []byte("foo"), 0600), ) - git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) + git, err := gitutil.New(bkgitutil.WithDir(dirSpec)) require.NoError(t, err) gittestutil.GitInit(git, t) diff --git a/tests/build.go b/tests/build.go index 5cab893297a6..c56682b34bf5 100644 --- a/tests/build.go +++ b/tests/build.go @@ -28,6 +28,7 @@ import ( provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types" "github.com/moby/buildkit/util/appdefaults" "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/integration" "github.com/opencontainers/go-digest" @@ -129,7 +130,7 @@ COPY foo /foo ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -154,7 +155,7 @@ COPY foo /foo ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -181,7 +182,7 @@ COPY foo /foo ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -214,7 +215,7 @@ COPY foo /foo ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -247,7 +248,7 @@ COPY foo /foo ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -280,7 +281,7 @@ COPY foo /foo ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -402,7 +403,7 @@ COPY foo /foo ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) diff --git a/tests/history.go b/tests/history.go index fa3526f37b86..8ca243c7a09e 100644 --- a/tests/history.go +++ b/tests/history.go @@ -12,6 +12,7 @@ import ( "github.com/containerd/continuity/fs/fstest" "github.com/docker/buildx/util/gitutil" "github.com/docker/buildx/util/gitutil/gittestutil" + bkgitutil "github.com/moby/buildkit/util/gitutil" "github.com/moby/buildkit/util/testutil/integration" "github.com/stretchr/testify/require" ) @@ -192,7 +193,7 @@ COPY foo /foo ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -257,7 +258,7 @@ EOT ) dirDest := t.TempDir() - git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + git, err := gitutil.New(bkgitutil.WithDir(dir)) require.NoError(t, err) gittestutil.GitInit(git, t) diff --git a/tests/policy_build.go b/tests/policy_build.go index 244b8e00d492..0906b283cb72 100644 --- a/tests/policy_build.go +++ b/tests/policy_build.go @@ -3,6 +3,7 @@ package tests import ( "archive/tar" "bytes" + "context" "encoding/json" "errors" "fmt" @@ -20,6 +21,7 @@ import ( "github.com/moby/buildkit/client" "github.com/moby/buildkit/identity" "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/httpserver" "github.com/moby/buildkit/util/testutil/integration" @@ -823,7 +825,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, "a"), []byte("a"), 0600)) - git, err := gitutil.New(gitutil.WithWorkingDir(gitDir)) + git, err := gitutil.New(bkgitutil.WithDir(gitDir)) require.NoError(t, err) gittestutil.GitInit(git, t) @@ -835,15 +837,18 @@ func testBuildPolicyGit(t *testing.T, sb integration.Sandbox) { require.NoError(t, os.WriteFile(filepath.Join(gitDir, "b"), []byte("b"), 0600)) gittestutil.GitAdd(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) - commitHead, err := git.Run("rev-parse", "HEAD") + commitHeadB, err := git.Run(context.TODO(), "rev-parse", "HEAD") 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) - 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) + commitTagCommit := strings.TrimSpace(string(commitTagCommitB)) baseURL := gittestutil.GitServeHTTP(git, t) tagURL := baseURL + "#v0.1" branchURL := baseURL + "#v2" @@ -1320,7 +1325,7 @@ default allow = true decision := {"allow": allow} `), 0600)) - git, err := gitutil.New(gitutil.WithWorkingDir(gitDir)) + git, err := gitutil.New(bkgitutil.WithDir(gitDir)) require.NoError(t, err) gittestutil.GitInit(git, t) gittestutil.GitAdd(git, t, ".") diff --git a/util/gitutil/gittestutil/testutil.go b/util/gitutil/gittestutil/testutil.go index a88c3aa6163a..aa5939fc56f9 100644 --- a/util/gitutil/gittestutil/testutil.go +++ b/util/gitutil/gittestutil/testutil.go @@ -1,15 +1,17 @@ package gittestutil import ( + "context" "os" "strings" "testing" "github.com/docker/buildx/util/gitutil" + "github.com/pkg/errors" "github.com/stretchr/testify/require" ) -func GitInit(c *gitutil.Git, tb testing.TB) { +func GitInit(c *gitutil.GitCLI, tb testing.TB) { tb.Helper() out, err := fakeGit(c, "init") require.NoError(tb, err) @@ -19,48 +21,48 @@ func GitInit(c *gitutil.Git, tb testing.TB) { _, _ = 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() out, err := fakeGit(c, "commit", "--allow-empty", "-m", msg) require.NoError(tb, err) 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() out, err := fakeGit(c, "tag", tag) require.NoError(tb, err) 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() out, err := fakeGit(c, "tag", "-a", tag, "-m", message) require.NoError(tb, err) 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() out, err := fakeGit(c, "checkout", "-b", name) require.NoError(tb, err) 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() args := append([]string{"add"}, files...) _, err := fakeGit(c, args...) 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() _, err := fakeGit(c, "remote", "add", name, url) 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() _, err := fakeGit(c, "fetch", "--depth", "1", remote, target) require.NoError(tb, err) @@ -81,7 +83,7 @@ func Mktmp(tb testing.TB) string { return folder } -func fakeGit(c *gitutil.Git, args ...string) (string, error) { +func fakeGit(c *gitutil.GitCLI, args ...string) (string, error) { allArgs := []string{ "-c", "user.name=buildx", "-c", "user.email=buildx@docker.com", @@ -90,7 +92,15 @@ func fakeGit(c *gitutil.Git, args ...string) (string, error) { "-c", "log.showSignature=false", } 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 { diff --git a/util/gitutil/gittestutil/testutilserve.go b/util/gitutil/gittestutil/testutilserve.go index 0a6076ec43b7..8fde0f950f22 100644 --- a/util/gitutil/gittestutil/testutilserve.go +++ b/util/gitutil/gittestutil/testutilserve.go @@ -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() gitUpdateServerInfo(c, t) 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{}) name := "test.git" - dir, err := c.GitDir() + dir, err := c.GitDir(context.TODO()) if err != nil { 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) } -func gitUpdateServerInfo(c *gitutil.Git, tb testing.TB) { +func gitUpdateServerInfo(c *gitutil.GitCLI, tb testing.TB) { tb.Helper() _, err := fakeGit(c, "update-server-info") require.NoError(tb, err) diff --git a/util/gitutil/gitutil.go b/util/gitutil/gitutil.go index cca79f9001f4..29c938488f74 100644 --- a/util/gitutil/gitutil.go +++ b/util/gitutil/gitutil.go @@ -1,122 +1,80 @@ package gitutil import ( - "bytes" "context" "net/url" - "os" - "os/exec" - "path/filepath" "strings" - "github.com/docker/buildx/util/osutil" + bkgitutil "github.com/moby/buildkit/util/gitutil" "github.com/pkg/errors" ) -// Git represents an active git object -type Git struct { - ctx context.Context - wd string - gitpath string +// GitCLI represents an active git object. +type GitCLI struct { + bkgitutil.GitCLI } -// Option provides a variadic option for configuring the git client. -type Option func(b *Git) +// New initializes a new git client. +func New(opts ...bkgitutil.Option) (*GitCLI, error) { + cli := bkgitutil.NewGitCLI(opts...) -// WithContext sets context. -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) + gitpath, err := gitPath(cli.Dir()) if err != nil { 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 { - out, err := c.Run("rev-parse", "--is-inside-work-tree") +func (cli *GitCLI) IsInsideWorkTree(ctx context.Context) bool { + out, err := cli.clean(cli.Run(ctx, "rev-parse", "--is-inside-work-tree")) return out == "true" && err == nil } -func (c *Git) IsDirty() bool { - out, err := c.Run("status", "--porcelain", "--ignored") - return strings.TrimSpace(out) != "" || err != nil -} - -func (c *Git) RootDir() (string, error) { - root, err := c.Run("rev-parse", "--show-toplevel") - if err != nil { - return "", err - } - return osutil.SanitizePath(root), nil -} - -func (c *Git) GitDir() (string, error) { - dir, err := c.RootDir() - if err != nil { - return "", err - } - return filepath.Join(dir, ".git"), nil +func (cli *GitCLI) IsDirty(ctx context.Context) bool { + out, err := cli.Run(ctx, "status", "--porcelain", "--ignored") + return strings.TrimSpace(string(out)) != "" || err != nil } -func (c *Git) RemoteURL() (string, error) { - // Try default remote based on remote tracking branch - if remote, err := c.currentRemote(); err == nil && remote != "" { - if ru, err := c.clean(c.run("remote", "get-url", remote)); err == nil && ru != "" { +func (cli *GitCLI) RemoteURL(ctx context.Context) (string, error) { + // Try default remote based on remote tracking branch. + if remote, err := cli.currentRemote(ctx); err == nil && remote != "" { + if ru, err := cli.clean(cli.Run(ctx, "remote", "get-url", remote)); err == nil && ru != "" { return stripCredentials(ru), nil } } - // 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 != "" { + // Next try to get the remote URL from the origin remote first. + if ru, err := cli.clean(cli.Run(ctx, "remote", "get-url", "origin")); err == nil && ru != "" { return stripCredentials(ru), nil } - // 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 that fails, try to get the remote URL from the upstream remote. + if ru, err := cli.clean(cli.Run(ctx, "remote", "get-url", "upstream")); err == nil && ru != "" { return stripCredentials(ru), nil } return "", errors.New("no remote URL found for either origin or upstream") } -func (c *Git) FullCommit() (string, error) { - return c.clean(c.run("show", "--format=%H", "HEAD", "--quiet", "--")) +func (cli *GitCLI) FullCommit(ctx context.Context) (string, error) { + return cli.clean(cli.Run(ctx, "show", "--format=%H", "HEAD", "--quiet", "--")) } -func (c *Git) ShortCommit() (string, error) { - return c.clean(c.run("show", "--format=%h", "HEAD", "--quiet", "--")) +func (cli *GitCLI) ShortCommit(ctx context.Context) (string, error) { + 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 err error for _, fn := range []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) { - return c.clean(c.run("describe", "--tags", "--abbrev=0")) + return cli.clean(cli.Run(ctx, "describe", "--tags", "--abbrev=0")) }, } { tag, err = fn() @@ -127,36 +85,8 @@ func (c *Git) Tag() (string, error) { return tag, err } -func (c *Git) Run(args ...string) (string, error) { - return c.clean(c.run(args...)) -} - -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) { +func (cli *GitCLI) clean(dt []byte, err error) (string, error) { + out := string(dt) out = strings.ReplaceAll(strings.Split(out, "\n")[0], "'", "") if err != nil { err = errors.New(strings.TrimSuffix(err.Error(), "\n")) @@ -164,16 +94,15 @@ func (c *Git) clean(out string, err error) (string, error) { return out, err } -func (c *Git) currentRemote() (string, error) { - symref, err := c.Run("symbolic-ref", "-q", "HEAD") +func (cli *GitCLI) currentRemote(ctx context.Context) (string, error) { + symref, err := cli.clean(cli.Run(ctx, "symbolic-ref", "-q", "HEAD")) if err != nil { return "", err } if symref == "" { return "", nil } - // git for-each-ref --format='%(upstream:remotename)' - remote, err := c.Run("for-each-ref", "--format=%(upstream:remotename)", symref) + remote, err := cli.clean(cli.Run(ctx, "for-each-ref", "--format=%(upstream:remotename)", symref)) if err != nil { return "", err } diff --git a/util/gitutil/gitutil_test.go b/util/gitutil/gitutil_test.go index 8a21b25ef1e7..db868c3a8b4c 100644 --- a/util/gitutil/gitutil_test.go +++ b/util/gitutil/gitutil_test.go @@ -1,6 +1,8 @@ package gitutil_test import ( + "context" + "strings" "testing" "github.com/docker/buildx/util/gitutil" @@ -9,6 +11,7 @@ import ( ) func TestGit(t *testing.T) { + ctx := context.TODO() gittestutil.Mktmp(t) c, err := gitutil.New() require.NoError(t, err) @@ -16,17 +19,18 @@ func TestGit(t *testing.T) { gittestutil.GitInit(c, t) gittestutil.GitCommit(c, t, "bar") - out, err := c.Run("status") + out, err := c.Run(ctx, "status") require.NoError(t, err) require.NotEmpty(t, out) - out, err = c.Run("not-exist") + out, err = c.Run(ctx, "not-exist") require.Error(t, err) 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) { + ctx := context.TODO() gittestutil.Mktmp(t) c, err := gitutil.New() require.NoError(t, err) @@ -34,12 +38,13 @@ func TestGitFullCommit(t *testing.T) { gittestutil.GitInit(c, t) gittestutil.GitCommit(c, t, "bar") - out, err := c.FullCommit() + out, err := c.FullCommit(ctx) require.NoError(t, err) require.Equal(t, 40, len(out)) } func TestGitShortCommit(t *testing.T) { + ctx := context.TODO() gittestutil.Mktmp(t) c, err := gitutil.New() require.NoError(t, err) @@ -47,38 +52,41 @@ func TestGitShortCommit(t *testing.T) { gittestutil.GitInit(c, t) gittestutil.GitCommit(c, t, "bar") - out, err := c.ShortCommit() + out, err := c.ShortCommit(ctx) require.NoError(t, err) require.Equal(t, 7, len(out)) } func TestGitFullCommitErr(t *testing.T) { + ctx := context.TODO() gittestutil.Mktmp(t) c, err := gitutil.New() require.NoError(t, err) gittestutil.GitInit(c, t) - _, err = c.FullCommit() + _, err = c.FullCommit(ctx) require.Error(t, err) require.True(t, gitutil.IsUnknownRevision(err)) require.False(t, gittestutil.IsAmbiguousArgument(err)) } func TestGitShortCommitErr(t *testing.T) { + ctx := context.TODO() gittestutil.Mktmp(t) c, err := gitutil.New() require.NoError(t, err) gittestutil.GitInit(c, t) - _, err = c.ShortCommit() + _, err = c.ShortCommit(ctx) require.Error(t, err) require.True(t, gitutil.IsUnknownRevision(err)) require.False(t, gittestutil.IsAmbiguousArgument(err)) } func TestGitTagsPointsAt(t *testing.T) { + ctx := context.TODO() gittestutil.Mktmp(t) c, err := gitutil.New() require.NoError(t, err) @@ -89,12 +97,13 @@ func TestGitTagsPointsAt(t *testing.T) { gittestutil.GitCommit(c, t, "foo") 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.Equal(t, "v0.9.0", out) + require.Equal(t, "v0.9.0", strings.TrimSpace(string(out))) } func TestGitDescribeTags(t *testing.T) { + ctx := context.TODO() gittestutil.Mktmp(t) c, err := gitutil.New() require.NoError(t, err) @@ -105,12 +114,13 @@ func TestGitDescribeTags(t *testing.T) { gittestutil.GitCommit(c, t, "foo") 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.Equal(t, "v0.9.0", out) + require.Equal(t, "v0.9.0", strings.TrimSpace(string(out))) } func TestGitRemoteURL(t *testing.T) { + ctx := context.TODO() type remote struct { name string url string @@ -218,7 +228,7 @@ func TestGitRemoteURL(t *testing.T) { } } - ru, err := c.RemoteURL() + ru, err := c.RemoteURL(ctx) if tt.fail { require.Error(t, err) return