From 148816541edb260daa6502f8fce31fb2b4e792f8 Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Mon, 19 Aug 2024 21:46:23 -0400 Subject: [PATCH 01/12] Add remote taskfile HTTP tests --- task_test.go | 106 ++++++++++++++++++ testdata/includes_remote/.gitignore | 1 + testdata/includes_remote/Taskfile.yml | 4 + testdata/includes_remote/first/Taskfile.yml | 11 ++ .../includes_remote/first/second/Taskfile.yml | 8 ++ 5 files changed, 130 insertions(+) create mode 100644 testdata/includes_remote/.gitignore create mode 100644 testdata/includes_remote/Taskfile.yml create mode 100644 testdata/includes_remote/first/Taskfile.yml create mode 100644 testdata/includes_remote/first/second/Taskfile.yml diff --git a/task_test.go b/task_test.go index 4ca92fb18e..a69c300e01 100644 --- a/task_test.go +++ b/task_test.go @@ -5,6 +5,9 @@ import ( "context" "fmt" "io" + "math/rand" + "net/http" + "net/http/httptest" "os" "path/filepath" "regexp" @@ -12,6 +15,7 @@ import ( "strings" "sync" "testing" + "time" "github.com/Masterminds/semver/v3" "github.com/stretchr/testify/assert" @@ -21,9 +25,12 @@ import ( "github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/internal/experiments" "github.com/go-task/task/v3/internal/filepathext" + "github.com/go-task/task/v3/internal/logger" "github.com/go-task/task/v3/taskfile/ast" ) +var random = rand.New(rand.NewSource(time.Now().UnixNano())) + func init() { _ = os.Setenv("NO_COLOR", "1") } @@ -1042,6 +1049,105 @@ func TestIncludesMultiLevel(t *testing.T) { tt.Run(t) } +func TestIncludesRemote(t *testing.T) { + dir := "testdata/includes_remote" + + srv := httptest.NewServer(http.FileServer(http.Dir(dir))) + defer srv.Close() + + tcs := []struct { + firstRemote string + secondRemote string + }{ + { + firstRemote: srv.URL + "/first/Taskfile.yml", + secondRemote: srv.URL + "/first/second/Taskfile.yml", + }, + { + firstRemote: srv.URL + "/first/Taskfile.yml", + secondRemote: "./second/Taskfile.yml", + }, + } + + tasks := []string{ + "first:write-file", + "first:second:write-file", + } + + for i, tc := range tcs { + t.Run(fmt.Sprint(i), func(t *testing.T) { + t.Setenv("FIRST_REMOTE_URL", tc.firstRemote) + t.Setenv("SECOND_REMOTE_URL", tc.secondRemote) + + var buff SyncBuffer + + executors := []struct { + name string + executor *task.Executor + }{ + { + name: "online, always download", + executor: &task.Executor{ + Dir: dir, + Stdout: &buff, + Stderr: &buff, + Timeout: time.Minute, + Insecure: true, + Logger: &logger.Logger{Stdout: &buff, Stderr: &buff, Verbose: true}, + + // Without caching + AssumeYes: true, + Download: true, + }, + }, + { + name: "offline, use cache", + executor: &task.Executor{ + Dir: dir, + Stdout: &buff, + Stderr: &buff, + Timeout: time.Minute, + Insecure: true, + Logger: &logger.Logger{Stdout: &buff, Stderr: &buff, Verbose: true}, + + // With caching + AssumeYes: false, + Download: false, + Offline: true, + }, + }, + } + + for j, e := range executors { + t.Run(fmt.Sprint(j), func(t *testing.T) { + require.NoError(t, e.executor.Setup()) + + for k, task := range tasks { + t.Run(task, func(t *testing.T) { + expectedContent := fmt.Sprint(random.Int63()) + t.Setenv("CONTENT", expectedContent) + + outputFile := fmt.Sprintf("%d.%d.txt", i, k) + t.Setenv("OUTPUT_FILE", outputFile) + + path := filepath.Join(dir, outputFile) + require.NoError(t, os.RemoveAll(path)) + + require.NoError(t, e.executor.Run(context.Background(), &ast.Call{Task: task})) + + actualContent, err := os.ReadFile(path) + require.NoError(t, err) + assert.Equal(t, expectedContent, strings.TrimSpace(string(actualContent))) + }) + } + }) + } + + t.Log("\noutput:\n", buff.buf.String()) + }) + } +} + func TestIncludeCycle(t *testing.T) { const dir = "testdata/includes_cycle" diff --git a/testdata/includes_remote/.gitignore b/testdata/includes_remote/.gitignore new file mode 100644 index 0000000000..2211df63dd --- /dev/null +++ b/testdata/includes_remote/.gitignore @@ -0,0 +1 @@ +*.txt diff --git a/testdata/includes_remote/Taskfile.yml b/testdata/includes_remote/Taskfile.yml new file mode 100644 index 0000000000..450a6cdab2 --- /dev/null +++ b/testdata/includes_remote/Taskfile.yml @@ -0,0 +1,4 @@ +version: '3' + +includes: + first: "{{ .FIRST_REMOTE_URL }}" \ No newline at end of file diff --git a/testdata/includes_remote/first/Taskfile.yml b/testdata/includes_remote/first/Taskfile.yml new file mode 100644 index 0000000000..f9219caf06 --- /dev/null +++ b/testdata/includes_remote/first/Taskfile.yml @@ -0,0 +1,11 @@ +version: '3' + +includes: + second: "{{ .SECOND_REMOTE_URL }}" + +tasks: + write-file: + requires: + vars: [CONTENT, OUTPUT_FILE] + cmd: | + echo "{{ .CONTENT }}" > "{{ .OUTPUT_FILE }}" diff --git a/testdata/includes_remote/first/second/Taskfile.yml b/testdata/includes_remote/first/second/Taskfile.yml new file mode 100644 index 0000000000..be85a00a21 --- /dev/null +++ b/testdata/includes_remote/first/second/Taskfile.yml @@ -0,0 +1,8 @@ +version: '3' + +tasks: + write-file: + requires: + vars: [CONTENT, OUTPUT_FILE] + cmd: | + echo "{{ .CONTENT }}" > "{{ .OUTPUT_FILE }}" From 101162f0cedabc34dd328a40e61a0cfc29e34837 Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Wed, 21 Aug 2024 16:14:59 -0400 Subject: [PATCH 02/12] Add test data This data will be used by tests in the following commit, which will clone this branch to test git remotes. --- testdata/includes_remote/first/Taskfile.yml | 4 ++++ testdata/includes_remote/first/Taskfile2.yml | 14 ++++++++++++++ testdata/includes_remote/first/neighbor-of-first | 1 + testdata/includes_remote/first/second/Taskfile.yml | 4 ++++ .../includes_remote/first/second/Taskfile2.yml | 14 ++++++++++++++ .../first/second/neighbor-of-second | 1 + 6 files changed, 38 insertions(+) create mode 100644 testdata/includes_remote/first/Taskfile2.yml create mode 100644 testdata/includes_remote/first/neighbor-of-first create mode 100644 testdata/includes_remote/first/second/Taskfile2.yml create mode 100644 testdata/includes_remote/first/second/neighbor-of-second diff --git a/testdata/includes_remote/first/Taskfile.yml b/testdata/includes_remote/first/Taskfile.yml index f9219caf06..b1e5f7deac 100644 --- a/testdata/includes_remote/first/Taskfile.yml +++ b/testdata/includes_remote/first/Taskfile.yml @@ -9,3 +9,7 @@ tasks: vars: [CONTENT, OUTPUT_FILE] cmd: | echo "{{ .CONTENT }}" > "{{ .OUTPUT_FILE }}" + + check-if-neighbor-file-exists: + cmd: | + [[ -f "{{ .TASKFILE_DIR }}/neighbor-of-first" ]] \ No newline at end of file diff --git a/testdata/includes_remote/first/Taskfile2.yml b/testdata/includes_remote/first/Taskfile2.yml new file mode 100644 index 0000000000..b6aad92f52 --- /dev/null +++ b/testdata/includes_remote/first/Taskfile2.yml @@ -0,0 +1,14 @@ +version: '3' + +includes: + second: ./second/Taskfile.yml + +tasks: + write-file: + requires: + vars: [CONTENT, OUTPUT_FILE] + cmd: | + echo "{{ .CONTENT }}" > "{{ .OUTPUT_FILE }}" + + first-taskfile2-task: + cmd: echo {{ .TASK }} diff --git a/testdata/includes_remote/first/neighbor-of-first b/testdata/includes_remote/first/neighbor-of-first new file mode 100644 index 0000000000..ee3b91457f --- /dev/null +++ b/testdata/includes_remote/first/neighbor-of-first @@ -0,0 +1 @@ +This is a file. \ No newline at end of file diff --git a/testdata/includes_remote/first/second/Taskfile.yml b/testdata/includes_remote/first/second/Taskfile.yml index be85a00a21..e79cc6b91d 100644 --- a/testdata/includes_remote/first/second/Taskfile.yml +++ b/testdata/includes_remote/first/second/Taskfile.yml @@ -6,3 +6,7 @@ tasks: vars: [CONTENT, OUTPUT_FILE] cmd: | echo "{{ .CONTENT }}" > "{{ .OUTPUT_FILE }}" + + check-if-neighbor-file-exists: + cmd: | + [[ -f "{{ .TASKFILE_DIR }}/neighbor-of-second" ]] diff --git a/testdata/includes_remote/first/second/Taskfile2.yml b/testdata/includes_remote/first/second/Taskfile2.yml new file mode 100644 index 0000000000..b4b071040b --- /dev/null +++ b/testdata/includes_remote/first/second/Taskfile2.yml @@ -0,0 +1,14 @@ +version: '3' + +includes: + second: ./Taskfile.yml + +tasks: + write-file: + requires: + vars: [CONTENT, OUTPUT_FILE] + cmd: | + echo "{{ .CONTENT }}" > "{{ .OUTPUT_FILE }}" + + second-taskfile2-task: + cmd: echo {{ .TASK }} diff --git a/testdata/includes_remote/first/second/neighbor-of-second b/testdata/includes_remote/first/second/neighbor-of-second new file mode 100644 index 0000000000..524acfffa7 --- /dev/null +++ b/testdata/includes_remote/first/second/neighbor-of-second @@ -0,0 +1 @@ +Test file From 1940bdb3e175b2857e3a6a1485cdcfbf775c5a18 Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Wed, 21 Aug 2024 16:18:01 -0400 Subject: [PATCH 03/12] Add git remote tests --- task_test.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/task_test.go b/task_test.go index a69c300e01..4c27b52643 100644 --- a/task_test.go +++ b/task_test.go @@ -9,6 +9,7 @@ import ( "net/http" "net/http/httptest" "os" + "os/exec" "path/filepath" "regexp" "runtime" @@ -1058,7 +1059,13 @@ func TestIncludesRemote(t *testing.T) { tcs := []struct { firstRemote string secondRemote string + extraTasks []string }{ + // NOTE: When adding content for tests that use `getGitRemoteURL`, + // you must commit the test data for the tests to be able to find it. + // + // These tests will not see data in the working tree because they clone + // this repo. { firstRemote: srv.URL + "/first/Taskfile.yml", secondRemote: srv.URL + "/first/second/Taskfile.yml", @@ -1067,6 +1074,38 @@ func TestIncludesRemote(t *testing.T) { firstRemote: srv.URL + "/first/Taskfile.yml", secondRemote: "./second/Taskfile.yml", }, + { + firstRemote: getGitRemoteURL(t, dir+"/first"), + secondRemote: getGitRemoteURL(t, dir+"/first/second"), + }, + { + firstRemote: srv.URL + "/first/Taskfile.yml", + secondRemote: getGitRemoteURL(t, dir+"/first/second"), + }, + { + firstRemote: getGitRemoteURL(t, dir+"/first"), + secondRemote: srv.URL + "/first/second/Taskfile.yml", + }, + { + firstRemote: getGitRemoteURL(t, dir+"/first"), + secondRemote: "./second/Taskfile.yml", + extraTasks: []string{ + "first:check-if-neighbor-file-exists", + "first:second:check-if-neighbor-file-exists", + }, + }, + { + firstRemote: getGitRemoteURL(t, dir+"/first") + "&taskfile=Taskfile2.yml", + extraTasks: []string{ + "first:first-taskfile2-task", + }, + }, + { + firstRemote: getGitRemoteURL(t, dir+"/first") + "&taskfile=second/Taskfile2.yml", + extraTasks: []string{ + "first:second-taskfile2-task", + }, + }, } tasks := []string{ @@ -1140,6 +1179,12 @@ func TestIncludesRemote(t *testing.T) { assert.Equal(t, expectedContent, strings.TrimSpace(string(actualContent))) }) } + + for _, task := range tc.extraTasks { + t.Run(task, func(t *testing.T) { + require.NoError(t, e.executor.Run(context.Background(), &ast.Call{Task: task})) + }) + } }) } @@ -1148,6 +1193,25 @@ func TestIncludesRemote(t *testing.T) { } } +func getGitRemoteURL(t *testing.T, path string) string { + repoRoot, err := exec.Command("git", "rev-parse", "--show-toplevel").Output() + require.NoError(t, err) + + // This is to support Github Workflows on PRs where we are in a detached HEAD state. + branch := os.Getenv("GITHUB_REF") + if branch == "" { + b, err := exec.Command("git", "branch", "--show-current").Output() + require.NoError(t, err) + branch = string(b) + } + + return fmt.Sprintf("git::file://%s//%s?ref=%s&depth=1", + strings.TrimSpace(string(repoRoot)), + path, + strings.TrimSpace(string(branch)), + ) +} + func TestIncludeCycle(t *testing.T) { const dir = "testdata/includes_cycle" From e1d5aa48446d2a2d2e100f2dfa5a7b5e0e3a76bc Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Wed, 21 Aug 2024 16:18:42 -0400 Subject: [PATCH 04/12] Disable remote caching tests --- task_test.go | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/task_test.go b/task_test.go index 4c27b52643..80093b9044 100644 --- a/task_test.go +++ b/task_test.go @@ -1139,22 +1139,24 @@ func TestIncludesRemote(t *testing.T) { Download: true, }, }, - { - name: "offline, use cache", - executor: &task.Executor{ - Dir: dir, - Stdout: &buff, - Stderr: &buff, - Timeout: time.Minute, - Insecure: true, - Logger: &logger.Logger{Stdout: &buff, Stderr: &buff, Verbose: true}, - - // With caching - AssumeYes: false, - Download: false, - Offline: true, - }, - }, + // Disabled until we add caching support for directories + // + // { + // name: "offline, use cache", + // executor: &task.Executor{ + // Dir: dir, + // Stdout: &buff, + // Stderr: &buff, + // Timeout: time.Minute, + // Insecure: true, + // Logger: &logger.Logger{Stdout: &buff, Stderr: &buff, Verbose: true}, + + // // With caching + // AssumeYes: false, + // Download: false, + // Offline: true, + // }, + // }, } for j, e := range executors { From fa07a9b56e230bfbe18d848216705f1762b97f17 Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Tue, 20 Aug 2024 15:46:02 -0400 Subject: [PATCH 05/12] Add go-getter implementation --- go.mod | 32 +- go.sum | 871 ++++++++++++++++++++++++++++++++++ internal/compiler/compiler.go | 2 +- task_test.go | 47 +- taskfile/ast/location.go | 14 +- taskfile/node.go | 35 +- taskfile/node_file.go | 12 +- taskfile/node_http.go | 117 ----- taskfile/node_remote.go | 333 +++++++++++++ taskfile/node_stdin.go | 7 +- taskfile/reader.go | 7 +- 11 files changed, 1311 insertions(+), 166 deletions(-) delete mode 100644 taskfile/node_http.go create mode 100644 taskfile/node_remote.go diff --git a/go.mod b/go.mod index 99ca0dc052..9838fa4543 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/fatih/color v1.17.0 github.com/go-task/slim-sprig/v3 v3.0.0 github.com/go-task/template v0.1.0 + github.com/hashicorp/go-getter v1.7.5 github.com/joho/godotenv v1.5.1 github.com/mattn/go-zglob v0.0.4 github.com/mitchellh/hashstructure/v2 v2.0.2 @@ -27,13 +28,42 @@ require ( ) require ( + cloud.google.com/go v0.104.0 // indirect + cloud.google.com/go/compute v1.10.0 // indirect + cloud.google.com/go/iam v0.5.0 // indirect + cloud.google.com/go/storage v1.27.0 // indirect + github.com/aws/aws-sdk-go v1.44.122 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/dlclark/regexp2 v1.11.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect + github.com/googleapis/gax-go/v2 v2.6.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-safetemp v1.0.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/klauspost/compress v1.15.11 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/ulikunitz/xz v0.5.10 // indirect + go.opencensus.io v0.23.0 // indirect + golang.org/x/net v0.1.0 // indirect + golang.org/x/oauth2 v0.1.0 // indirect golang.org/x/sys v0.23.0 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect + golang.org/x/text v0.4.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/api v0.100.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect + google.golang.org/grpc v1.50.1 // indirect + google.golang.org/protobuf v1.28.1 // indirect ) diff --git a/go.sum b/go.sum index 3f5db6185d..951e69c525 100644 --- a/go.sum +++ b/go.sum @@ -1,48 +1,392 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0 h1:gSmWO7DY1vOm0MVU6DNXM11BWHHsTUmsC5cv1fuW5X8= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0 h1:aoLIYaA1fX3ywihqpBk2APQKOo20nXsp1GEZQbx5Jk4= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0 h1:fz9X5zyTWBmamZsqvqZqD7khbifcZF/q+Z1J8pfhIUg= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Ladicle/tabwriter v1.0.0 h1:DZQqPvMumBDwVNElso13afjYLNp0Z7pHqHnu0r4t9Dg= github.com/Ladicle/tabwriter v1.0.0/go.mod h1:c4MdCjxQyTbGuQO/gvqJ+IA/89UEwrsD6hUCW98dyp4= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/aws/aws-sdk-go v1.44.122 h1:p6mw01WBaNpbdP2xrisz5tIkcNwzj/HysobNoaAHjgo= +github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo= github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/template v0.1.0 h1:ym/r2G937RZA1bsgiWedNnY9e5kxDT+3YcoAnuIetTE= github.com/go-task/template v0.1.0/go.mod h1:RgwRaZK+kni/hJJ7/AaOE2lPQFPbAdji/DyhC6pxo4k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0 h1:SXk3ABtQYDT/OH8jAyvEOQ58mgawq5C4o/4/89qN2ZU= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-getter v1.7.5 h1:dT58k9hQ/vbxNMwoI5+xFYAJuv6152UNvdHokfI5wE4= +github.com/hashicorp/go-getter v1.7.5/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-zglob v0.0.4 h1:LQi2iOm0/fGgu80AioIJ/1j9w9Oh+9DZ39J4VAGzHQM= github.com/mattn/go-zglob v0.0.4/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= @@ -51,36 +395,563 @@ github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= +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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE= github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/sajari/fuzzy v1.0.0 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY= github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y= +golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.100.0 h1:LGUYIrbW9pzYQQ8NWXlaIVkgnfubVBZbMFb9P8TK374= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 h1:GEgb2jF5zxsFJpJfg9RoDDWm7tiwc/DDSTE2BtLUkXU= +google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= mvdan.cc/sh/v3 v3.9.0 h1:it14fyjCdQUk4jf/aYxLO3FG8jFarR9GzMCtnlvvD7c= mvdan.cc/sh/v3 v3.9.0/go.mod h1:cdBk8bgoiBI7lSZqK5JhUuq7OB64VQ7fgm85xelw3Nk= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/compiler/compiler.go b/internal/compiler/compiler.go index 8627909a7a..b489914b93 100644 --- a/internal/compiler/compiler.go +++ b/internal/compiler/compiler.go @@ -187,7 +187,7 @@ func (c *Compiler) getSpecialVars(t *ast.Task, call *ast.Call) (map[string]strin "ROOT_TASKFILE": filepathext.SmartJoin(c.Dir, c.Entrypoint), "ROOT_DIR": c.Dir, "TASKFILE": t.Location.Taskfile, - "TASKFILE_DIR": filepath.Dir(t.Location.Taskfile), + "TASKFILE_DIR": t.Location.TaskfileDir, "USER_WORKING_DIR": c.UserWorkingDir, "TASK_VERSION": version.GetVersion(), }, nil diff --git a/task_test.go b/task_test.go index 80093b9044..7fdc0d49e7 100644 --- a/task_test.go +++ b/task_test.go @@ -1057,6 +1057,7 @@ func TestIncludesRemote(t *testing.T) { defer srv.Close() tcs := []struct { + rootTaskfile string firstRemote string secondRemote string extraTasks []string @@ -1106,6 +1107,15 @@ func TestIncludesRemote(t *testing.T) { "first:second-taskfile2-task", }, }, + { + firstRemote: srv.URL + "/tasks.zip", + secondRemote: "./second/Taskfile.yml", + }, + { + rootTaskfile: srv.URL + "/Taskfile.yml", + firstRemote: "./first/Taskfile.yml", + secondRemote: "./second/Taskfile.yml", + }, } tasks := []string{ @@ -1118,8 +1128,6 @@ func TestIncludesRemote(t *testing.T) { t.Setenv("FIRST_REMOTE_URL", tc.firstRemote) t.Setenv("SECOND_REMOTE_URL", tc.secondRemote) - var buff SyncBuffer - executors := []struct { name string executor *task.Executor @@ -1127,12 +1135,11 @@ func TestIncludesRemote(t *testing.T) { { name: "online, always download", executor: &task.Executor{ - Dir: dir, - Stdout: &buff, - Stderr: &buff, - Timeout: time.Minute, - Insecure: true, - Logger: &logger.Logger{Stdout: &buff, Stderr: &buff, Verbose: true}, + Dir: dir, + Entrypoint: tc.rootTaskfile, + Timeout: time.Minute, + Insecure: true, + Verbose: true, // Without caching AssumeYes: true, @@ -1142,15 +1149,14 @@ func TestIncludesRemote(t *testing.T) { // Disabled until we add caching support for directories // // { - // name: "offline, use cache", + // name: "offline, use-cache", // executor: &task.Executor{ - // Dir: dir, - // Stdout: &buff, - // Stderr: &buff, - // Timeout: time.Minute, - // Insecure: true, - // Logger: &logger.Logger{Stdout: &buff, Stderr: &buff, Verbose: true}, - + // Dir: dir, + // Entrypoint: tc.rootTaskfile, + // Timeout: time.Minute, + // Insecure: true, + // Verbose: true, + // // // With caching // AssumeYes: false, // Download: false, @@ -1161,6 +1167,13 @@ func TestIncludesRemote(t *testing.T) { for j, e := range executors { t.Run(fmt.Sprint(j), func(t *testing.T) { + var buff SyncBuffer + defer func() { t.Log("\noutput:\n", buff.buf.String()) }() + + e.executor.Stderr = &buff + e.executor.Stdout = &buff + e.executor.Logger = &logger.Logger{Stderr: &buff, Stdout: &buff, Verbose: true} + require.NoError(t, e.executor.Setup()) for k, task := range tasks { @@ -1189,8 +1202,6 @@ func TestIncludesRemote(t *testing.T) { } }) } - - t.Log("\noutput:\n", buff.buf.String()) }) } } diff --git a/taskfile/ast/location.go b/taskfile/ast/location.go index e98da726d8..eec8a27a84 100644 --- a/taskfile/ast/location.go +++ b/taskfile/ast/location.go @@ -1,9 +1,10 @@ package ast type Location struct { - Line int - Column int - Taskfile string + Line int + Column int + Taskfile string + TaskfileDir string } func (l *Location) DeepCopy() *Location { @@ -11,8 +12,9 @@ func (l *Location) DeepCopy() *Location { return nil } return &Location{ - Line: l.Line, - Column: l.Column, - Taskfile: l.Taskfile, + Line: l.Line, + Column: l.Column, + Taskfile: l.Taskfile, + TaskfileDir: l.TaskfileDir, } } diff --git a/taskfile/node.go b/taskfile/node.go index 56b25a16aa..2cdade1b77 100644 --- a/taskfile/node.go +++ b/taskfile/node.go @@ -4,7 +4,6 @@ import ( "context" "os" "path/filepath" - "strings" "time" "github.com/go-task/task/v3/errors" @@ -12,8 +11,14 @@ import ( "github.com/go-task/task/v3/internal/logger" ) +type source struct { + FileContent []byte + FileDirectory string + Filename string +} + type Node interface { - Read(ctx context.Context) ([]byte, error) + Read(ctx context.Context) (*source, error) Parent() Node Location() string Dir() string @@ -46,26 +51,20 @@ func NewNode( timeout time.Duration, opts ...NodeOption, ) (Node, error) { - var node Node - var err error - switch getScheme(entrypoint) { - case "http", "https": - node, err = NewHTTPNode(l, entrypoint, dir, insecure, timeout, opts...) - default: - // If no other scheme matches, we assume it's a file - node, err = NewFileNode(l, entrypoint, dir, opts...) + remote, supported, err := NewRemoteNode(l, entrypoint, dir, insecure, timeout, opts...) + if err != nil { + return nil, err } - if node.Remote() && !experiments.RemoteTaskfiles.Enabled { - return nil, errors.New("task: Remote taskfiles are not enabled. You can read more about this experiment and how to enable it at https://taskfile.dev/experiments/remote-taskfiles") + + if !supported { + // If no other scheme matches, we assume it's a file + return NewFileNode(l, entrypoint, dir, opts...) } - return node, err -} -func getScheme(uri string) string { - if i := strings.Index(uri, "://"); i != -1 { - return uri[:i] + if !experiments.RemoteTaskfiles.Enabled { + return nil, errors.New("task: Remote taskfiles are not enabled. You can read more about this experiment and how to enable it at https://taskfile.dev/experiments/remote-taskfiles") } - return "" + return remote, nil } func getDefaultDir(entrypoint, dir string) string { diff --git a/taskfile/node_file.go b/taskfile/node_file.go index 3e5ec9d69e..e87cf38301 100644 --- a/taskfile/node_file.go +++ b/taskfile/node_file.go @@ -39,13 +39,21 @@ func (node *FileNode) Remote() bool { return false } -func (node *FileNode) Read(ctx context.Context) ([]byte, error) { +func (node *FileNode) Read(ctx context.Context) (*source, error) { f, err := os.Open(node.Location()) if err != nil { return nil, err } defer f.Close() - return io.ReadAll(f) + b, err := io.ReadAll(f) + if err != nil { + return nil, err + } + return &source{ + FileContent: b, + FileDirectory: filepath.Dir(node.Location()), + Filename: filepath.Base(node.Location()), + }, nil } // resolveFileNodeEntrypointAndDir resolves checks the values of entrypoint and dir and diff --git a/taskfile/node_http.go b/taskfile/node_http.go deleted file mode 100644 index e21c2c7130..0000000000 --- a/taskfile/node_http.go +++ /dev/null @@ -1,117 +0,0 @@ -package taskfile - -import ( - "context" - "io" - "net/http" - "net/url" - "path/filepath" - "time" - - "github.com/go-task/task/v3/errors" - "github.com/go-task/task/v3/internal/execext" - "github.com/go-task/task/v3/internal/filepathext" - "github.com/go-task/task/v3/internal/logger" -) - -// An HTTPNode is a node that reads a Taskfile from a remote location via HTTP. -type HTTPNode struct { - *BaseNode - URL *url.URL -} - -func NewHTTPNode( - l *logger.Logger, - entrypoint string, - dir string, - insecure bool, - timeout time.Duration, - opts ...NodeOption, -) (*HTTPNode, error) { - base := NewBaseNode(dir, opts...) - url, err := url.Parse(entrypoint) - if err != nil { - return nil, err - } - if url.Scheme == "http" && !insecure { - return nil, &errors.TaskfileNotSecureError{URI: entrypoint} - } - ctx, cf := context.WithTimeout(context.Background(), timeout) - defer cf() - url, err = RemoteExists(ctx, l, url) - if err != nil { - return nil, err - } - if errors.Is(ctx.Err(), context.DeadlineExceeded) { - return nil, &errors.TaskfileNetworkTimeoutError{URI: url.String(), Timeout: timeout} - } - return &HTTPNode{ - BaseNode: base, - URL: url, - }, nil -} - -func (node *HTTPNode) Location() string { - return node.URL.String() -} - -func (node *HTTPNode) Remote() bool { - return true -} - -func (node *HTTPNode) Read(ctx context.Context) ([]byte, error) { - req, err := http.NewRequest("GET", node.URL.String(), nil) - if err != nil { - return nil, errors.TaskfileFetchFailedError{URI: node.URL.String()} - } - - resp, err := http.DefaultClient.Do(req.WithContext(ctx)) - if err != nil { - return nil, errors.TaskfileFetchFailedError{URI: node.URL.String()} - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, errors.TaskfileFetchFailedError{ - URI: node.URL.String(), - HTTPStatusCode: resp.StatusCode, - } - } - - // Read the entire response body - b, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - return b, nil -} - -func (node *HTTPNode) ResolveEntrypoint(entrypoint string) (string, error) { - ref, err := url.Parse(entrypoint) - if err != nil { - return "", err - } - return node.URL.ResolveReference(ref).String(), nil -} - -func (node *HTTPNode) ResolveDir(dir string) (string, error) { - path, err := execext.Expand(dir) - if err != nil { - return "", err - } - - if filepathext.IsAbs(path) { - return path, nil - } - - // NOTE: Uses the directory of the entrypoint (Taskfile), not the current working directory - // This means that files are included relative to one another - entrypointDir := filepath.Dir(node.Dir()) - return filepathext.SmartJoin(entrypointDir, path), nil -} - -func (node *HTTPNode) FilenameAndLastDir() (string, string) { - dir, filename := filepath.Split(node.URL.Path) - return filepath.Base(dir), filename -} diff --git a/taskfile/node_remote.go b/taskfile/node_remote.go new file mode 100644 index 0000000000..d832a61e5f --- /dev/null +++ b/taskfile/node_remote.go @@ -0,0 +1,333 @@ +package taskfile + +import ( + "context" + "fmt" + "net/url" + "os" + "path/filepath" + "regexp" + "time" + + "github.com/hashicorp/go-getter" + + "github.com/go-task/task/v3/errors" + "github.com/go-task/task/v3/internal/execext" + "github.com/go-task/task/v3/internal/filepathext" + "github.com/go-task/task/v3/internal/logger" +) + +// An RemoteNode is a node that reads a Taskfile from a remote location using go-getter's URL syntax +// +// See https://pkg.go.dev/github.com/hashicorp/go-getter#readme-url-format for details. +type RemoteNode struct { + *BaseNode + + cachedSource *source + client *getter.Client + logger *logger.Logger + proto string + taskfileOverride string + timeout time.Duration + url *url.URL +} + +func NewRemoteNode( + l *logger.Logger, + entrypoint string, + dir string, + insecure bool, + timeout time.Duration, + opts ...NodeOption, +) (*RemoteNode, bool, error) { + + client := newGetterClient(dir) + proto, u, err := extractProtocolFromURL(client, entrypoint) + if err != nil { + return nil, false, fmt.Errorf("error parsing remote protocol %s: %w", entrypoint, err) + } + + if proto == "file" { + // We don't support go-getter's file implementation because it doesn't give direct access to the file, + // returning a symlink instead. We prefer to rely on our own implementation. + return nil, false, nil + } + + u, err = resolveHTTPEntrypoint(l, insecure, timeout, u) + if err != nil { + return nil, false, err + } + + var tf string + tf, u = resolveTaskfileOverride(u) + + return &RemoteNode{ + BaseNode: NewBaseNode(dir, opts...), + client: client, + logger: l, + proto: proto, + taskfileOverride: tf, + timeout: timeout, + url: u, + }, true, nil +} + +func (r *RemoteNode) Location() string { + return r.proto + "::" + r.url.String() +} + +func (r *RemoteNode) Remote() bool { + return true +} + +func (r *RemoteNode) Read(ctx context.Context) (*source, error) { + return r.loadSource(ctx) +} + +func (r *RemoteNode) ResolveEntrypoint(entrypoint string) (string, error) { + childProto, _, err := extractProtocolFromURL(r.client, entrypoint) + if err != nil { + return "", fmt.Errorf("could not resolve protocol for include %s: %w", entrypoint, err) + } + + switch { + case childProto != "file": + return entrypoint, nil + + case filepath.IsAbs(entrypoint): + return entrypoint, nil + + case r.proto == "http" || r.proto == "https": + // In HTTP, relative includes aren't available locally and are downloaded from the same base URL. + base := *r.url + base.Path = filepath.Join(filepath.Dir(base.Path), entrypoint) + + return base.String(), nil + + default: + ctx, cancel := context.WithTimeout(context.Background(), r.timeout) + defer cancel() + + src, err := r.loadSource(ctx) + if err != nil { + return "", err + } + + return filepathext.SmartJoin(src.FileDirectory, entrypoint), nil + } +} + +func (r *RemoteNode) ResolveDir(dir string) (string, error) { + path, err := execext.Expand(dir) + if err != nil { + return "", err + } + + if filepathext.IsAbs(path) { + return path, nil + } + + // NOTE: Uses the directory of the entrypoint (Taskfile), not the current working directory + // This means that files are included relative to one another + entrypointDir := filepath.Dir(r.Dir()) + return filepathext.SmartJoin(entrypointDir, path), nil +} + +func (r *RemoteNode) FilenameAndLastDir() (string, string) { + u, path := getter.SourceDirSubdir(r.url.String()) + + u2, _ := url.Parse(u) + fullPath := filepath.Join(u2.Path, path) + + dir, filename := filepath.Split(fullPath) + return filepath.Base(dir), filename +} + +func (r *RemoteNode) loadSource(ctx context.Context) (*source, error) { + if r.cachedSource == nil { + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetching remote taskfile from %s\n", r.Location(), r.client.Src) + + dir, err := os.MkdirTemp("", "taskfile-remote-") + if err != nil { + return nil, err + } + r.client.Ctx = ctx + r.client.Src = r.Location() + r.client.Dst = dir + + if err := r.client.Get(); err != nil { + return nil, err + } + + taskfile, err := r.resolveTaskfilePath(r.logger, dir) + if err != nil { + return nil, err + } + + b, err := os.ReadFile(taskfile) + if err != nil { + return nil, err + } + + r.cachedSource = &source{ + FileContent: b, + FileDirectory: filepath.Dir(taskfile), + Filename: filepath.Base(taskfile), + } + } + + return r.cachedSource, nil +} + +func (r *RemoteNode) resolveTaskfilePath(l *logger.Logger, dir string) (string, error) { + // If there's a single file in the directory, use that + // If there's a default taskfile name, use that + entries, err := os.ReadDir(dir) + if err != nil { + return "", err + } + + if len(entries) == 1 { + return filepath.Join(dir, entries[0].Name()), nil + } + + if r.taskfileOverride != "" { + return filepath.Join(dir, r.taskfileOverride), nil + } + + return Exists(l, dir) +} + +func newGetterClient(dir string) *getter.Client { + httpGetter := &httpGetter{ + HttpGetter: &getter.HttpGetter{ + XTerraformGetDisabled: true, + }, + } + + client := &getter.Client{ + // Src and Dst are intentioally left empty, they will be set before the call to client.Get() + // because that's when these values are known. + Src: "", + Dst: "", + + Mode: getter.ClientModeAny, + Pwd: dir, + Getters: map[string]getter.Getter{ + "git": &getter.GitGetter{}, + "gcs": &getter.GCSGetter{}, + "hg": &getter.HgGetter{}, + "s3": &getter.S3Getter{}, + "http": httpGetter, + "https": httpGetter, + }, + Detectors: []getter.Detector{ + &getter.GitHubDetector{}, + &getter.GitLabDetector{}, + &getter.GitDetector{}, + &getter.BitBucketDetector{}, + &getter.S3Detector{}, + &getter.GCSDetector{}, + &getter.FileDetector{}, + }, + + DisableSymlinks: true, + } + + // Force client to have defaults configured + client.Configure() + return client +} + +var getterURLRegexp = regexp.MustCompile(`^([A-Za-z0-9]+)::(.+)$`) + +func extractProtocolFromURL(client *getter.Client, src string) (string, *url.URL, error) { + if src == "" { + // If empty we assume current directory and let NodeFile logic deal with finding and appropriate file + u, err := url.Parse(".") + return "file", u, err + } + + parsed, err := getter.Detect(src, client.Pwd, client.Detectors) + if err != nil { + return "", nil, err + } + + var proto string + if ms := getterURLRegexp.FindStringSubmatch(parsed); ms != nil { + proto = ms[1] + parsed = ms[2] + } + + u, err := url.Parse(parsed) + if err != nil { + return "", nil, err + } + + if proto == "" { + proto = u.Scheme + } + + return proto, u, nil +} + +func resolveHTTPEntrypoint(l *logger.Logger, insecure bool, timeout time.Duration, u *url.URL) (*url.URL, error) { + if u.Scheme != "http" && u.Scheme != "https" { + return u, nil + } + + if u.Scheme == "http" && !insecure { + return nil, &errors.TaskfileNotSecureError{URI: u.String()} + } + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + // HTTP and HTTPS entrypoint get filename auto-resolution before being handed off to go-getter + u, err := RemoteExists(ctx, l, u) + if err != nil { + return nil, err + } else if ctx.Err() != nil { + return nil, &errors.TaskfileNetworkTimeoutError{URI: u.String(), Timeout: timeout} + } + + return u, nil +} + +func resolveTaskfileOverride(u *url.URL) (string, *url.URL) { + q := u.Query() + + if f := q.Get("taskfile"); f != "" { + // Delete magic parameter so we don't pass it onto further steps + u2 := *u + q.Del("taskfile") + u2.RawQuery = q.Encode() + + return f, &u2 + } + + return "", u +} + +// httpGetter wraps getter.HttpGetter to give us the ability +// to download single files into a directory, like other getters would +type httpGetter struct { + *getter.HttpGetter +} + +func (h httpGetter) Get(dst string, src *url.URL) error { + // getter.HttpGetter does not support ClientModeDir for downloading directories (except when using the X-Terraform-Get feature, which we do not use). + // By deferring to Get(), we allow this Getter to work in ClientModeDir while only downloading a single file. + + // In our case, dst is always a directory, so we append the filename to it. + dst = filepath.Join(dst, filepath.Base(src.Path)) + + return h.HttpGetter.GetFile(dst, src) +} + +func (h httpGetter) ClientMode(*url.URL) (getter.ClientMode, error) { + // We force ClientModeDir so we always get the same behaviour whether we're + // requesting a file or a directory. The override of Get() ensures this works + // properly for both. + return getter.ClientModeDir, nil +} diff --git a/taskfile/node_stdin.go b/taskfile/node_stdin.go index 3855415dae..9d1f66e77e 100644 --- a/taskfile/node_stdin.go +++ b/taskfile/node_stdin.go @@ -30,7 +30,7 @@ func (node *StdinNode) Remote() bool { return false } -func (node *StdinNode) Read(ctx context.Context) ([]byte, error) { +func (node *StdinNode) Read(ctx context.Context) (*source, error) { var stdin []byte scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { @@ -39,7 +39,10 @@ func (node *StdinNode) Read(ctx context.Context) ([]byte, error) { if err := scanner.Err(); err != nil { return nil, err } - return stdin, nil + return &source{ + FileContent: stdin, + Filename: node.Location(), + }, nil } func (node *StdinNode) ResolveEntrypoint(entrypoint string) (string, error) { diff --git a/taskfile/reader.go b/taskfile/reader.go index 32783dea66..2b971e2597 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -184,6 +184,7 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { var b []byte var err error var cache *Cache + source := &source{} if node.Remote() { cache, err = NewCache(r.tempDir) @@ -207,7 +208,7 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { defer cf() // Read the file - b, err = node.Read(ctx) + source, err = node.Read(ctx) // If we timed out then we likely have a network issue if node.Remote() && errors.Is(ctx.Err(), context.DeadlineExceeded) { // If a download was requested, then we can't use a cached copy @@ -225,6 +226,7 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { return nil, err } else { downloaded = true + b = source.FileContent } // If the node was remote, we need to check the checksum @@ -290,6 +292,9 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { if task.Location.Taskfile == "" { task.Location.Taskfile = tf.Location } + if task.Location.TaskfileDir == "" { + task.Location.TaskfileDir = source.FileDirectory + } } return &tf, nil From 86f986f69ccf35c2de8586de442ae56639bfe701 Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Wed, 21 Aug 2024 16:00:53 -0400 Subject: [PATCH 06/12] Add caching support for remote directories --- task_test.go | 81 +++++-- taskfile/cache.go | 206 ++++++++++++++++-- taskfile/node_remote.go | 56 +++-- taskfile/reader.go | 52 +++-- taskfile/taskfile.go | 1 + testdata/includes_remote/.gitignore | 1 + .../includes_remote/first/neighbor-of-first | 2 +- 7 files changed, 327 insertions(+), 72 deletions(-) diff --git a/task_test.go b/task_test.go index 7fdc0d49e7..9d14e5c001 100644 --- a/task_test.go +++ b/task_test.go @@ -1,6 +1,7 @@ package task_test import ( + "archive/zip" "bytes" "context" "fmt" @@ -1053,20 +1054,32 @@ func TestIncludesMultiLevel(t *testing.T) { func TestIncludesRemote(t *testing.T) { dir := "testdata/includes_remote" + os.RemoveAll(filepath.Join(dir, ".task")) + srv := httptest.NewServer(http.FileServer(http.Dir(dir))) defer srv.Close() + createZipFileOfDir(t, filepath.Join(dir, "tasks-root.zip"), dir) + createZipFileOfDir(t, filepath.Join(dir, "tasks-first.zip"), filepath.Join(dir, "first")) + tcs := []struct { rootTaskfile string firstRemote string secondRemote string extraTasks []string }{ + // // NOTE: When adding content for tests that use `getGitRemoteURL`, // you must commit the test data for the tests to be able to find it. // // These tests will not see data in the working tree because they clone // this repo. + // + { + // Ensure non-remote includes still work + firstRemote: "./first/Taskfile.yml", + secondRemote: "./second/Taskfile.yml", + }, { firstRemote: srv.URL + "/first/Taskfile.yml", secondRemote: srv.URL + "/first/second/Taskfile.yml", @@ -1108,14 +1121,36 @@ func TestIncludesRemote(t *testing.T) { }, }, { - firstRemote: srv.URL + "/tasks.zip", + firstRemote: srv.URL + "/tasks-first.zip", secondRemote: "./second/Taskfile.yml", + extraTasks: []string{ + "first:check-if-neighbor-file-exists", + "first:second:check-if-neighbor-file-exists", + }, }, { rootTaskfile: srv.URL + "/Taskfile.yml", firstRemote: "./first/Taskfile.yml", secondRemote: "./second/Taskfile.yml", }, + { + rootTaskfile: getGitRemoteURL(t, dir), + firstRemote: "./first/Taskfile.yml", + secondRemote: "./second/Taskfile.yml", + extraTasks: []string{ + "first:check-if-neighbor-file-exists", + "first:second:check-if-neighbor-file-exists", + }, + }, + { + rootTaskfile: srv.URL + "/tasks-root.zip", + firstRemote: "./first/Taskfile.yml", + secondRemote: "./second/Taskfile.yml", + extraTasks: []string{ + "first:check-if-neighbor-file-exists", + "first:second:check-if-neighbor-file-exists", + }, + }, } tasks := []string{ @@ -1144,25 +1179,24 @@ func TestIncludesRemote(t *testing.T) { // Without caching AssumeYes: true, Download: true, + Offline: false, + }, + }, + { + name: "offline, use-cache", + executor: &task.Executor{ + Dir: dir, + Entrypoint: tc.rootTaskfile, + Timeout: time.Minute, + Insecure: true, + Verbose: true, + + // With caching + AssumeYes: false, + Download: false, + Offline: true, }, }, - // Disabled until we add caching support for directories - // - // { - // name: "offline, use-cache", - // executor: &task.Executor{ - // Dir: dir, - // Entrypoint: tc.rootTaskfile, - // Timeout: time.Minute, - // Insecure: true, - // Verbose: true, - // - // // With caching - // AssumeYes: false, - // Download: false, - // Offline: true, - // }, - // }, } for j, e := range executors { @@ -1206,6 +1240,17 @@ func TestIncludesRemote(t *testing.T) { } } +func createZipFileOfDir(t *testing.T, zipFilePath string, dir string) { + f, err := os.OpenFile(zipFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) + require.NoError(t, err) + defer f.Close() + + w := zip.NewWriter(f) + err = w.AddFS(os.DirFS(dir)) + require.NoError(t, err) + w.Close() +} + func getGitRemoteURL(t *testing.T, path string) string { repoRoot, err := exec.Command("git", "rev-parse", "--show-toplevel").Output() require.NoError(t, err) diff --git a/taskfile/cache.go b/taskfile/cache.go index 2b57c17dd8..62e4148659 100644 --- a/taskfile/cache.go +++ b/taskfile/cache.go @@ -6,12 +6,21 @@ import ( "os" "path/filepath" "strings" + + "gopkg.in/yaml.v3" + + "github.com/go-task/task/v3/errors" ) type Cache struct { dir string } +type metadata struct { + Checksum string + TaskfileName string +} + func NewCache(dir string) (*Cache, error) { dir = filepath.Join(dir, "remote") if err := os.MkdirAll(dir, 0o755); err != nil { @@ -25,46 +34,207 @@ func NewCache(dir string) (*Cache, error) { func checksum(b []byte) string { h := sha256.New() h.Write(b) - return fmt.Sprintf("%x", h.Sum(nil)) + return fmt.Sprintf("%x", h.Sum(nil))[:16] } -func (c *Cache) write(node Node, b []byte) error { - return os.WriteFile(c.cacheFilePath(node), b, 0o644) +func checksumSource(s source) (string, error) { + h := sha256.New() + + entries, err := os.ReadDir(s.FileDirectory) + if err != nil { + return "", fmt.Errorf("could not list files at %s: %w", s.FileDirectory, err) + } + + for _, e := range entries { + if e.Type().IsRegular() { + path := filepath.Join(s.FileDirectory, e.Name()) + f, err := os.Open(path) + if err != nil { + return "", fmt.Errorf("error opening file %s for checksumming: %w", path, err) + } + if _, err := f.WriteTo(h); err != nil { + f.Close() + return "", fmt.Errorf("error reading file %s for checksumming: %w", path, err) + } + f.Close() + } + } + return fmt.Sprintf("%x", h.Sum(nil))[:16], nil } -func (c *Cache) read(node Node) ([]byte, error) { - return os.ReadFile(c.cacheFilePath(node)) +func (c *Cache) write(node Node, src source) (*source, error) { + // Clear metadata file so that if the rest of the operations fail part-way we don't + // end up in an inconsistent state where we've written the contents but have old metadata + if err := c.clearMetadata(node); err != nil { + return nil, err + } + + p, err := c.contentsPath(node) + if err != nil { + return nil, err + } + + switch fi, err := os.Stat(p); { + case errors.Is(err, os.ErrNotExist): + // Nothign to clear, do nothing + + case !fi.IsDir(): + return nil, fmt.Errorf("error writing to contents path %s: not a directory", p) + + case err != nil: + return nil, fmt.Errorf("error cheacking for previous contents path %s: %w", p, err) + + default: + err := os.RemoveAll(p) + if err != nil { + return nil, fmt.Errorf("error clearing contents directory: %s", err) + } + } + + if err := os.Rename(src.FileDirectory, p); err != nil { + return nil, err + } + + // TODO Clean up + src.FileDirectory = p + + cs, err := checksumSource(src) + if err != nil { + return nil, err + } + + m := metadata{ + Checksum: cs, + TaskfileName: src.Filename, + } + + if err := c.storeMetadata(node, m); err != nil { + return nil, fmt.Errorf("error storing metadata for node %s: %w", node.Location(), err) + } + + return &src, nil } -func (c *Cache) writeChecksum(node Node, checksum string) error { - return os.WriteFile(c.checksumFilePath(node), []byte(checksum), 0o644) +func (c *Cache) read(node Node) (*source, error) { + path, err := c.contentsPath(node) + if err != nil { + return nil, err + } + + m, err := c.readMetadata(node) + if err != nil { + return nil, err + } + + taskfileName := m.TaskfileName + + content, err := os.ReadFile(filepath.Join(path, m.TaskfileName)) + if err != nil { + return nil, err + } + + return &source{ + FileContent: content, + FileDirectory: path, + Filename: taskfileName, + }, nil } func (c *Cache) readChecksum(node Node) string { - b, _ := os.ReadFile(c.checksumFilePath(node)) - return string(b) + m, err := c.readMetadata(node) + if err != nil { + return "" + } + return m.Checksum +} + +func (c *Cache) clearMetadata(node Node) error { + path, err := c.metadataFilePath(node) + if err != nil { + return fmt.Errorf("error clearing metadata file at %s: %w", path, err) + } + + fi, err := os.Stat(path) + if errors.Is(err, os.ErrNotExist) { + return nil + } + + if !fi.Mode().IsRegular() { + return fmt.Errorf("path is not a real file when trying to delete metadata file: %s", path) + } + + // if err := os.Remove(path) + if err := os.Remove(path); err != nil { + return fmt.Errorf("error removing metadata file %s: %w", path, err) + } + + return nil +} + +func (c *Cache) storeMetadata(node Node, m metadata) error { + path, err := c.metadataFilePath(node) + if err != nil { + return err + } + + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) + if err != nil { + return fmt.Errorf("error creating metadata file %s: %w", path, err) + } + defer f.Close() + + if err := yaml.NewEncoder(f).Encode(m); err != nil { + return fmt.Errorf("error writing metadata into %s: %w", path, err) + } + + return nil +} + +func (c *Cache) readMetadata(node Node) (*metadata, error) { + path, err := c.metadataFilePath(node) + if err != nil { + return nil, err + } + + f, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("error opening metadata file %s: %w", path, err) + } + defer f.Close() + + var m *metadata + if err := yaml.NewDecoder(f).Decode(&m); err != nil { + return nil, fmt.Errorf("error reading metadata file %s: %w", path, err) + } + + return m, nil } func (c *Cache) key(node Node) string { return strings.TrimRight(checksum([]byte(node.Location())), "=") } -func (c *Cache) cacheFilePath(node Node) string { - return c.filePath(node, "yaml") +func (c *Cache) contentsPath(node Node) (string, error) { + return c.cacheFilePath(node, "contents") } -func (c *Cache) checksumFilePath(node Node) string { - return c.filePath(node, "checksum") +func (c *Cache) metadataFilePath(node Node) (string, error) { + return c.cacheFilePath(node, "metadata.yaml") } -func (c *Cache) filePath(node Node, suffix string) string { - lastDir, filename := node.FilenameAndLastDir() - prefix := filename +func (c *Cache) cacheFilePath(node Node, filename string) (string, error) { + lastDir, prefix := node.FilenameAndLastDir() // Means it's not "", nor "." nor "/", so it's a valid directory if len(lastDir) > 1 { - prefix = fmt.Sprintf("%s-%s", lastDir, filename) + prefix = fmt.Sprintf("%s-%s", lastDir, prefix) } - return filepath.Join(c.dir, fmt.Sprintf("%s.%s.%s", prefix, c.key(node), suffix)) + + dir := filepath.Join(c.dir, fmt.Sprintf("%s.%s", prefix, c.key(node))) + if err := os.MkdirAll(dir, 0o755); err != nil { + return "", fmt.Errorf("error creating cache dir %s: %w", dir, err) + } + + return filepath.Join(dir, filename), nil } func (c *Cache) Clear() error { diff --git a/taskfile/node_remote.go b/taskfile/node_remote.go index d832a61e5f..660104d2bc 100644 --- a/taskfile/node_remote.go +++ b/taskfile/node_remote.go @@ -40,7 +40,6 @@ func NewRemoteNode( timeout time.Duration, opts ...NodeOption, ) (*RemoteNode, bool, error) { - client := newGetterClient(dir) proto, u, err := extractProtocolFromURL(client, entrypoint) if err != nil { @@ -90,21 +89,8 @@ func (r *RemoteNode) ResolveEntrypoint(entrypoint string) (string, error) { return "", fmt.Errorf("could not resolve protocol for include %s: %w", entrypoint, err) } - switch { - case childProto != "file": - return entrypoint, nil - - case filepath.IsAbs(entrypoint): - return entrypoint, nil - - case r.proto == "http" || r.proto == "https": - // In HTTP, relative includes aren't available locally and are downloaded from the same base URL. - base := *r.url - base.Path = filepath.Join(filepath.Dir(base.Path), entrypoint) - - return base.String(), nil - - default: + if childProto == "file" && !filepath.IsAbs(entrypoint) { + // Relative file paths are resolved as relative to our own source location ctx, cancel := context.WithTimeout(context.Background(), r.timeout) defer cancel() @@ -113,8 +99,26 @@ func (r *RemoteNode) ResolveEntrypoint(entrypoint string) (string, error) { return "", err } - return filepathext.SmartJoin(src.FileDirectory, entrypoint), nil + relativePath := filepath.Join(src.FileDirectory, entrypoint) + if exists, err := fileExists(relativePath); err != nil { + return "", err + } else if exists { + return relativePath, nil + } + + if r.proto == "http" || r.proto == "https" { + // In HTTP, if relative includes are not available locally (eg. from a ZIP file), + // we try to download them relative to the base URL. + rel, err := url.Parse(entrypoint) + if err != nil { + return "", fmt.Errorf("error parsing entrypoint %s as url: %w", entrypoint, err) + } + + return r.url.ResolveReference(rel).String(), nil + } } + + return entrypoint, nil } func (r *RemoteNode) ResolveDir(dir string) (string, error) { @@ -145,16 +149,16 @@ func (r *RemoteNode) FilenameAndLastDir() (string, string) { func (r *RemoteNode) loadSource(ctx context.Context) (*source, error) { if r.cachedSource == nil { - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetching remote taskfile from %s\n", r.Location(), r.client.Src) - dir, err := os.MkdirTemp("", "taskfile-remote-") if err != nil { return nil, err } + r.client.Ctx = ctx r.client.Src = r.Location() r.client.Dst = dir + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetching remote taskfile from %s into %s\n", r.Location(), r.client.Src, r.client.Dst) if err := r.client.Get(); err != nil { return nil, err } @@ -243,7 +247,8 @@ var getterURLRegexp = regexp.MustCompile(`^([A-Za-z0-9]+)::(.+)$`) func extractProtocolFromURL(client *getter.Client, src string) (string, *url.URL, error) { if src == "" { - // If empty we assume current directory and let NodeFile logic deal with finding and appropriate file + // If empty we assume current directory and type `file` and let the FileNode logic + // deal with finding and appropriate file. u, err := url.Parse(".") return "file", u, err } @@ -309,6 +314,17 @@ func resolveTaskfileOverride(u *url.URL) (string, *url.URL) { return "", u } +func fileExists(path string) (bool, error) { + switch _, err := os.Stat(path); { + case errors.Is(err, os.ErrNotExist): + return false, nil + case err != nil: + return false, fmt.Errorf("error checking if file exists at %s: %w", path, err) + default: + return true, nil + } +} + // httpGetter wraps getter.HttpGetter to give us the ability // to download single files into a directory, like other getters would type httpGetter struct { diff --git a/taskfile/reader.go b/taskfile/reader.go index 2b971e2597..53a5cb2fc9 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -181,10 +181,11 @@ func (r *Reader) include(node Node) error { } func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { - var b []byte - var err error - var cache *Cache - source := &source{} + var ( + err error + cache *Cache + source *source + ) if node.Remote() { cache, err = NewCache(r.tempDir) @@ -195,11 +196,19 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { // If the file is remote and we're in offline mode, check if we have a cached copy if node.Remote() && r.offline { - if b, err = cache.read(node); errors.Is(err, os.ErrNotExist) { + if source, err = cache.read(node); errors.Is(err, os.ErrNotExist) { return nil, &errors.TaskfileCacheNotFoundError{URI: node.Location()} } else if err != nil { return nil, err } + + // TODO: Find a cleaner way to override source when loading from the cache + // Without this later usages of ResolveEntrypoint will be relative to the old source location + // fr before it got moved into the cache. + if n, ok := node.(*RemoteNode); ok { + n.cachedSource = source + } + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched cached copy\n", node.Location()) } else { @@ -216,17 +225,24 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout} } // Search for any cached copies - if b, err = cache.read(node); errors.Is(err, os.ErrNotExist) { + if source, err = cache.read(node); errors.Is(err, os.ErrNotExist) { return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout, CheckedCache: true} } else if err != nil { return nil, err } r.logger.VerboseOutf(logger.Magenta, "task: [%s] Network timeout. Fetched cached copy\n", node.Location()) + + // TODO: Find a cleaner way to override source when loading from the cache + // Without this later usages of ResolveEntrypoint will be relative to the old source location + // fr before it got moved into the cache. + if n, ok := node.(*RemoteNode); ok { + n.cachedSource = source + } + } else if err != nil { return nil, err } else { downloaded = true - b = source.FileContent } // If the node was remote, we need to check the checksum @@ -234,8 +250,11 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched remote copy\n", node.Location()) // Get the checksums - checksum := checksum(b) cachedChecksum := cache.readChecksum(node) + checksum, err := checksumSource(*source) + if err != nil { + return nil, err + } var prompt string if cachedChecksum == "" { @@ -253,25 +272,28 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { // If the hash has changed (or is new) if checksum != cachedChecksum { - // Store the checksum - if err := cache.writeChecksum(node, checksum); err != nil { - return nil, err - } // Cache the file r.logger.VerboseOutf(logger.Magenta, "task: [%s] Caching downloaded file\n", node.Location()) - if err = cache.write(node, b); err != nil { + if source, err = cache.write(node, *source); err != nil { return nil, err } + + // TODO: Find a cleaner way to override source when loading from the cache + // Without this later usages of ResolveEntrypoint will be relative to the old source location + // fr before it got moved into the cache. + if n, ok := node.(*RemoteNode); ok { + n.cachedSource = source + } } } } var tf ast.Taskfile - if err := yaml.Unmarshal(b, &tf); err != nil { + if err := yaml.Unmarshal(source.FileContent, &tf); err != nil { // Decode the taskfile and add the file info the any errors taskfileInvalidErr := &errors.TaskfileDecodeError{} if errors.As(err, &taskfileInvalidErr) { - return nil, taskfileInvalidErr.WithFileInfo(node.Location(), b, 2) + return nil, taskfileInvalidErr.WithFileInfo(node.Location(), source.FileContent, 2) } return nil, &errors.TaskfileInvalidError{URI: filepathext.TryAbsToRel(node.Location()), Err: err} } diff --git a/taskfile/taskfile.go b/taskfile/taskfile.go index ca915eae75..e7642067f9 100644 --- a/taskfile/taskfile.go +++ b/taskfile/taskfile.go @@ -32,6 +32,7 @@ var ( "text/x-yaml", "application/yaml", "application/x-yaml", + "application/zip", } ) diff --git a/testdata/includes_remote/.gitignore b/testdata/includes_remote/.gitignore index 2211df63dd..8d8a34eeab 100644 --- a/testdata/includes_remote/.gitignore +++ b/testdata/includes_remote/.gitignore @@ -1 +1,2 @@ *.txt +*.zip \ No newline at end of file diff --git a/testdata/includes_remote/first/neighbor-of-first b/testdata/includes_remote/first/neighbor-of-first index ee3b91457f..4dd1ef7569 100644 --- a/testdata/includes_remote/first/neighbor-of-first +++ b/testdata/includes_remote/first/neighbor-of-first @@ -1 +1 @@ -This is a file. \ No newline at end of file +This is a file. From 4e7a2f3331bdfe08b497c2cedfcc8c6dbdda5e31 Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Wed, 21 Aug 2024 16:22:59 -0400 Subject: [PATCH 07/12] TEMP commit: applying caching refactor from #1771 --- taskfile/reader.go | 192 ++++++++++++++++++++------------------------- 1 file changed, 87 insertions(+), 105 deletions(-) diff --git a/taskfile/reader.go b/taskfile/reader.go index 53a5cb2fc9..12ef61c185 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -181,111 +181,9 @@ func (r *Reader) include(node Node) error { } func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { - var ( - err error - cache *Cache - source *source - ) - - if node.Remote() { - cache, err = NewCache(r.tempDir) - if err != nil { - return nil, err - } - } - - // If the file is remote and we're in offline mode, check if we have a cached copy - if node.Remote() && r.offline { - if source, err = cache.read(node); errors.Is(err, os.ErrNotExist) { - return nil, &errors.TaskfileCacheNotFoundError{URI: node.Location()} - } else if err != nil { - return nil, err - } - - // TODO: Find a cleaner way to override source when loading from the cache - // Without this later usages of ResolveEntrypoint will be relative to the old source location - // fr before it got moved into the cache. - if n, ok := node.(*RemoteNode); ok { - n.cachedSource = source - } - - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched cached copy\n", node.Location()) - } else { - - downloaded := false - ctx, cf := context.WithTimeout(context.Background(), r.timeout) - defer cf() - - // Read the file - source, err = node.Read(ctx) - // If we timed out then we likely have a network issue - if node.Remote() && errors.Is(ctx.Err(), context.DeadlineExceeded) { - // If a download was requested, then we can't use a cached copy - if r.download { - return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout} - } - // Search for any cached copies - if source, err = cache.read(node); errors.Is(err, os.ErrNotExist) { - return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout, CheckedCache: true} - } else if err != nil { - return nil, err - } - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Network timeout. Fetched cached copy\n", node.Location()) - - // TODO: Find a cleaner way to override source when loading from the cache - // Without this later usages of ResolveEntrypoint will be relative to the old source location - // fr before it got moved into the cache. - if n, ok := node.(*RemoteNode); ok { - n.cachedSource = source - } - - } else if err != nil { - return nil, err - } else { - downloaded = true - } - - // If the node was remote, we need to check the checksum - if node.Remote() && downloaded { - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched remote copy\n", node.Location()) - - // Get the checksums - cachedChecksum := cache.readChecksum(node) - checksum, err := checksumSource(*source) - if err != nil { - return nil, err - } - - var prompt string - if cachedChecksum == "" { - // If the checksum doesn't exist, prompt the user to continue - prompt = fmt.Sprintf(taskfileUntrustedPrompt, node.Location()) - } else if checksum != cachedChecksum { - // If there is a cached hash, but it doesn't match the expected hash, prompt the user to continue - prompt = fmt.Sprintf(taskfileChangedPrompt, node.Location()) - } - if prompt != "" { - if err := r.logger.Prompt(logger.Yellow, prompt, "n", "y", "yes"); err != nil { - return nil, &errors.TaskfileNotTrustedError{URI: node.Location()} - } - } - - // If the hash has changed (or is new) - if checksum != cachedChecksum { - // Cache the file - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Caching downloaded file\n", node.Location()) - if source, err = cache.write(node, *source); err != nil { - return nil, err - } - - // TODO: Find a cleaner way to override source when loading from the cache - // Without this later usages of ResolveEntrypoint will be relative to the old source location - // fr before it got moved into the cache. - if n, ok := node.(*RemoteNode); ok { - n.cachedSource = source - } - } - } + source, err := r.loadNodeContent(node) + if err != nil { + return nil, err } var tf ast.Taskfile @@ -321,3 +219,87 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { return &tf, nil } + +func (r *Reader) loadNodeContent(node Node) (*source, error) { + if !node.Remote() { + ctx, cf := context.WithTimeout(context.Background(), r.timeout) + defer cf() + return node.Read(ctx) + } + + cache, err := NewCache(r.tempDir) + if err != nil { + return nil, err + } + + if r.offline { + // In offline mode try to use cached copy + cached, err := cache.read(node) + if errors.Is(err, os.ErrNotExist) { + return nil, &errors.TaskfileCacheNotFoundError{URI: node.Location()} + } else if err != nil { + return nil, err + } + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched cached copy\n", node.Location()) + + return cached, nil + } + + ctx, cf := context.WithTimeout(context.Background(), r.timeout) + defer cf() + + src, err := node.Read(ctx) + if errors.Is(ctx.Err(), context.DeadlineExceeded) { + // If we timed out then we likely have a network issue + + // If a download was requested, then we can't use a cached copy + if r.download { + return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout} + } + + // Search for any cached copies + cached, err := cache.read(node) + if errors.Is(err, os.ErrNotExist) { + return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout, CheckedCache: true} + } else if err != nil { + return nil, err + } + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Network timeout. Fetched cached copy\n", node.Location()) + + return cached, nil + + } else if err != nil { + return nil, err + } + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched remote copy\n", node.Location()) + + // Get the checksums + cachedChecksum := cache.readChecksum(node) + checksum, err := checksumSource(*src) + if err != nil { + return nil, err + } + + var prompt string + if cachedChecksum == "" { + // If the checksum doesn't exist, prompt the user to continue + prompt = fmt.Sprintf(taskfileUntrustedPrompt, node.Location()) + } else if checksum != cachedChecksum { + // If there is a cached hash, but it doesn't match the expected hash, prompt the user to continue + prompt = fmt.Sprintf(taskfileChangedPrompt, node.Location()) + } + + if prompt != "" { + if err := r.logger.Prompt(logger.Yellow, prompt, "n", "y", "yes"); err != nil { + return nil, &errors.TaskfileNotTrustedError{URI: node.Location()} + } + + // Cache the file + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Caching downloaded file\n", node.Location()) + if src, err = cache.write(node, *src); err != nil { + return nil, err + } + } + + return src, nil +} From 695ff733f8ddbc1b26ace463df5392b525c99e3c Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Mon, 26 Aug 2024 20:45:08 -0400 Subject: [PATCH 08/12] Make source location consistent between cached and uncached nodes --- taskfile/cache.go | 27 ++++++++-------- taskfile/node.go | 1 - taskfile/node_file.go | 4 --- taskfile/node_remote.go | 4 --- taskfile/node_stdin.go | 4 --- taskfile/reader.go | 69 +++++++++++++++++++++++------------------ 6 files changed, 52 insertions(+), 57 deletions(-) diff --git a/taskfile/cache.go b/taskfile/cache.go index 62e4148659..0fe31f1ec1 100644 --- a/taskfile/cache.go +++ b/taskfile/cache.go @@ -62,7 +62,7 @@ func checksumSource(s source) (string, error) { return fmt.Sprintf("%x", h.Sum(nil))[:16], nil } -func (c *Cache) write(node Node, src source) (*source, error) { +func (c *Cache) write(node RemoteNode, src source) (*RemoteNode, error) { // Clear metadata file so that if the rest of the operations fail part-way we don't // end up in an inconsistent state where we've written the contents but have old metadata if err := c.clearMetadata(node); err != nil { @@ -112,10 +112,10 @@ func (c *Cache) write(node Node, src source) (*source, error) { return nil, fmt.Errorf("error storing metadata for node %s: %w", node.Location(), err) } - return &src, nil + return c.read(node) } -func (c *Cache) read(node Node) (*source, error) { +func (c *Cache) read(node RemoteNode) (*RemoteNode, error) { path, err := c.contentsPath(node) if err != nil { return nil, err @@ -133,14 +133,15 @@ func (c *Cache) read(node Node) (*source, error) { return nil, err } - return &source{ + node.cachedSource = &source{ FileContent: content, FileDirectory: path, Filename: taskfileName, - }, nil + } + return &node, nil } -func (c *Cache) readChecksum(node Node) string { +func (c *Cache) readChecksum(node RemoteNode) string { m, err := c.readMetadata(node) if err != nil { return "" @@ -148,7 +149,7 @@ func (c *Cache) readChecksum(node Node) string { return m.Checksum } -func (c *Cache) clearMetadata(node Node) error { +func (c *Cache) clearMetadata(node RemoteNode) error { path, err := c.metadataFilePath(node) if err != nil { return fmt.Errorf("error clearing metadata file at %s: %w", path, err) @@ -171,7 +172,7 @@ func (c *Cache) clearMetadata(node Node) error { return nil } -func (c *Cache) storeMetadata(node Node, m metadata) error { +func (c *Cache) storeMetadata(node RemoteNode, m metadata) error { path, err := c.metadataFilePath(node) if err != nil { return err @@ -190,7 +191,7 @@ func (c *Cache) storeMetadata(node Node, m metadata) error { return nil } -func (c *Cache) readMetadata(node Node) (*metadata, error) { +func (c *Cache) readMetadata(node RemoteNode) (*metadata, error) { path, err := c.metadataFilePath(node) if err != nil { return nil, err @@ -210,19 +211,19 @@ func (c *Cache) readMetadata(node Node) (*metadata, error) { return m, nil } -func (c *Cache) key(node Node) string { +func (c *Cache) key(node RemoteNode) string { return strings.TrimRight(checksum([]byte(node.Location())), "=") } -func (c *Cache) contentsPath(node Node) (string, error) { +func (c *Cache) contentsPath(node RemoteNode) (string, error) { return c.cacheFilePath(node, "contents") } -func (c *Cache) metadataFilePath(node Node) (string, error) { +func (c *Cache) metadataFilePath(node RemoteNode) (string, error) { return c.cacheFilePath(node, "metadata.yaml") } -func (c *Cache) cacheFilePath(node Node, filename string) (string, error) { +func (c *Cache) cacheFilePath(node RemoteNode, filename string) (string, error) { lastDir, prefix := node.FilenameAndLastDir() // Means it's not "", nor "." nor "/", so it's a valid directory if len(lastDir) > 1 { diff --git a/taskfile/node.go b/taskfile/node.go index 2cdade1b77..1c8f1154b8 100644 --- a/taskfile/node.go +++ b/taskfile/node.go @@ -22,7 +22,6 @@ type Node interface { Parent() Node Location() string Dir() string - Remote() bool ResolveEntrypoint(entrypoint string) (string, error) ResolveDir(dir string) (string, error) FilenameAndLastDir() (string, string) diff --git a/taskfile/node_file.go b/taskfile/node_file.go index e87cf38301..5bd53598b6 100644 --- a/taskfile/node_file.go +++ b/taskfile/node_file.go @@ -35,10 +35,6 @@ func (node *FileNode) Location() string { return node.Entrypoint } -func (node *FileNode) Remote() bool { - return false -} - func (node *FileNode) Read(ctx context.Context) (*source, error) { f, err := os.Open(node.Location()) if err != nil { diff --git a/taskfile/node_remote.go b/taskfile/node_remote.go index 660104d2bc..22e40e6dec 100644 --- a/taskfile/node_remote.go +++ b/taskfile/node_remote.go @@ -75,10 +75,6 @@ func (r *RemoteNode) Location() string { return r.proto + "::" + r.url.String() } -func (r *RemoteNode) Remote() bool { - return true -} - func (r *RemoteNode) Read(ctx context.Context) (*source, error) { return r.loadSource(ctx) } diff --git a/taskfile/node_stdin.go b/taskfile/node_stdin.go index 9d1f66e77e..b071887c2f 100644 --- a/taskfile/node_stdin.go +++ b/taskfile/node_stdin.go @@ -26,10 +26,6 @@ func (node *StdinNode) Location() string { return "__stdin__" } -func (node *StdinNode) Remote() bool { - return false -} - func (node *StdinNode) Read(ctx context.Context) (*source, error) { var stdin []byte scanner := bufio.NewScanner(os.Stdin) diff --git a/taskfile/reader.go b/taskfile/reader.go index 12ef61c185..f2cb64be35 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -88,7 +88,7 @@ func (r *Reader) include(node Node) error { // Read and parse the Taskfile from the file and add it to the vertex var err error - vertex.Taskfile, err = r.readNode(node) + vertex.Taskfile, node, err = r.readNode(node) if err != nil { return err } @@ -180,25 +180,33 @@ func (r *Reader) include(node Node) error { return g.Wait() } -func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { - source, err := r.loadNodeContent(node) +func (r *Reader) readNode(node Node) (*ast.Taskfile, Node, error) { + node, err := r.loadNode(node) if err != nil { - return nil, err + return nil, nil, err + } + + ctx, cf := context.WithTimeout(context.Background(), r.timeout) + defer cf() + + src, err := node.Read(ctx) + if err != nil { + return nil, nil, err } var tf ast.Taskfile - if err := yaml.Unmarshal(source.FileContent, &tf); err != nil { + if err := yaml.Unmarshal(src.FileContent, &tf); err != nil { // Decode the taskfile and add the file info the any errors taskfileInvalidErr := &errors.TaskfileDecodeError{} if errors.As(err, &taskfileInvalidErr) { - return nil, taskfileInvalidErr.WithFileInfo(node.Location(), source.FileContent, 2) + return nil, nil, taskfileInvalidErr.WithFileInfo(node.Location(), src.FileContent, 2) } - return nil, &errors.TaskfileInvalidError{URI: filepathext.TryAbsToRel(node.Location()), Err: err} + return nil, nil, &errors.TaskfileInvalidError{URI: filepathext.TryAbsToRel(node.Location()), Err: err} } // Check that the Taskfile is set and has a schema version if tf.Version == nil { - return nil, &errors.TaskfileVersionCheckError{URI: node.Location()} + return nil, nil, &errors.TaskfileVersionCheckError{URI: node.Location()} } // Set the taskfile/task's locations @@ -213,18 +221,17 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { task.Location.Taskfile = tf.Location } if task.Location.TaskfileDir == "" { - task.Location.TaskfileDir = source.FileDirectory + task.Location.TaskfileDir = src.FileDirectory } } - return &tf, nil + return &tf, node, nil } -func (r *Reader) loadNodeContent(node Node) (*source, error) { - if !node.Remote() { - ctx, cf := context.WithTimeout(context.Background(), r.timeout) - defer cf() - return node.Read(ctx) +func (r *Reader) loadNode(n Node) (Node, error) { + remote, ok := n.(*RemoteNode) + if !ok { + return n, nil } cache, err := NewCache(r.tempDir) @@ -234,13 +241,13 @@ func (r *Reader) loadNodeContent(node Node) (*source, error) { if r.offline { // In offline mode try to use cached copy - cached, err := cache.read(node) + cached, err := cache.read(*remote) if errors.Is(err, os.ErrNotExist) { - return nil, &errors.TaskfileCacheNotFoundError{URI: node.Location()} + return nil, &errors.TaskfileCacheNotFoundError{URI: remote.Location()} } else if err != nil { return nil, err } - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched cached copy\n", node.Location()) + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched cached copy\n", remote.Location()) return cached, nil } @@ -248,33 +255,33 @@ func (r *Reader) loadNodeContent(node Node) (*source, error) { ctx, cf := context.WithTimeout(context.Background(), r.timeout) defer cf() - src, err := node.Read(ctx) + src, err := remote.Read(ctx) if errors.Is(ctx.Err(), context.DeadlineExceeded) { // If we timed out then we likely have a network issue // If a download was requested, then we can't use a cached copy if r.download { - return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout} + return nil, &errors.TaskfileNetworkTimeoutError{URI: remote.Location(), Timeout: r.timeout} } // Search for any cached copies - cached, err := cache.read(node) + cached, err := cache.read(*remote) if errors.Is(err, os.ErrNotExist) { - return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout, CheckedCache: true} + return nil, &errors.TaskfileNetworkTimeoutError{URI: remote.Location(), Timeout: r.timeout, CheckedCache: true} } else if err != nil { return nil, err } - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Network timeout. Fetched cached copy\n", node.Location()) + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Network timeout. Fetched cached copy\n", remote.Location()) return cached, nil } else if err != nil { return nil, err } - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched remote copy\n", node.Location()) + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched remote copy\n", remote.Location()) // Get the checksums - cachedChecksum := cache.readChecksum(node) + cachedChecksum := cache.readChecksum(*remote) checksum, err := checksumSource(*src) if err != nil { return nil, err @@ -283,23 +290,23 @@ func (r *Reader) loadNodeContent(node Node) (*source, error) { var prompt string if cachedChecksum == "" { // If the checksum doesn't exist, prompt the user to continue - prompt = fmt.Sprintf(taskfileUntrustedPrompt, node.Location()) + prompt = fmt.Sprintf(taskfileUntrustedPrompt, remote.Location()) } else if checksum != cachedChecksum { // If there is a cached hash, but it doesn't match the expected hash, prompt the user to continue - prompt = fmt.Sprintf(taskfileChangedPrompt, node.Location()) + prompt = fmt.Sprintf(taskfileChangedPrompt, remote.Location()) } if prompt != "" { if err := r.logger.Prompt(logger.Yellow, prompt, "n", "y", "yes"); err != nil { - return nil, &errors.TaskfileNotTrustedError{URI: node.Location()} + return nil, &errors.TaskfileNotTrustedError{URI: remote.Location()} } // Cache the file - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Caching downloaded file\n", node.Location()) - if src, err = cache.write(node, *src); err != nil { + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Caching downloaded file\n", remote.Location()) + if remote, err = cache.write(*remote, *src); err != nil { return nil, err } } - return src, nil + return remote, nil } From 31527803505ccd4f3d40ff76a418fadc4738345c Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Tue, 10 Sep 2024 12:58:50 -0400 Subject: [PATCH 09/12] Incorporate changes from c77c8a419b0c4b6905882c61bc663df5aaef6289 --- taskfile/node.go | 3 +-- taskfile/node_file.go | 3 +-- taskfile/node_remote.go | 38 +++++++++++++++++++------------------- taskfile/node_stdin.go | 3 +-- taskfile/reader.go | 15 +++++---------- taskfile/taskfile.go | 12 +++++------- 6 files changed, 32 insertions(+), 42 deletions(-) diff --git a/taskfile/node.go b/taskfile/node.go index 1c8f1154b8..f43be8e765 100644 --- a/taskfile/node.go +++ b/taskfile/node.go @@ -1,7 +1,6 @@ package taskfile import ( - "context" "os" "path/filepath" "time" @@ -18,7 +17,7 @@ type source struct { } type Node interface { - Read(ctx context.Context) (*source, error) + Read() (*source, error) Parent() Node Location() string Dir() string diff --git a/taskfile/node_file.go b/taskfile/node_file.go index 5bd53598b6..d71245c759 100644 --- a/taskfile/node_file.go +++ b/taskfile/node_file.go @@ -1,7 +1,6 @@ package taskfile import ( - "context" "io" "os" "path/filepath" @@ -35,7 +34,7 @@ func (node *FileNode) Location() string { return node.Entrypoint } -func (node *FileNode) Read(ctx context.Context) (*source, error) { +func (node *FileNode) Read() (*source, error) { f, err := os.Open(node.Location()) if err != nil { return nil, err diff --git a/taskfile/node_remote.go b/taskfile/node_remote.go index 22e40e6dec..617ce657bf 100644 --- a/taskfile/node_remote.go +++ b/taskfile/node_remote.go @@ -75,8 +75,8 @@ func (r *RemoteNode) Location() string { return r.proto + "::" + r.url.String() } -func (r *RemoteNode) Read(ctx context.Context) (*source, error) { - return r.loadSource(ctx) +func (r *RemoteNode) Read() (*source, error) { + return r.loadSource() } func (r *RemoteNode) ResolveEntrypoint(entrypoint string) (string, error) { @@ -87,10 +87,7 @@ func (r *RemoteNode) ResolveEntrypoint(entrypoint string) (string, error) { if childProto == "file" && !filepath.IsAbs(entrypoint) { // Relative file paths are resolved as relative to our own source location - ctx, cancel := context.WithTimeout(context.Background(), r.timeout) - defer cancel() - - src, err := r.loadSource(ctx) + src, err := r.loadSource() if err != nil { return "", err } @@ -143,15 +140,29 @@ func (r *RemoteNode) FilenameAndLastDir() (string, string) { return filepath.Base(dir), filename } -func (r *RemoteNode) loadSource(ctx context.Context) (*source, error) { +func (r *RemoteNode) loadSource() (*source, error) { if r.cachedSource == nil { dir, err := os.MkdirTemp("", "taskfile-remote-") if err != nil { return nil, err } + ctx, cancel := context.WithTimeout(context.Background(), r.timeout) + defer cancel() + + u := r.url + if r.proto == "http" || r.proto == "https" { + // HTTP and HTTPS entrypoint get filename auto-resolution before being handed off to go-getter + u, err = remoteExists(ctx, r.logger, r.url) + if err != nil { + return nil, err + } else if ctx.Err() != nil { + return nil, &errors.TaskfileNetworkTimeoutError{URI: u.String(), Timeout: r.timeout} + } + } + r.client.Ctx = ctx - r.client.Src = r.Location() + r.client.Src = r.proto + "::" + u.String() r.client.Dst = dir r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetching remote taskfile from %s into %s\n", r.Location(), r.client.Src, r.client.Dst) @@ -281,17 +292,6 @@ func resolveHTTPEntrypoint(l *logger.Logger, insecure bool, timeout time.Duratio return nil, &errors.TaskfileNotSecureError{URI: u.String()} } - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - // HTTP and HTTPS entrypoint get filename auto-resolution before being handed off to go-getter - u, err := RemoteExists(ctx, l, u) - if err != nil { - return nil, err - } else if ctx.Err() != nil { - return nil, &errors.TaskfileNetworkTimeoutError{URI: u.String(), Timeout: timeout} - } - return u, nil } diff --git a/taskfile/node_stdin.go b/taskfile/node_stdin.go index b071887c2f..3c697208fb 100644 --- a/taskfile/node_stdin.go +++ b/taskfile/node_stdin.go @@ -2,7 +2,6 @@ package taskfile import ( "bufio" - "context" "fmt" "os" "strings" @@ -26,7 +25,7 @@ func (node *StdinNode) Location() string { return "__stdin__" } -func (node *StdinNode) Read(ctx context.Context) (*source, error) { +func (node *StdinNode) Read() (*source, error) { var stdin []byte scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { diff --git a/taskfile/reader.go b/taskfile/reader.go index f2cb64be35..d38c198059 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -1,7 +1,6 @@ package taskfile import ( - "context" "fmt" "os" "time" @@ -186,10 +185,7 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, Node, error) { return nil, nil, err } - ctx, cf := context.WithTimeout(context.Background(), r.timeout) - defer cf() - - src, err := node.Read(ctx) + src, err := node.Read() if err != nil { return nil, nil, err } @@ -252,16 +248,15 @@ func (r *Reader) loadNode(n Node) (Node, error) { return cached, nil } - ctx, cf := context.WithTimeout(context.Background(), r.timeout) - defer cf() + src, err := remote.Read() - src, err := remote.Read(ctx) - if errors.Is(ctx.Err(), context.DeadlineExceeded) { + var te errors.TaskfileNetworkTimeoutError + if errors.As(err, &te) { // If we timed out then we likely have a network issue // If a download was requested, then we can't use a cached copy if r.download { - return nil, &errors.TaskfileNetworkTimeoutError{URI: remote.Location(), Timeout: r.timeout} + return nil, &errors.TaskfileNetworkTimeoutError{URI: remote.Location(), Timeout: te.Timeout} } // Search for any cached copies diff --git a/taskfile/taskfile.go b/taskfile/taskfile.go index a24a771fff..7479dd7de0 100644 --- a/taskfile/taskfile.go +++ b/taskfile/taskfile.go @@ -8,7 +8,6 @@ import ( "path/filepath" "slices" "strings" - "time" "github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/internal/filepathext" @@ -37,12 +36,12 @@ var ( } ) -// RemoteExists will check if a file at the given URL Exists. If it does, it +// remoteExists will check if a file at the given URL Exists. If it does, it // will return its URL. If it does not, it will search the search for any files // at the given URL with any of the default Taskfile files names. If any of // these match a file, the first matching path will be returned. If no files are // found, an error will be returned. -func RemoteExists(ctx context.Context, l *logger.Logger, u *url.URL, timeout time.Duration) (*url.URL, error) { +func remoteExists(ctx context.Context, l *logger.Logger, u *url.URL) (*url.URL, error) { // Create a new HEAD request for the given URL to check if the resource exists req, err := http.NewRequest("HEAD", u.String(), nil) if err != nil { @@ -51,10 +50,9 @@ func RemoteExists(ctx context.Context, l *logger.Logger, u *url.URL, timeout tim // Request the given URL resp, err := http.DefaultClient.Do(req.WithContext(ctx)) - if err != nil { - if errors.Is(ctx.Err(), context.DeadlineExceeded) { - return nil, &errors.TaskfileNetworkTimeoutError{URI: u.String(), Timeout: timeout} - } + if errors.Is(ctx.Err(), context.DeadlineExceeded) { + return nil, ctx.Err() + } else if err != nil { return nil, errors.TaskfileFetchFailedError{URI: u.String()} } defer resp.Body.Close() From a48b35f77a25608eacd2836df68d3e24c22b964b Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Wed, 11 Sep 2024 15:14:47 -0400 Subject: [PATCH 10/12] Fix error type detection This type implements `error` as a pointer. If the type passed in is not a pointer to a pointer, `errors.As()` panics. --- taskfile/reader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taskfile/reader.go b/taskfile/reader.go index d38c198059..276d650bad 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -250,7 +250,7 @@ func (r *Reader) loadNode(n Node) (Node, error) { src, err := remote.Read() - var te errors.TaskfileNetworkTimeoutError + var te *errors.TaskfileNetworkTimeoutError if errors.As(err, &te) { // If we timed out then we likely have a network issue From 00f64d4a16740e739bd9b2a862faaab47ba62a96 Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Wed, 11 Sep 2024 15:15:22 -0400 Subject: [PATCH 11/12] Add remote include detection to FileNode Without this, FileNode things that `github.com/org/repo` is a file location and it attempts to map it relative to itself. Something tells me we should integrate both node types into some kind of shared type, but for now this works. --- taskfile/node_file.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/taskfile/node_file.go b/taskfile/node_file.go index d71245c759..15c3391d01 100644 --- a/taskfile/node_file.go +++ b/taskfile/node_file.go @@ -1,10 +1,10 @@ package taskfile import ( + "fmt" "io" "os" "path/filepath" - "strings" "github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/filepathext" @@ -80,8 +80,12 @@ func resolveFileNodeEntrypointAndDir(l *logger.Logger, entrypoint, dir string) ( } func (node *FileNode) ResolveEntrypoint(entrypoint string) (string, error) { - // If the file is remote, we don't need to resolve the path - if strings.Contains(entrypoint, "://") { + client := newGetterClient(node.Dir()) + proto, _, err := extractProtocolFromURL(client, entrypoint) + if err != nil { + return "", fmt.Errorf("error determining protocol of include %s: %w", entrypoint, err) + } else if proto != "file" { + // If the file is remote, we don't need to resolve the path return entrypoint, nil } From 70048b530e00d75e70aeeac9afec59967a6b4e03 Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Thu, 12 Sep 2024 12:04:28 -0400 Subject: [PATCH 12/12] Comment and tidy-up --- taskfile/cache.go | 2 ++ taskfile/node_remote.go | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/taskfile/cache.go b/taskfile/cache.go index 0fe31f1ec1..c8dc2b6d44 100644 --- a/taskfile/cache.go +++ b/taskfile/cache.go @@ -133,6 +133,8 @@ func (c *Cache) read(node RemoteNode) (*RemoteNode, error) { return nil, err } + // We change the node's source location but keep the rest of its metadata like entrypoint and dir intact + // Since we use a struct value (and not a pointer) we don't mutate the original one node.cachedSource = &source{ FileContent: content, FileDirectory: path, diff --git a/taskfile/node_remote.go b/taskfile/node_remote.go index 617ce657bf..82dff0489c 100644 --- a/taskfile/node_remote.go +++ b/taskfile/node_remote.go @@ -57,8 +57,7 @@ func NewRemoteNode( return nil, false, err } - var tf string - tf, u = resolveTaskfileOverride(u) + tf, u := resolveTaskfileOverride(u) return &RemoteNode{ BaseNode: NewBaseNode(dir, opts...),