Files
buildkit/client/client_git_source_test.go
Tonis Tiigi 38f8536ee3 client: split client integration tests by area
Move client integration test declarations out of the large harness files into
focused files for cache, exporters, sources, mounts, gateway behavior, and
related client features.

Keep registration lists grouped by source file, move non-gateway tests out of
the gateway harness, and remove the now-empty build_test.go. This is intended
as a mechanical split with no test logic changes.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2026-07-06 22:19:20 -07:00

707 lines
23 KiB
Go

package client
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/plugins/content/local"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/client/llb/sourceresolver"
gateway "github.com/moby/buildkit/frontend/gateway/client"
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/contentutil"
"github.com/moby/buildkit/util/gitutil/gitobject"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/moby/buildkit/util/testutil/workers"
digest "github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
)
// testGitBundleRoundTrip exercises the bundle round-trip end to end:
//
// 1. Seed a git repo served over HTTP.
// 2. Build 1 uses GitCheckoutBundle() to export a single-file git bundle.
// 3. Build 2 imports that bundle via GitBundleURL("oci-layout+blob://...")
// against a client-side OCI layout store, once with the sha pinned as ref
// and once with a symbolic ref ("master"). Both checkouts must succeed
// and produce a worktree matching Build 0 (plain git clone).
// 4. A KeepGitDir subtest exercises llb.KeepGitDir() + bundle and asserts
// no fake refs/buildkit/* namespace leaks into the checkout's .git dir.
// 5. A provenance subtest enables attest:provenance and asserts both the
// git-commit material and the bundle-blob material are recorded.
func testGitBundleRoundTrip(t *testing.T, sb integration.Sandbox) {
workers.CheckFeatureCompat(t, sb, workers.FeatureOCILayout)
integration.SkipOnPlatform(t, "windows")
ctx := sb.Context()
c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()
gitDir := t.TempDir()
err = runInDir(gitDir,
"git init -b master",
"git config --local user.email test@example.com",
"git config --local user.name test",
"echo hello > file.txt",
"git add file.txt",
"git commit -m initial",
"git update-server-info",
)
require.NoError(t, err)
cmd := exec.CommandContext(context.TODO(), "git", "rev-parse", "HEAD")
cmd.Dir = gitDir
out, err := cmd.Output()
require.NoError(t, err)
headSha := strings.TrimSpace(string(out))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Clean(gitDir))))
defer server.Close()
repoURL := server.URL + "/.git"
// Build 1: export a bundle via GitCheckoutBundle.
bundleDir := t.TempDir()
st := llb.Git(repoURL, "master", llb.GitChecksum(headSha), llb.GitCheckoutBundle())
def, err := st.Marshal(ctx)
require.NoError(t, err)
_, err = c.Solve(ctx, def, SolveOpt{
Exports: []ExportEntry{{Type: ExporterLocal, OutputDir: bundleDir}},
}, nil)
require.NoError(t, err)
bundleBytes, err := os.ReadFile(filepath.Join(bundleDir, "bundle"))
require.NoError(t, err)
require.NotEmpty(t, bundleBytes)
bundleDgst := digest.FromBytes(bundleBytes)
// Sanity-check the bundle locally: it must contain the commit we pinned,
// landing in the natural ref namespace (refs/heads/master).
localBare := t.TempDir()
err = runInDir(localBare, "git init --bare")
require.NoError(t, err)
bundleRoot := t.TempDir()
root, err := os.OpenRoot(bundleRoot)
require.NoError(t, err)
require.NoError(t, root.WriteFile("bundle.pack", bundleBytes, 0644))
require.NoError(t, root.Close())
bundlePath := filepath.Join(bundleRoot, "bundle.pack")
err = runInDir(localBare, fmt.Sprintf("git fetch %s +refs/*:refs/*", bundlePath))
require.NoError(t, err)
//nolint:gosec // Test-controlled temp dir and commit SHA are not attacker input.
cmd = exec.CommandContext(context.TODO(), "git", "--git-dir="+localBare, "cat-file", "-e", headSha+"^{commit}")
require.NoError(t, cmd.Run(), "bundle does not contain expected commit")
//nolint:gosec // Test-controlled temp dir is not attacker input.
cmd = exec.CommandContext(context.TODO(), "git", "--git-dir="+localBare, "rev-parse", "--verify", "refs/heads/master")
masterOut, err := cmd.CombinedOutput()
require.NoError(t, err, "bundle should carry user's ref refs/heads/master; output=%q", string(masterOut))
require.Equal(t, headSha, strings.TrimSpace(string(masterOut)))
// Stage the bundle in a client-side OCI layout store so Build 2 can
// resolve it through an oci-layout+blob:// locator.
storeDir := t.TempDir()
store, err := local.NewStore(storeDir)
require.NoError(t, err)
err = content.WriteBlob(ctx, store, "bundle-"+bundleDgst.String(), bytes.NewReader(bundleBytes),
ocispecs.Descriptor{Digest: bundleDgst, Size: int64(len(bundleBytes))})
require.NoError(t, err)
csID := "git-bundle-oci-store"
bundleLocator := fmt.Sprintf("oci-layout+blob://not/real@%s", bundleDgst.String())
cases := []struct {
name string
ref string // value passed as the fragment/ref to llb.Git
}{
{"sha-ref", headSha},
{"symbolic-ref", "master"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
destDir := t.TempDir()
st := llb.Git(
repoURL,
tc.ref,
llb.GitChecksum(headSha),
llb.GitBundleURL(bundleLocator, llb.GitBundleOCIStore("", csID)),
)
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{{Type: ExporterLocal, OutputDir: destDir}},
OCIStores: map[string]content.Store{
csID: store,
},
}, nil)
require.NoError(t, err)
dt, err := os.ReadFile(filepath.Join(destDir, "file.txt"))
require.NoError(t, err)
require.Equal(t, "hello\n", string(dt))
})
}
// KeepGitDir + bundle: the checkout's .git must not contain any leaked
// refs/buildkit/* namespace. `.git/refs/heads/*` and `.git/refs/tags/*`
// are populated by the plain-git checkout path (it runs its own fetch
// into a freshly inited repo), so we only assert no refs/buildkit/
// leak — the whole point of Item A is that the bundle no longer uses
// that namespace.
t.Run("keepgitdir-no-namespace-leak", func(t *testing.T) {
destDir := t.TempDir()
st := llb.Git(
repoURL,
"master",
llb.GitChecksum(headSha),
llb.GitBundleURL(bundleLocator, llb.GitBundleOCIStore("", csID)),
llb.KeepGitDir(),
)
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{{Type: ExporterLocal, OutputDir: destDir}},
OCIStores: map[string]content.Store{
csID: store,
},
}, nil)
require.NoError(t, err)
// .git dir should be present.
st2, err := os.Stat(filepath.Join(destDir, ".git"))
require.NoError(t, err)
require.True(t, st2.IsDir(), ".git should be a directory")
// No fake internal namespace anywhere under the checkout's .git.
leakRoot := filepath.Join(destDir, ".git", "refs", "buildkit")
_, err = os.Stat(leakRoot)
require.True(t, os.IsNotExist(err), ".git/refs/buildkit should not exist: %v", err)
// Also scan packed-refs, which is where cloned/fetched refs may
// live on disk after git gc / pack-refs.
if pr, err := os.ReadFile(filepath.Join(destDir, ".git", "packed-refs")); err == nil {
require.NotContains(t, string(pr), "refs/buildkit/",
"packed-refs should not carry refs/buildkit/* entries")
}
// The user's ref name must survive into .git: passing "master" to
// llb.Git should produce a checkout whose .git carries
// refs/heads/master, either loose or packed.
//nolint:gosec // Test-controlled export dir is not attacker input.
cmd := exec.CommandContext(context.TODO(), "git",
"--git-dir="+filepath.Join(destDir, ".git"),
"rev-parse", "--verify", "refs/heads/master")
refOut, err := cmd.CombinedOutput()
require.NoError(t, err, ".git should carry refs/heads/master; output=%q", string(refOut))
require.Equal(t, headSha, strings.TrimSpace(string(refOut)))
// HEAD must point at the pinned commit.
//nolint:gosec // Test-controlled export dir is not attacker input.
cmd = exec.CommandContext(context.TODO(), "git",
"--git-dir="+filepath.Join(destDir, ".git"),
"rev-parse", "HEAD")
gotOut, err := cmd.CombinedOutput()
require.NoError(t, err, "output=%q", string(gotOut))
require.Equal(t, headSha, strings.TrimSpace(string(gotOut)))
// file.txt content matches.
dt, err := os.ReadFile(filepath.Join(destDir, "file.txt"))
require.NoError(t, err)
require.Equal(t, "hello\n", string(dt))
})
// Provenance subtest: assert that the git-commit material and the
// bundle-blob material are both recorded.
t.Run("provenance", func(t *testing.T) {
workers.CheckFeatureCompat(t, sb, workers.FeatureProvenance)
destDir := t.TempDir()
st := llb.Scratch().File(
llb.Copy(
llb.Git(
repoURL,
"master",
llb.GitChecksum(headSha),
llb.GitBundleURL(bundleLocator, llb.GitBundleOCIStore("", csID)),
),
"file.txt",
"file.txt",
),
)
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{
FrontendAttrs: map[string]string{
"attest:provenance": "",
},
Exports: []ExportEntry{{Type: ExporterLocal, OutputDir: destDir}},
OCIStores: map[string]content.Store{
csID: store,
},
}, nil)
require.NoError(t, err)
provDt, err := os.ReadFile(filepath.Join(destDir, "provenance.json"))
require.NoError(t, err)
var stmt struct {
intoto.StatementHeader
Predicate provenancetypes.ProvenancePredicateSLSA1 `json:"predicate"`
}
require.NoError(t, json.Unmarshal(provDt, &stmt))
// Bundle-backed git sources emit two materials: the git source
// at its raw repository URL (same shape as a non-bundle git
// source), and the bundle blob as a purl with a vcs_url
// qualifier linking back to the git URL.
expectedGitURI := repoURL + "#master"
var foundCommit, foundBundle bool
for _, m := range stmt.Predicate.BuildDefinition.ResolvedDependencies {
if m.URI == expectedGitURI && m.Digest["sha1"] == headSha {
foundCommit = true
}
if strings.HasPrefix(m.URI, "pkg:oci/") && strings.Contains(m.URI, "ref_type=bundle") && strings.Contains(m.URI, "vcs_url=") && m.Digest["sha256"] == bundleDgst.Hex() {
foundBundle = true
}
}
require.True(t, foundCommit, "expected git commit material %q with sha1=%s in %+v", expectedGitURI, headSha, stmt.Predicate.BuildDefinition.ResolvedDependencies)
require.True(t, foundBundle, "expected bundle blob material (pkg:oci/...?ref_type=bundle) with sha256=%s in %+v", bundleDgst.Hex(), stmt.Predicate.BuildDefinition.ResolvedDependencies)
})
checkAllReleasable(t, c, sb, false)
}
// testGitBundleRoundTripRegistry mirrors testGitBundleRoundTrip but resolves
// the bundle blob through a registry using the docker-image+blob:// scheme.
// The bundle bytes are uploaded directly to the sandbox registry by digest
// and then re-imported in Build 2.
func testGitBundleRoundTripRegistry(t *testing.T, sb integration.Sandbox) {
workers.CheckFeatureCompat(t, sb, workers.FeatureDirectPush)
integration.SkipOnPlatform(t, "windows")
requiresLinux(t)
ctx := sb.Context()
c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()
registry, err := sb.NewRegistry()
if errors.Is(err, integration.ErrRequirements) {
t.Skip(err.Error())
}
require.NoError(t, err)
gitDir := t.TempDir()
err = runInDir(gitDir,
"git init -b master",
"git config --local user.email test@example.com",
"git config --local user.name test",
"echo hello > file.txt",
"git add file.txt",
"git commit -m initial",
"git update-server-info",
)
require.NoError(t, err)
cmd := exec.CommandContext(context.TODO(), "git", "rev-parse", "HEAD")
cmd.Dir = gitDir
out, err := cmd.Output()
require.NoError(t, err)
headSha := strings.TrimSpace(string(out))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Clean(gitDir))))
defer server.Close()
repoURL := server.URL + "/.git"
// Build 1: export a bundle via GitCheckoutBundle.
bundleDir := t.TempDir()
st := llb.Git(repoURL, "master", llb.GitChecksum(headSha), llb.GitCheckoutBundle())
def, err := st.Marshal(ctx)
require.NoError(t, err)
_, err = c.Solve(ctx, def, SolveOpt{
Exports: []ExportEntry{{Type: ExporterLocal, OutputDir: bundleDir}},
}, nil)
require.NoError(t, err)
bundleBytes, err := os.ReadFile(filepath.Join(bundleDir, "bundle"))
require.NoError(t, err)
require.NotEmpty(t, bundleBytes)
bundleDgst := digest.FromBytes(bundleBytes)
// Push the bundle as a raw blob into the sandbox registry under a
// repository name. The docker registry accepts arbitrary blobs by
// digest; the repository exists implicitly once any blob is uploaded
// under its name.
bundleRepoRef := registry + "/foo/bundle@" + bundleDgst.String()
ingester, err := contentutil.IngesterFromRef(bundleRepoRef)
require.NoError(t, err)
err = content.WriteBlob(ctx, ingester, "bundle-"+bundleDgst.String(), bytes.NewReader(bundleBytes),
ocispecs.Descriptor{Digest: bundleDgst, Size: int64(len(bundleBytes))})
require.NoError(t, err)
bundleLocator := "docker-image+blob://" + registry + "/foo/bundle@" + bundleDgst.String()
t.Run("checkout", func(t *testing.T) {
destDir := t.TempDir()
st := llb.Git(
repoURL,
"master",
llb.GitChecksum(headSha),
llb.GitBundleURL(bundleLocator),
)
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{{Type: ExporterLocal, OutputDir: destDir}},
}, nil)
require.NoError(t, err)
dt, err := os.ReadFile(filepath.Join(destDir, "file.txt"))
require.NoError(t, err)
require.Equal(t, "hello\n", string(dt))
})
t.Run("provenance", func(t *testing.T) {
workers.CheckFeatureCompat(t, sb, workers.FeatureProvenance)
destDir := t.TempDir()
st := llb.Scratch().File(
llb.Copy(
llb.Git(
repoURL,
"master",
llb.GitChecksum(headSha),
llb.GitBundleURL(bundleLocator),
),
"file.txt",
"file.txt",
),
)
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{
FrontendAttrs: map[string]string{
"attest:provenance": "",
},
Exports: []ExportEntry{{Type: ExporterLocal, OutputDir: destDir}},
}, nil)
require.NoError(t, err)
provDt, err := os.ReadFile(filepath.Join(destDir, "provenance.json"))
require.NoError(t, err)
var stmt struct {
intoto.StatementHeader
Predicate provenancetypes.ProvenancePredicateSLSA1 `json:"predicate"`
}
require.NoError(t, json.Unmarshal(provDt, &stmt))
expectedGitURI := repoURL + "#master"
var foundCommit, foundBundle bool
for _, m := range stmt.Predicate.BuildDefinition.ResolvedDependencies {
if m.URI == expectedGitURI && m.Digest["sha1"] == headSha {
foundCommit = true
}
if strings.HasPrefix(m.URI, "pkg:docker/") && strings.Contains(m.URI, "ref_type=bundle") && strings.Contains(m.URI, "vcs_url=") && m.Digest["sha256"] == bundleDgst.Hex() {
foundBundle = true
}
}
require.True(t, foundCommit, "expected git commit material %q with sha1=%s in %+v", expectedGitURI, headSha, stmt.Predicate.BuildDefinition.ResolvedDependencies)
require.True(t, foundBundle, "expected bundle blob material (pkg:docker/...?ref_type=bundle) with sha256=%s in %+v", bundleDgst.Hex(), stmt.Predicate.BuildDefinition.ResolvedDependencies)
})
checkAllReleasable(t, c, sb, false)
}
func testGitResolveMutatedSource(t *testing.T, sb integration.Sandbox) {
integration.SkipOnPlatform(t, "windows")
ctx := sb.Context()
c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()
gitDir := t.TempDir()
gitCommands := []string{
"git init",
"git config --local user.email test",
"git config --local user.name test",
"echo a > a",
"git add a",
"git commit -m a",
"git tag -a v0.1 -m v0.1",
"echo b > b",
"git add b",
"git commit -m b",
"git checkout -B v2",
"git update-server-info",
}
err = runInDir(gitDir, gitCommands...)
require.NoError(t, err)
cmd := exec.CommandContext(context.TODO(), "git", "rev-parse", "v0.1")
cmd.Dir = gitDir
out, err := cmd.Output()
require.NoError(t, err)
commitTag := strings.TrimSpace(string(out))
cmd = exec.CommandContext(context.TODO(), "git", "rev-parse", "v0.1^{commit}")
cmd.Dir = gitDir
out, err = cmd.Output()
require.NoError(t, err)
commitTagCommit := strings.TrimSpace(string(out))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Clean(gitDir))))
defer server.Close()
dest := t.TempDir()
_, err = c.Build(ctx, SolveOpt{
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: dest,
},
},
}, "test", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
id := "git://" + strings.TrimPrefix(server.URL, "http://") + "/.git#v0.1"
md, err := c.ResolveSourceMetadata(ctx, &pb.SourceOp{
Identifier: id,
Attrs: map[string]string{
"git.fullurl": server.URL + "/.git",
},
}, sourceresolver.Opt{})
if err != nil {
return nil, err
}
require.NotNil(t, md.Git)
require.Equal(t, "refs/tags/v0.1", md.Git.Ref)
require.Equal(t, commitTag, md.Git.Checksum)
require.Equal(t, commitTagCommit, md.Git.CommitChecksum)
require.Equal(t, id, md.Op.Identifier)
require.Equal(t, server.URL+"/.git", md.Op.Attrs["git.fullurl"])
// update the tag to point to a different commit
err = runInDir(gitDir, []string{
"git tag -f v0.1",
"git update-server-info",
}...)
require.NoError(t, err)
md, err = c.ResolveSourceMetadata(ctx, &pb.SourceOp{
Identifier: id,
Attrs: map[string]string{
"git.fullurl": server.URL + "/.git",
},
}, sourceresolver.Opt{})
if err != nil {
return nil, err
}
require.NotNil(t, md.Git)
require.Equal(t, "refs/tags/v0.1", md.Git.Ref)
require.Equal(t, commitTag, md.Git.Checksum)
require.Equal(t, commitTagCommit, md.Git.CommitChecksum)
require.Equal(t, id, md.Op.Identifier)
require.Equal(t, server.URL+"/.git", md.Op.Attrs["git.fullurl"])
st := llb.Git(server.URL+"/.git", "", llb.GitRef("v0.1"))
def, err := st.Marshal(sb.Context())
if err != nil {
return nil, err
}
return c.Solve(ctx, gateway.SolveRequest{
Definition: def.ToPB(),
})
}, nil)
require.NoError(t, err)
_, err = os.ReadFile(filepath.Join(dest, "b"))
require.Error(t, err)
require.True(t, os.IsNotExist(err), "expected file b to not exist")
dt, err := os.ReadFile(filepath.Join(dest, "a"))
require.NoError(t, err)
require.Equal(t, "a\n", string(dt))
checkAllReleasable(t, c, sb, false)
}
func testGitResolveSourceMetadata(t *testing.T, sb integration.Sandbox) {
ctx := sb.Context()
c, err := New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()
gitDir := t.TempDir()
gitCommands := []string{
"git init",
"git config --local user.email test@example.com",
"git config --local user.name test",
"touch a",
"git add a",
"git commit -m msg",
"git tag -a v0.1 -m v0.1release",
"echo b > b",
"git add b",
"git commit -m b",
"git checkout -B v2",
"git update-server-info",
}
err = runInDir(gitDir, gitCommands...)
require.NoError(t, err)
cmd := exec.CommandContext(context.TODO(), "git", "rev-parse", "HEAD")
cmd.Dir = gitDir
out, err := cmd.Output()
require.NoError(t, err)
commitHEAD := strings.TrimSpace(string(out))
cmd = exec.CommandContext(context.TODO(), "git", "rev-parse", "v0.1")
cmd.Dir = gitDir
out, err = cmd.Output()
require.NoError(t, err)
commitTag := strings.TrimSpace(string(out))
cmd = exec.CommandContext(context.TODO(), "git", "rev-parse", "v0.1^{commit}")
cmd.Dir = gitDir
out, err = cmd.Output()
require.NoError(t, err)
commitTagCommit := strings.TrimSpace(string(out))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Clean(gitDir))))
defer server.Close()
_, err = c.Build(ctx, SolveOpt{}, "test", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
id := "git://" + strings.TrimPrefix(server.URL, "http://") + "/.git"
md, err := c.ResolveSourceMetadata(ctx, &pb.SourceOp{
Identifier: id,
Attrs: map[string]string{
"git.fullurl": server.URL + "/.git",
},
}, sourceresolver.Opt{})
if err != nil {
return nil, err
}
require.NotNil(t, md.Git)
require.Equal(t, "refs/heads/v2", md.Git.Ref) // default to branch head
require.Equal(t, commitHEAD, md.Git.Checksum)
require.Equal(t, "", md.Git.CommitChecksum) // not annotated tag
require.Equal(t, id, md.Op.Identifier)
require.Equal(t, server.URL+"/.git", md.Op.Attrs["git.fullurl"])
require.Nil(t, md.Git.CommitObject)
require.Nil(t, md.Git.TagObject)
id += "#v0.1"
md, err = c.ResolveSourceMetadata(ctx, &pb.SourceOp{
Identifier: id,
Attrs: map[string]string{
"git.fullurl": server.URL + "/.git",
},
}, sourceresolver.Opt{})
if err != nil {
return nil, err
}
require.NotNil(t, md.Git)
require.Equal(t, "refs/tags/v0.1", md.Git.Ref)
require.Equal(t, commitTag, md.Git.Checksum) // annotated tag
require.Equal(t, commitTagCommit, md.Git.CommitChecksum)
require.Equal(t, id, md.Op.Identifier)
require.Equal(t, server.URL+"/.git", md.Op.Attrs["git.fullurl"])
require.Nil(t, md.Git.CommitObject)
require.Nil(t, md.Git.TagObject)
md, err = c.ResolveSourceMetadata(ctx, &pb.SourceOp{
Identifier: id,
Attrs: map[string]string{
"git.fullurl": server.URL + "/.git",
},
}, sourceresolver.Opt{
GitOpt: &sourceresolver.ResolveGitOpt{
ReturnObject: true,
},
})
if err != nil {
return nil, err
}
require.NotNil(t, md.Git)
require.Equal(t, "refs/tags/v0.1", md.Git.Ref)
require.Equal(t, commitTag, md.Git.Checksum) // annotated tag
require.Equal(t, commitTagCommit, md.Git.CommitChecksum)
require.Equal(t, id, md.Op.Identifier)
require.Equal(t, server.URL+"/.git", md.Op.Attrs["git.fullurl"])
require.NotNil(t, md.Git.CommitObject)
require.NotNil(t, md.Git.TagObject)
commitObj, err := gitobject.Parse(md.Git.CommitObject)
require.NoError(t, err)
require.NoError(t, commitObj.VerifyChecksum(md.Git.CommitChecksum))
commit, err := commitObj.ToCommit()
require.NoError(t, err)
require.Equal(t, "msg", commit.Message)
require.Equal(t, "test", commit.Author.Name)
require.Equal(t, "test@example.com", commit.Author.Email)
require.Equal(t, "test", commit.Committer.Name)
require.Equal(t, "test@example.com", commit.Committer.Email)
commitTime := commit.Committer.When
require.NotNil(t, commitTime)
require.WithinDuration(t, time.Now(), *commitTime, 2*time.Minute)
tagObj, err := gitobject.Parse(md.Git.TagObject)
require.NoError(t, err)
require.NoError(t, tagObj.VerifyChecksum(md.Git.Checksum))
tag, err := tagObj.ToTag()
require.NoError(t, err)
require.Equal(t, "v0.1release", tag.Message)
require.Equal(t, "v0.1", tag.Tag)
require.Equal(t, "test", tag.Tagger.Name)
require.Equal(t, "test@example.com", tag.Tagger.Email)
tagTime := tag.Tagger.When
require.NotNil(t, tagTime)
require.WithinDuration(t, time.Now(), *tagTime, 2*time.Minute)
return nil, nil
}, nil)
require.NoError(t, err)
}
func runInDir(dir string, cmds ...string) error {
return runInDirEnv(dir, nil, cmds...)
}
func runInDirEnv(dir string, env []string, cmds ...string) error {
for _, args := range cmds {
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.CommandContext(context.TODO(), "powershell", "-command", args)
} else {
cmd = exec.CommandContext(context.TODO(), "sh", "-c", args)
}
cmd.Env = append(os.Environ(), env...)
cmd.Dir = dir
if err := cmd.Run(); err != nil {
return errors.Wrapf(err, "error running %v", args)
}
}
return nil
}