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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ updates:
interval: weekly
open-pull-requests-limit: 10

- package-ecosystem: gomod
directory: /gogit
schedule:
interval: weekly
open-pull-requests-limit: 10

- package-ecosystem: github-actions
directory: /
schedule:
Expand Down
21 changes: 20 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,22 @@ jobs:
- name: Build
run: go build -v ./...

- name: Build gogit module
working-directory: gogit
run: go build -v ./...

- name: Test
run: go test -v -race ./...

- name: Test gogit module against minimum clone version
working-directory: gogit
run: go test -v -race ./...

- name: Test gogit module with sibling clone module
run: |
go work init . ./gogit
go test -v -race ./gogit/...

lint:
runs-on: ubuntu-latest
steps:
Expand All @@ -43,7 +56,13 @@ jobs:
with:
go-version: '1.25'

- name: golangci-lint
- name: Lint root module
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9
with:
version: latest

- name: Lint gogit module
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9
with:
version: latest
working-directory: gogit
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# clone

Go library for programs that keep local checkouts of HTTPS Git repositories. Clone, fetch, remote queries, and command retries shell out to the `git` binary, which must be on `PATH`. Blob reads use [go-git](https://github.com/go-git/go-git) in process. The package supports Go 1.25 or later.
Go library for programs that keep local checkouts of HTTPS Git repositories. Clone, fetch, remote queries, command retries, and blob reads shell out to the `git` binary, which must be on `PATH`. Programs that read many blobs can use the optional `gogit` module for in-process object access. Both modules support Go 1.25 or later.

## Install

Expand Down Expand Up @@ -79,17 +79,21 @@ if err := cache.EnsureCommit(ctx, url, commit); err != nil {

## Read a file from a commit

`InspectBlob` reads the object in process and returns at most `maxBytes`. The object's size distinguishes content exactly at the limit from truncated content. Complete reads use `magic.Detect`; truncated reads use `magic.DetectPrefix` so the result can report that later bytes may change the classification. The returned content is retained for text, binary, and unknown results. Repositories using object formats unsupported by go-git fall back to `git show`.
`clone.InspectBlob` runs `git show` and returns at most `maxBytes`. The optional `github.com/git-pkgs/clone/gogit` module reads the object in process and falls back to `git show` for unsupported object formats and repository layouts. Install it separately when repeated process startup is costly:

Both blob functions validate commits and paths before reading the repository. `ValidCommit` and `SanitizePath` are also available when callers need to validate input earlier:
```
go get github.com/git-pkgs/clone/gogit
```

Both implementations use the object's size to distinguish content exactly at the limit from truncated content. Complete reads use `magic.Detect`; truncated reads use `magic.DetectPrefix` so the result can report that later bytes may change the classification. They retain returned content for text, binary, and unknown results, and validate commits and paths before reading the repository. `ValidCommit` and `SanitizePath` are also available when callers need to validate input earlier:

```go
path, ok := clone.SanitizePath("cmd/tool/main.go")
if !ok || !clone.ValidCommit(commit) {
log.Fatal("invalid commit or path")
}

result, err := clone.InspectBlob(
result, err := gogit.InspectBlob(
ctx,
filepath.Join(cache.Dir(url), "src"),
commit,
Expand All @@ -106,9 +110,7 @@ if result.Detection.Kind == magic.KindText &&
fmt.Println("truncated:", result.Truncated)
```

`Blob` remains available for callers that only need its original NUL-based
binary flag. It returns nil content when a NUL occurs within the returned range;
a NUL beyond `maxBytes` is not observed.
`clone.Blob` and `gogit.Blob` are available for callers that only need the original NUL-based binary flag. They return nil content when a NUL occurs within the returned range; a NUL beyond `maxBytes` is not observed.

## Remote queries

Expand Down
51 changes: 8 additions & 43 deletions blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ type BlobResult struct {
Truncated bool
}

// InspectBlob reads path from commit in dir and classifies the returned bytes.
// It uses prefix detection when maxBytes truncates the blob. commit and path
// are validated with ValidCommit and SanitizePath before reading the
// repository.
// InspectBlob reads path from commit in dir through the git binary and
// classifies the returned bytes. It uses prefix detection when maxBytes
// truncates the blob. commit and path are validated with ValidCommit and
// SanitizePath before invoking Git.
func InspectBlob(ctx context.Context, dir, commit, blobPath string, maxBytes int64) (BlobResult, error) {
content, truncated, err := readBlob(ctx, dir, commit, blobPath, maxBytes)
if err != nil {
Expand All @@ -44,9 +44,9 @@ func InspectBlob(ctx context.Context, dir, commit, blobPath string, maxBytes int
}, nil
}

// Blob reads path from commit in dir. It caps content at maxBytes and reports
// whether the blob is binary or was truncated. commit and path are validated
// with ValidCommit and SanitizePath before reading the repository.
// Blob reads path from commit in dir through the git binary. It caps content
// at maxBytes and reports whether the blob is binary or was truncated. commit
// and path are validated with ValidCommit and SanitizePath before invoking Git.
func Blob(ctx context.Context, dir, commit, blobPath string, maxBytes int64) (content []byte, binary, truncated bool, err error) {
content, truncated, err = readBlob(ctx, dir, commit, blobPath, maxBytes)
if err != nil {
Expand Down Expand Up @@ -75,42 +75,7 @@ func readBlob(ctx context.Context, dir, commit, blobPath string, maxBytes int64)
if err := ctx.Err(); err != nil {
return nil, false, err
}

raw, truncated, goGitErr := readBlobWithGoGit(ctx, dir, commit, clean, maxBytes)
if goGitErr == nil {
return raw, truncated, nil
}
if ctxErr := ctx.Err(); ctxErr != nil {
return nil, false, ctxErr
}
// Native Git remains the compatibility path for object formats and
// repository layouts that go-git v5 cannot read. This also covers
// abbreviated SHA-256 object IDs, whose length alone does not identify the
// repository's object format.
raw, truncated, gitErr := readBlobWithGit(ctx, dir, commit, clean, maxBytes)
if gitErr != nil {
return nil, false, errors.Join(
fmt.Errorf("go-git blob read: %w", goGitErr),
fmt.Errorf("git blob read: %w", gitErr),
)
}
return raw, truncated, nil
}

type contextReader struct {
ctx context.Context
reader io.Reader
}

func (r contextReader) Read(p []byte) (int, error) {
if err := r.ctx.Err(); err != nil {
return 0, err
}
n, err := r.reader.Read(p)
if err == nil {
err = r.ctx.Err()
}
return n, err
return readBlobWithGit(ctx, dir, commit, clean, maxBytes)
}

func readBlobWithGit(ctx context.Context, dir, commit, clean string, maxBytes int64) (content []byte, truncated bool, err error) {
Expand Down
111 changes: 5 additions & 106 deletions blob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,101 +232,13 @@ func TestBlobReadsTextAtLimit(t *testing.T) {
}
}

func TestBlobReadsWithoutGitOnPath(t *testing.T) {
func TestBlobRequiresGitOnPath(t *testing.T) {
dir, commit := seedBlobRepository(t)
t.Setenv("PATH", t.TempDir())

content, binary, truncated, err := Blob(context.Background(), dir, commit, "nested/file.txt", 6)
if err != nil {
t.Fatalf("Blob: %v", err)
}
if string(content) != "nested" || binary || truncated {
t.Errorf("Blob = (%q, %v, %v), want exact text", content, binary, truncated)
}
}

func TestBlobReadsPackedObject(t *testing.T) {
dir, commit := seedBlobRepository(t)
runGitTest(t, dir, "gc", "--quiet", "--prune=now")
t.Setenv("PATH", t.TempDir())

content, binary, truncated, err := Blob(context.Background(), dir, commit, "big.txt", 32)
if err != nil {
t.Fatalf("Blob: %v", err)
}
if !bytes.Equal(content, bytes.Repeat([]byte("a"), 32)) || binary || !truncated {
t.Errorf("Blob = (%q, %v, %v), want truncated text", content, binary, truncated)
}
}

func TestBlobReadsLinkedWorktree(t *testing.T) {
dir, commit := seedBlobRepository(t)
worktree := filepath.Join(t.TempDir(), "checkout")
runGitTest(t, dir, "worktree", "add", "--quiet", "--detach", worktree, commit)
gitFile := filepath.Join(worktree, ".git")
resolved, err := resolveGitFile(gitFile)
if err != nil {
t.Fatal(err)
}
relative, err := filepath.Rel(worktree, resolved)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(gitFile, []byte("gitdir: "+relative+"\n"), 0o644); err != nil {
t.Fatal(err)
}
t.Setenv("PATH", t.TempDir())

content, binary, truncated, err := Blob(context.Background(), worktree, commit, "exact.txt", 5)
if err != nil {
t.Fatalf("Blob: %v", err)
}
if string(content) != "12345" || binary || truncated {
t.Errorf("Blob = (%q, %v, %v), want exact text", content, binary, truncated)
}
}

func TestBlobResolvesAbbreviatedCommitFromSubdirectory(t *testing.T) {
dir, commit := seedBlobRepository(t)
nested := filepath.Join(dir, "nested")
t.Setenv("PATH", t.TempDir())

content, binary, truncated, err := Blob(context.Background(), nested, commit[:7], "exact.txt", 5)
if err != nil {
t.Fatalf("Blob: %v", err)
}
if string(content) != "12345" || binary || truncated {
t.Errorf("Blob = (%q, %v, %v), want exact text", content, binary, truncated)
}
}

func TestBlobReadsBareRepositoryWithoutGitOnPath(t *testing.T) {
dir, commit := seedBlobRepository(t)
bare := filepath.Join(t.TempDir(), "repo.git")
runGitTest(t, dir, "clone", "--quiet", "--bare", dir, bare)
t.Setenv("PATH", t.TempDir())

content, binary, truncated, err := Blob(context.Background(), bare, commit, "exact.txt", 5)
if err != nil {
t.Fatalf("Blob: %v", err)
}
if string(content) != "12345" || binary || truncated {
t.Errorf("Blob = (%q, %v, %v), want exact text", content, binary, truncated)
}
}

func TestBlobPeelsAnnotatedTagWithoutGitOnPath(t *testing.T) {
dir, _ := seedBlobRepository(t)
runGitTest(t, dir, "tag", "-a", "blob-test", "-m", "blob test")
tag := runGitTest(t, dir, "rev-parse", "blob-test^{tag}")
t.Setenv("PATH", t.TempDir())

content, binary, truncated, err := Blob(context.Background(), dir, tag, "exact.txt", 5)
if err != nil {
t.Fatalf("Blob: %v", err)
}
if string(content) != "12345" || binary || truncated {
t.Errorf("Blob = (%q, %v, %v), want exact text", content, binary, truncated)
_, _, _, err := Blob(context.Background(), dir, commit, "exact.txt", 5)
if !errors.Is(err, exec.ErrNotFound) {
t.Fatalf("error = %v, want exec.ErrNotFound", err)
}
}

Expand All @@ -341,20 +253,7 @@ func TestBlobHonorsCanceledContext(t *testing.T) {
}
}

func TestBlobPreservesGoGitAndGitErrors(t *testing.T) {
dir := t.TempDir()
t.Setenv("PATH", t.TempDir())

_, _, _, err := Blob(context.Background(), dir, strings.Repeat("a", 40), "file.txt", 5)
if !errors.Is(err, exec.ErrNotFound) {
t.Errorf("error = %v, want exec.ErrNotFound", err)
}
if err == nil || !strings.Contains(err.Error(), dir) {
t.Errorf("error = %v, want starting path %q", err, dir)
}
}

func TestBlobReadsSHA256RepositoryWithGitFallback(t *testing.T) {
func TestBlobReadsSHA256Repository(t *testing.T) {
requireGit(t)
dir := t.TempDir()
cmd := exec.Command("git", "init", "--quiet", "--object-format=sha256", "-b", "main")
Expand Down
6 changes: 3 additions & 3 deletions doc.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Package clone keeps local checkouts of HTTPS Git repositories. It provides
// shallow clone-or-fetch, exact temporary tag checkouts, bounded retries for
// network failures, a persistent cache, and capped reads and content
// classification for files from commits. Blob reads use go-git in process.
// Clone, fetch, checkout, remote queries, and command retries shell out to the
// git binary, which must be on PATH.
// classification for files from commits. Operations shell out to the git
// binary, which must be on PATH. The github.com/git-pkgs/clone/gogit module
// provides in-process blob reads.
package clone
17 changes: 1 addition & 16 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,4 @@ module github.com/git-pkgs/clone

go 1.25.6

require (
github.com/git-pkgs/magic v0.2.0
github.com/go-git/go-billy/v5 v5.9.1
github.com/go-git/go-git/v5 v5.19.2
)

require (
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/pjbgf/sha1cd v0.6.0 // indirect
golang.org/x/net v0.56.0 // indirect
golang.org/x/sys v0.46.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
require github.com/git-pkgs/magic v0.2.0
70 changes: 0 additions & 70 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,72 +1,2 @@
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/git-pkgs/magic v0.2.0 h1:c7HqVxnP8c88EaVMH0/KraDFVTcmiXckRiSvNZEnvMQ=
github.com/git-pkgs/magic v0.2.0/go.mod h1:3ndidt+yvFaI1M0aEkkzkOlFnLPkeVQASIUojazcxCI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.9.1 h1:8U73XiOTfINdItHVa6z4Gv7ToObcZ6grkqQbLryLCdA=
github.com/go-git/go-billy/v5 v5.9.1/go.mod h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.19.2 h1:wkfn7vOlUBu8ivAWKBWisTiwJK4jYHzTF8Ndv1LyGqY=
github.com/go-git/go-git/v5 v5.19.2/go.mod h1:QqCBE1EFN5ddFmrliLQ3/ntRCUjZU3EJuwuB/jWEHjk=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading