diff --git a/go.mod b/go.mod index 5093441..bbece73 100644 --- a/go.mod +++ b/go.mod @@ -14,29 +14,40 @@ require ( github.com/gookit/color v1.5.3 github.com/imdario/mergo v0.3.13 github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f + github.com/mholt/archiver/v4 v4.0.0-alpha.8 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.5.0 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.3 github.com/u-root/u-root v0.11.0 github.com/wavesoftware/go-commandline v1.0.0 go.uber.org/zap v1.24.0 golang.org/x/oauth2 v0.2.0 - golang.org/x/term v0.4.0 + golang.org/x/term v0.5.0 sigs.k8s.io/yaml v1.3.0 ) require ( + github.com/andybalholm/brotli v1.0.5 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52 v1.2.1 // indirect github.com/benbjohnson/clock v1.3.0 // indirect + github.com/bodgit/plumbing v1.3.0 // indirect + github.com/bodgit/sevenzip v1.4.2 // indirect + github.com/bodgit/windows v1.0.1 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/containerd/console v1.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/go-eden/common v0.1.14 // indirect github.com/go-eden/routine v1.0.2 // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.1.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/klauspost/compress v1.16.6 // indirect + github.com/klauspost/pgzip v1.2.6 // indirect github.com/kr/pretty v0.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.17 // indirect @@ -46,17 +57,22 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.13.0 // indirect + github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect + github.com/pierrec/lz4/v4 v4.1.17 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.3 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/therootcompany/xz v1.0.1 // indirect + github.com/ulikunitz/xz v0.5.11 // indirect github.com/wavesoftware/go-retcode v1.0.0 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.9.0 // indirect + go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/net v0.2.0 // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/text v0.10.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect diff --git a/go.sum b/go.sum index 4420370..47d3ebf 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,26 @@ +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.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +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/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +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/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/1set/gut v0.0.0-20201117175203-a82363231997 h1:za2jSkE1Rx56hTzBko3ZZ4gA/nq+rA/jVovWuAF4jyo= github.com/1set/gut v0.0.0-20201117175203-a82363231997/go.mod h1:DpCCAL0dgBMQdiqPUIIRpdU9zNcIZwJjW+L/8Mb30mw= +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/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= @@ -8,6 +29,13 @@ github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZ github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= +github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= +github.com/bodgit/sevenzip v1.4.2 h1:J7MK2W+vlErQou57oGr7ezdsoDtPPGLi8jAtrf5JhPA= +github.com/bodgit/sevenzip v1.4.2/go.mod h1:Vk8AS10UhoKbRqh4zz5hN2Blz5Af/ve/N4K/333RwiM= +github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= +github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/bubbles v0.15.0 h1:c5vZ3woHV5W2b8YZI1q7v4ZNQaPetfHuoHzx+56Z6TI= github.com/charmbracelet/bubbles v0.15.0/go.mod h1:Y7gSFbBzlMpUDR/XM9MhZI374Q+1p1kluf1uLl8iK74= github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUIte8WPvhck= @@ -16,12 +44,21 @@ github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY= github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= +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/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= +github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= +github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikgeiser/promptkit v0.8.0 h1:bvOzPs6RLyfRZDSgVWOghQEiBSRHQ3zmDdxcV8zOc+E= github.com/erikgeiser/promptkit v0.8.0/go.mod h1:QxyFbCrrj20PyvV5b+ckWPozbgX11s04GeRlmTCIMTo= github.com/go-eden/common v0.1.14 h1:QiSxZLFQDXE2F7LX+ZnaiTs15t0n3lfZo5PanIJiv60= @@ -33,10 +70,31 @@ github.com/go-eden/slf4go v1.1.2 h1:WVl2nVp7GvqvhHGzAXC0u1LTQvn5OgFcfuwmeMok4d8= github.com/go-eden/slf4go v1.1.2/go.mod h1:mnVsjQFGLXKKWN6Mb9GREeZ2DOwwmLrb4PsLfTAdkTM= github.com/go-eden/slf4go-zap v0.0.0-20230228024123-c74fa79d28f0 h1:EXusj6Om/GL54lSFN4bEaVsMHte6VCeHt8RhhpZ7Wyg= github.com/go-eden/slf4go-zap v0.0.0-20230228024123-c74fa79d28f0/go.mod h1:mVkVsinFwm736IbCDhZLxn9JY4FethI06OXU1VwLCqA= +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/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/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/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.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 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.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/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.5.2/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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -44,14 +102,38 @@ github.com/google/go-github/v48 v48.1.0 h1:nqPqq+0oRY2AMR/SRskGrrP4nnewPB7e/m2+k github.com/google/go-github/v48 v48.1.0/go.mod h1:dDlehKBDo850ZPvCTK0sEqTCVWcrGl2LcDiajkYi89Y= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +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-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE= github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +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/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f h1:dKccXx7xA56UNqOcFIbuqFjAWPVtP688j5QMgmo6OHU= github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f/go.mod h1:4rEELDSfUAlBSyUjPG0JnaNGjf13JySHFeRdD/3dLP0= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk= +github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= +github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -72,6 +154,8 @@ github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM= +github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a h1:jlDOeO5TU0pYlbc/y6PFguab5IjANI0Knrpg3u/ton4= github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= @@ -83,16 +167,23 @@ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKt github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= +github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk= +github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= +github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= +github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= @@ -106,10 +197,16 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= +github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/u-root/u-root v0.11.0 h1:6gCZLOeRyevw7gbTwMj3fKxnr9+yHFlgF3N7udUVNO8= github.com/u-root/u-root v0.11.0/go.mod h1:DBkDtiZyONk9hzVEdB/PWI9B4TxDkElWlVTHseglrZY= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= +github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/wavesoftware/go-commandline v1.0.0 h1:n7nrFr1unfiUcF7shA1rYf+YhXB12pY8uNYqPgFsHio= github.com/wavesoftware/go-commandline v1.0.0/go.mod h1:C9yRtwZxJSck99kk6SRRkOtC2ppQF/KDRy0yrzWJuHU= github.com/wavesoftware/go-retcode v1.0.0 h1:Z53+VpIHMvRMtjS6jPScdihbAN1ks3lIJ5Mj32gCpno= @@ -117,6 +214,11 @@ github.com/wavesoftware/go-retcode v1.0.0/go.mod h1:BLqIIXhB/PQ+izkkRGfSQgu95BDt github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= 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= +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.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -127,25 +229,86 @@ go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= +go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= 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-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= 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-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +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/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.2.0/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-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-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +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.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU= golang.org/x/oauth2 v0.2.0/go.mod h1:Cwn6afJ8jrQwYMxQDTpISoXmXW9I6qF6vDeuuoX3Ibs= +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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-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-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -153,29 +316,92 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220204135822-1c1b9b1eba6a/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +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/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-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-20200130002326-2f3ba24bd6e7/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.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= +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/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.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-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +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.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/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.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= @@ -184,6 +410,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -193,5 +420,13 @@ gopkg.in/yaml.v3 v3.0.0/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= gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +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= +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= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/go.work.sum b/go.work.sum index 68d5b1f..8999fbe 100644 --- a/go.work.sum +++ b/go.work.sum @@ -440,6 +440,7 @@ github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOi github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= +github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw= github.com/containerd/aufs v1.0.0 h1:2oeJiwX5HstO7shSrPZjrohJZLzK36wvpdmzDRkL/LY= github.com/containerd/btrfs v1.0.0 h1:osn1exbzdub9L5SouXO5swW4ea/xVdJZ3wokxN5GrnA= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= @@ -498,7 +499,6 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= -github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780 h1:tFh1tRc4CA31yP6qDcu+Trax5wW5GuMxvkIba07qVLY= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= @@ -616,7 +616,6 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZ github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= @@ -947,6 +946,7 @@ github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= github.com/ryancurrah/gomodguard v1.2.3 h1:ww2fsjqocGCAFamzvv/b8IsRduuHHeK2MHTcTxZTQX8= github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw= github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= @@ -1147,15 +1147,20 @@ golang.org/x/exp v0.0.0-20220823124025-807a23277127 h1:S4NrSKDfihhl3+4jSTgwoIevK golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f h1:kgfVkAEEQXXQ0qc6dH7n6y37NAYmTFmz0YRwrRjgxKw= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= 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= diff --git a/internal/ght/args.go b/internal/ght/args.go index fc4e1a1..59054ff 100644 --- a/internal/ght/args.go +++ b/internal/ght/args.go @@ -1,10 +1,10 @@ package ght import ( + "context" "path" - "github.com/cardil/ghet/pkg/metadata" - "github.com/kirsle/configdir" + configdir "github.com/cardil/ghet/pkg/config/dir" "github.com/spf13/cobra" ) @@ -12,12 +12,8 @@ type Args struct { ConfigPath string } -func (a Args) Defaults() Args { - configPath := configdir.LocalConfig(metadata.Name) - err := configdir.MakePath(configPath) // Ensure it exists. - if err != nil { - panic(err) - } +func (a Args) Defaults(ctx context.Context) Args { + configPath := configdir.Config(ctx) settingsPth := path.Join(configPath, "settings.yaml") return Args{ @@ -26,7 +22,7 @@ func (a Args) Defaults() Args { } func (a *App) setFlags(c *cobra.Command) { - defs := a.Defaults() + defs := a.Defaults(c.Context()) fl := c.PersistentFlags() fl.StringVarP(&a.ConfigPath, "config", "c", defs.ConfigPath, "path to configuration file") diff --git a/internal/ght/install.go b/internal/ght/install.go index a023fb2..58efe81 100644 --- a/internal/ght/install.go +++ b/internal/ght/install.go @@ -28,11 +28,12 @@ func installCmd(_ *Args) *cobra.Command { } type installArgs struct { - site string - version string - basename string - checksums string - repo string + site string + version string + basename string + checksums string + repo string + multipleBinaries bool } func (ia *installArgs) defaults() installArgs { @@ -56,6 +57,8 @@ func (ia *installArgs) setFlags(c *cobra.Command) { "if not given a repo name will be used") fl.StringVar(&ia.checksums, "checksums", defs.checksums, "a checksums file name") + fl.BoolVar(&ia.multipleBinaries, "multiple-binaries", defs.multipleBinaries, + "if set, will download all binaries from the archive") c.Args = cobra.ExactArgs(1) } @@ -86,7 +89,8 @@ func (ia *installArgs) parse(ctx context.Context) install.Args { FileName: ia.checksumsFilename(), }, }, - Site: cfg.Site(ia.site), + Site: cfg.Site(ia.site), + MultipleBinaries: ia.multipleBinaries, } args = args.WithDefaults() return args diff --git a/pkg/config/dir/local.go b/pkg/config/dir/local.go new file mode 100644 index 0000000..a94d636 --- /dev/null +++ b/pkg/config/dir/local.go @@ -0,0 +1,51 @@ +package dir + +import ( + "context" + "log" + "os" + + "github.com/cardil/ghet/pkg/metadata" + "github.com/kirsle/configdir" + "github.com/pkg/errors" +) + +const ( + ConfigDirEnvName = "GHET_CONFIG_DIR" + CacheDirEnvName = "GHET_CACHE_DIR" +) + +var ( + configDirKey = struct{}{} //nolint:gochecknoglobals + cacheDirKey = struct{}{} //nolint:gochecknoglobals +) + +func Config(ctx context.Context) string { + return userPath(ctx, configDirKey, ConfigDirEnvName, func() string { + return configdir.LocalConfig(metadata.Name) + }) +} + +func Cache(ctx context.Context) string { + return userPath(ctx, cacheDirKey, CacheDirEnvName, func() string { + return configdir.LocalCache(metadata.Name) + }) +} + +func userPath(ctx context.Context, key interface{}, envKey string, fn func() string) string { + if p, ok := ctx.Value(key).(string); ok { + return ensurePathExists(p) + } + p := os.Getenv(envKey) + if p == "" { + p = fn() + } + return ensurePathExists(p) +} + +func ensurePathExists(p string) string { + if err := configdir.MakePath(p); err != nil { + log.Fatal(errors.WithStack(err)) + } + return p +} diff --git a/pkg/config/dir/test.go b/pkg/config/dir/test.go new file mode 100644 index 0000000..ba22cb7 --- /dev/null +++ b/pkg/config/dir/test.go @@ -0,0 +1,11 @@ +package dir + +import "context" + +func WithConfigDir(ctx context.Context, p string) context.Context { + return context.WithValue(ctx, configDirKey, p) +} + +func WithCacheDir(ctx context.Context, p string) context.Context { + return context.WithValue(ctx, cacheDirKey, p) +} diff --git a/pkg/ghet/download/cache.go b/pkg/ghet/download/cache.go new file mode 100644 index 0000000..e071049 --- /dev/null +++ b/pkg/ghet/download/cache.go @@ -0,0 +1,41 @@ +package download + +import ( + "context" + "fmt" + "hash/crc32" + "os" + "path" + "strconv" + + configdir "github.com/cardil/ghet/pkg/config/dir" + githubapi "github.com/cardil/ghet/pkg/github/api" + log "github.com/go-eden/slf4go" +) + +const executablePerm = 0o750 + +func (p Plan) cachePath(ctx context.Context, asset githubapi.Asset) string { + dir := path.Join(configdir.Cache(ctx), p.transationID()) + if err := os.MkdirAll(dir, executablePerm); err != nil { + log.Fatal(unexpected(err)) + } + return path.Join(dir, asset.Name) +} + +func (p Plan) cleanCache(ctx context.Context) error { + fp := path.Join(configdir.Cache(ctx), p.transationID()) + if err := os.RemoveAll(fp); err != nil { + return unexpected(err) + } + return nil +} + +func (p Plan) transationID() string { + h := crc32.NewIEEE() + for _, asset := range p.Assets { + repr := fmt.Sprintf("%#v", asset) + _, _ = h.Write([]byte(repr)) + } + return strconv.Itoa(int(h.Sum32())) +} diff --git a/pkg/ghet/download/checksums.go b/pkg/ghet/download/checksums.go index a92f706..25d8ed0 100644 --- a/pkg/ghet/download/checksums.go +++ b/pkg/ghet/download/checksums.go @@ -36,12 +36,12 @@ var ErrChecksumMismatch = errors.New("checksum mismatch") // ErrNotVerifiedAssets is returned when there are no verified assets. var ErrNotVerifiedAssets = errors.New("not verified assets") -var bsdStyleChecksums = regexp.MustCompile(`^(SHA[0-9]{1,3})\s+\([^)]+\)\s+=\s+([a-fA-F0-9]{32,128})$`) +var bsdStyleChecksums = regexp.MustCompile(`^(SHA[0-9]{1,3})\s+\(([^)]+)\)\s+=\s+([a-fA-F0-9]{32,128})$`) -func verifyChecksums(ctx context.Context, assets []githubapi.Asset, args Args) error { +func (p Plan) verifyChecksums(ctx context.Context) error { l := output.LoggerFrom(ctx) widgets := tui.WidgetsFrom(ctx) - index := githubapi.CreateIndex(assets) + index := githubapi.CreateIndex(p.Assets) if len(index.Checksums) == 0 { l.Debug("No checksums to verify") widgets.Printf(ctx, "🕵 No checksums to verify") @@ -51,11 +51,7 @@ func verifyChecksums(ctx context.Context, assets []githubapi.Asset, args Args) e ca := index.Checksums[0] if len(index.Checksums) > 1 { - options := make([]string, len(index.Checksums)) - for i, c := range index.Checksums { - options[i] = c.Name - } - intwidgets, err := widgets.Interactive(ctx) + iwidgets, err := tui.Interactive[githubapi.Asset](ctx) if err != nil { if errors.Is(err, tui.ErrNotInteractive) { l.Errorf("Number of checksums is %d. Expected just one.", len(index.Checksums)) @@ -63,24 +59,30 @@ func verifyChecksums(ctx context.Context, assets []githubapi.Asset, args Args) e } return fmt.Errorf("%w: %v", ErrUnexpected, err) } - selected := intwidgets.Ask(ctx, options, + selected := iwidgets.Choose(ctx, index.Checksums, "⚠️ More than one checksum file found. Choose proper one") for _, c := range index.Checksums { - if c.Name == selected { + if c == selected { ca = c break } } } + artifacts := append(index.Archives, index.Binaries...) + if len(artifacts) == 0 { + l.Errorf("No assets to verify") + return fmt.Errorf("%w: %d", ErrNotVerifiedAssets, len(index.Binaries)) + } + defaultArtifact := artifacts[0] l = l.WithFields(slog.Fields{"checksum": ca.Name}) l.Debug("Verifying checksum") - csp := checksumParser{Asset: ca, Args: args} - if cs, err := csp.parse(ctx); err != nil { + csp := checksumParser{Asset: ca, plan: &p} + if cs, err := csp.parse(ctx, defaultArtifact); err != nil { return err } else { - err = cs.verify(ctx, append(index.Archives, index.Other...), args.Destination) + err = cs.verify(ctx, artifacts) if err != nil { return err } @@ -88,25 +90,18 @@ func verifyChecksums(ctx context.Context, assets []githubapi.Asset, args Args) e widgets.Printf(ctx, "✅ All checksums match the downloaded assets") - l.Debugf("Deleting the checksums file(s): %q", index.Checksums) - for _, c := range index.Checksums { - if err := os.Remove(path.Join(args.Destination, c.Name)); err != nil { - return unexpected(err) - } - } - return nil } type checksumParser struct { githubapi.Asset - Args + plan *Plan *checksums } -func (p *checksumParser) parse(ctx context.Context) (*checksums, error) { +func (p *checksumParser) parse(ctx context.Context, defaultArtifact githubapi.Asset) (*checksums, error) { l := output.LoggerFrom(ctx) - fp := path.Join(p.Destination, p.Name) + fp := p.plan.cachePath(ctx, p.Asset) l.Debugf("Parsing checksum: %s", fp) if _, ferr := os.Stat(fp); ferr != nil { return nil, unexpected(ferr) @@ -120,9 +115,10 @@ func (p *checksumParser) parse(ctx context.Context) (*checksums, error) { scanner := bufio.NewScanner(file) p.checksums = &checksums{ entries: make([]checksumEntry, 0, 1), + plan: p.plan, } for scanner.Scan() { - if err := p.parseLine(ctx, scanner.Text()); err != nil { + if err := p.parseLine(ctx, scanner.Text(), defaultArtifact); err != nil { return nil, err } } @@ -134,12 +130,16 @@ func (p *checksumParser) parse(ctx context.Context) (*checksums, error) { return p.checksums, nil } -func (p *checksumParser) parseLine(ctx context.Context, line string) error { +func (p *checksumParser) parseLine( + ctx context.Context, + line string, + defaultArtifact githubapi.Asset, +) error { var entry checksumEntry if bsdStyleChecksums.MatchString(line) { entry = p.parseBSDStyleChecksum(ctx, line) } else { - if e, err := p.parseRegularChecksum(line); err != nil { + if e, err := p.parseRegularChecksum(line, defaultArtifact); err != nil { return err } else { entry = e @@ -149,15 +149,21 @@ func (p *checksumParser) parseLine(ctx context.Context, line string) error { return nil } -func (p *checksumParser) parseRegularChecksum(line string) (checksumEntry, error) { +func (p *checksumParser) parseRegularChecksum( + line string, + artifact githubapi.Asset, +) (checksumEntry, error) { fields := strings.Fields(line) - if len(fields) != 2 { + if len(fields) > 2 || len(fields) < 1 { return checksumEntry{}, unexpected(fmt.Errorf("invalid checksum line: %s", line)) } entry := checksumEntry{ hash: fields[0], - filename: fields[1], + filename: artifact.Name, + } + if len(fields) == 2 { + entry.filename = fields[1] } if algo, err := checksumAlgorithmForHash(entry.hash); err != nil { return checksumEntry{}, err @@ -256,9 +262,10 @@ func (e checksumEntry) verify(_ context.Context, asset githubapi.Asset, dest str type checksums struct { entries []checksumEntry + plan *Plan } -func (c checksums) verify(ctx context.Context, assets []githubapi.Asset, dest string) error { +func (c checksums) verify(ctx context.Context, assets []githubapi.Asset) error { widgets := tui.WidgetsFrom(ctx) for _, entry := range c.entries { for i, curr := range assets { @@ -266,6 +273,7 @@ func (c checksums) verify(ctx context.Context, assets []githubapi.Asset, dest st spin := widgets.NewSpinner(ctx, fmt.Sprintf("🔍 Verifying checksum for %s", color.Cyan.Sprintf(curr.Name))) if err := spin.With(func(_ tui.Spinner) error { + dest := path.Dir(c.plan.cachePath(ctx, curr)) if err := entry.verify(ctx, curr, dest); err != nil { return err } diff --git a/pkg/ghet/download/download.go b/pkg/ghet/download/download.go index 790199a..c7f58ef 100644 --- a/pkg/ghet/download/download.go +++ b/pkg/ghet/download/download.go @@ -11,11 +11,9 @@ import ( "github.com/1set/gut/yos" githubapi "github.com/cardil/ghet/pkg/github/api" - "github.com/cardil/ghet/pkg/metadata" "github.com/cardil/ghet/pkg/output" "github.com/cardil/ghet/pkg/output/tui" slog "github.com/go-eden/slf4go" - "github.com/kirsle/configdir" "github.com/pkg/errors" ) @@ -30,24 +28,20 @@ type assetInfo struct { longestName int } -func downloadAsset(ctx context.Context, asset assetInfo, args Args) error { +func (p Plan) downloadAsset(ctx context.Context, asset assetInfo) error { l := output.LoggerFrom(ctx).WithFields(slog.Fields{ "asset": asset.Name, }) - cachePath := configdir.LocalCache(metadata.Name) - if err := configdir.MakePath(cachePath); err != nil { - return errors.WithStack(err) - } - cachePath = path.Join(cachePath, fmt.Sprintf("%d", asset.ID)) + cachePath := p.cachePath(ctx, asset.Asset) if fileExists(l, cachePath, asset.Size) { l.WithFields(slog.Fields{"cachePath": cachePath}). Debug("Asset already downloaded") - return moveFile(cachePath, asset.Asset, args) + return nil } l.Debug("Downloading asset") - cl := http.Client{} + cl := githubapi.FromContext(ctx).Client() req, err := http.NewRequestWithContext(ctx, http.MethodGet, asset.URL, nil) if err != nil { return errors.WithStack(err) @@ -84,7 +78,7 @@ func downloadAsset(ctx context.Context, asset assetInfo, args Args) error { }); perr != nil { return perr } - return moveFile(cachePath, asset.Asset, args) + return nil } func moveFile(cachePath string, asset githubapi.Asset, args Args) error { diff --git a/pkg/ghet/download/download_test.go b/pkg/ghet/download/download_test.go new file mode 100644 index 0000000..fa9689c --- /dev/null +++ b/pkg/ghet/download/download_test.go @@ -0,0 +1,203 @@ +package download_test + +import ( + "io" + "math/rand" + "net/http" + "net/url" + "os" + "path" + "strings" + "testing" + + configdir "github.com/cardil/ghet/pkg/config/dir" + "github.com/cardil/ghet/pkg/context" + "github.com/cardil/ghet/pkg/ghet/download" + "github.com/cardil/ghet/pkg/ghet/install" + pkggithub "github.com/cardil/ghet/pkg/github" + ghapi "github.com/cardil/ghet/pkg/github/api" + "github.com/cardil/ghet/pkg/output" + "github.com/google/go-github/v48/github" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDownload(t *testing.T) { + t.Parallel() + tcs := []downloadTestCase{{ + name: "kubernetes/minikube", + args: downloadArgs{ + name: "minikube", + assets: []string{ + "minikube-linux-amd64.tar.gz", + "minikube-linux-amd64.sha256", + }, + }, + want: []downloaded{{ + name: "minikube", + size: 42, + }}, + }, { + name: "sharkdp/diskus", + args: downloadArgs{ + name: "diskus", + assets: []string{ + "diskus-v0.7.0-x86_64-unknown-linux-gnu.tar.gz", + }, + }, + want: []downloaded{{ + name: "diskus", + size: 40, + }}, + }, { + name: "pulumi/pulumi", + args: downloadArgs{ + name: "pulumi", + assets: []string{ + "pulumi-v3.71.0-linux-x64.tar.gz", + "pulumi-3.71.0-checksums.txt", + }, + multipleBins: true, + }, + want: []downloaded{ + {name: "pulumi", size: 40}, + {name: "pulumi-watch", size: 49}, + {name: "pulumi-language-go", size: 54}, + }, + }, { + name: "knative-sandbox/kn-plugin-event", + args: downloadArgs{ + name: "kn-event", + assets: []string{ + "kn-event-linux-amd64", + "kn-event-checksums.txt", + }, + }, + want: []downloaded{{ + name: "kn-event", + size: 42, + }}, + }} + for i := range tcs { + tc := tcs[i] + t.Run(tc.name, tc.run) + } +} + +func (tc downloadTestCase) run(t *testing.T) { + t.Parallel() + tmpDir := t.TempDir() + ctx := context.TestContext(t) + ctx = configdir.WithCacheDir(ctx, tmpDir) + ctx = configdir.WithConfigDir(ctx, tmpDir) + ctx = output.WithContext(ctx, output.NewTestPrinter()) + ghapi.WithTestClient(t, func(client *github.Client, mux *http.ServeMux) { + ctx = ghapi.WithContext(ctx, client) + tc.configureMux(mux) + + wd := t.TempDir() + plan := tc.buildPlan(t, client.BaseURL) + args := tc.buildArgs(wd) + err := plan.Download(ctx, args) + assert.ErrorIs(t, err, tc.wantErr, "%+v", err) + for _, d := range tc.want { + fp := path.Join(wd, d.name) + fi, err := os.Stat(fp) + require.NoError(t, err) + assert.Equal(t, d.size, fi.Size(), "file %s has wrong size", d.name) + assert.True(t, fi.Mode().IsRegular(), "file %s is not regular", d.name) + assert.True(t, isExecutable(fi.Mode()), "file %s is not executable", d.name) + } + }) +} + +func (tc downloadTestCase) configureMux(mux *http.ServeMux) { + for i := range tc.args.assets { + asset := tc.args.assets[i] + mux.HandleFunc("/"+asset, func(w http.ResponseWriter, r *http.Request) { + f, err := fs.Open("testdata/" + asset) + if err != nil { + http.Error(w, err.Error(), http.StatusNotFound) + return + } + defer f.Close() + st, err := f.Stat() + if err != nil { + http.Error(w, err.Error(), http.StatusForbidden) + return + } + http.ServeContent(w, r, asset, st.ModTime(), f.(io.ReadSeeker)) + }) + } +} + +func (tc downloadTestCase) buildPlan(t testingT, baseurl *url.URL) download.Plan { + p := download.Plan{ + Assets: make([]ghapi.Asset, 0, len(tc.args.assets)), + } + // Deterministic random numbers. + rnd := rand.New(rand.NewSource(465712)) //nolint:gosec + for _, asset := range tc.args.assets { + f, err := fs.Open("testdata/" + asset) + require.NoError(t, err) + st, err := f.Stat() + require.NoError(t, err) + require.NoError(t, f.Close()) + + p.Assets = append(p.Assets, ghapi.Asset{ + ID: rnd.Int63(), + Name: asset, + ContentType: "application/octet-stream", + Size: int(st.Size()), + URL: baseurl.String() + asset, + }) + } + return p +} + +func (tc downloadTestCase) buildArgs(wd string) download.Args { + fields := strings.FieldsFunc(tc.name, func(r rune) bool { + return r == '/' + }) + repo := pkggithub.Repository{ + Owner: fields[0], + Repo: fields[1], + } + return download.Args{ + Args: install.Args{ + Asset: pkggithub.Asset{ + FileName: pkggithub.FileName{ + BaseName: tc.args.name, + }, + Release: pkggithub.Release{Repository: repo}, + Architecture: pkggithub.ArchAMD64, + OperatingSystem: pkggithub.OSLinuxGnu, + }, + MultipleBinaries: tc.args.multipleBins, + }, + Destination: wd, + } +} + +type downloadArgs struct { + name string + assets []string + multipleBins bool +} + +type downloaded struct { + name string + size int64 +} + +type downloadTestCase struct { + name string + args downloadArgs + + want []downloaded + wantErr error +} + +func isExecutable(mode os.FileMode) bool { + return mode&0o111 != 0 +} diff --git a/pkg/ghet/download/errors.go b/pkg/ghet/download/errors.go index cdefdbf..5992287 100644 --- a/pkg/ghet/download/errors.go +++ b/pkg/ghet/download/errors.go @@ -10,5 +10,8 @@ import ( var ErrUnexpected = errors.New("unexpected error") func unexpected(err error) error { + if errors.Is(err, ErrUnexpected) { + return err + } return errors.WithStack(fmt.Errorf("%w: %v", ErrUnexpected, err)) } diff --git a/pkg/ghet/download/extract.go b/pkg/ghet/download/extract.go index 5d9fd73..c8ad12f 100644 --- a/pkg/ghet/download/extract.go +++ b/pkg/ghet/download/extract.go @@ -1,10 +1,10 @@ package download import ( - "archive/zip" "context" "fmt" "io" + "io/fs" "os" "path" "strings" @@ -14,75 +14,177 @@ import ( "github.com/cardil/ghet/pkg/output/tui" slog "github.com/go-eden/slf4go" "github.com/gookit/color" + "github.com/mholt/archiver/v4" ) -func extract(ctx context.Context, assets []githubapi.Asset, args Args) error { +func (p Plan) extractArchives(ctx context.Context, args Args) error { widgets := tui.WidgetsFrom(ctx) - index := githubapi.CreateIndex(assets) + index := githubapi.CreateIndex(p.Assets) for _, asset := range index.Archives { widgets.Printf(ctx, "📦 Extracting archive: %s", color.Cyan.Sprintf(asset.Name)) - ar := archiveAsset{asset} - ctx = output.EnsureLogger(ctx, slog.Fields{"asset": asset.Name, "type": ar.ty()}) - if err := extractArchive(ctx, ar, args); err != nil { + ar := archiveAsset{Asset: asset, plan: &p} + ctx = output.EnsureLogger(ctx, slog.Fields{"asset": asset.Name}) + if err := ar.extract(ctx, args); err != nil { return err } } return nil } -func extractArchive(ctx context.Context, ar archiveAsset, args Args) error { - r, err := ar.open(ctx, args) +type archiveAsset struct { + githubapi.Asset + plan *Plan +} + +func (aa archiveAsset) open(ctx context.Context, args Args) (fs.FS, error) { + log := output.LoggerFrom(ctx) + fp := aa.plan.cachePath(ctx, aa.Asset) + log.WithFields(slog.Fields{"archive": fp}).Debug("Opening archive") + + fsys, err := archiver.FileSystem(ctx, fp) if err != nil { - return err + return nil, unexpected(err) } - defer r.Close() + return fsys, nil } -type archiveAsset struct { - githubapi.Asset -} +func (aa archiveAsset) extract(ctx context.Context, args Args) error { + fsys, err := aa.open(ctx, args) + if err != nil { + return err + } -type archiveType int + var binaries []compressedBinary + if binaries, err = findBinaries(ctx, args, fsys); err != nil { + return err + } -const ( - archiveTypeZip archiveType = iota - archiveTypeTar - archiveTypeTarGz - archiveTypeTarBz2 - archiveTypeTarXz -) + if binaries, err = chooseBinaries(ctx, args, binaries); err != nil { + return err + } -func (a archiveAsset) ty() archiveType { - if strings.HasSuffix(a.Name, ".zip") { - return archiveTypeZip + for _, binary := range binaries { + if err = extractBinary(ctx, args, fsys, binary); err != nil { + return err + } } - if strings.HasSuffix(a.Name, ".tar") { - return archiveTypeTar + + return nil +} + +func extractBinary(ctx context.Context, args Args, fsys fs.FS, binary compressedBinary) error { + var ( + ff fs.File + fi fs.FileInfo + err error + ) + widgets := tui.WidgetsFrom(ctx) + if fi, err = archiver.TopDirStat(fsys, binary.path); err != nil { + return unexpected(err) } - if strings.HasSuffix(a.Name, ".tar.gz") || strings.HasSuffix(a.Name, ".tgz") { - return archiveTypeTarGz + if ff, err = archiver.TopDirOpen(fsys, binary.path); err != nil { + return unexpected(err) } - if strings.HasSuffix(a.Name, ".tar.bz2") || strings.HasSuffix(a.Name, ".tbz2") || strings.HasSuffix(a.Name, ".tbz") { - return archiveTypeTarBz2 + defer ff.Close() + + label := fmt.Sprintf("🎯 %s", binary.Name()) + progress := widgets.NewProgress(ctx, int(fi.Size()), tui.Message{ + Text: label, Size: len(label), + }) + binaryPath := path.Join(args.Destination, args.FileName.ToString()) + if args.MultipleBinaries { + binaryPath = path.Join(args.Destination, binary.Name()) } - if strings.HasSuffix(a.Name, ".tar.xz") || strings.HasSuffix(a.Name, ".txz") { - return archiveTypeTarXz + out, err := os.Create(binaryPath) + if err != nil { + return unexpected(err) } - return -1 + if perr := progress.With(func(pc tui.ProgressControl) error { + _, err = io.Copy(out, io.TeeReader(ff, pc)) + if err != nil { + err = unexpected(err) + pc.Error(err) + return err + } + return nil + }); perr != nil { + return perr + } + if err = out.Close(); err != nil { + return unexpected(err) + } + + if err = os.Chmod(binaryPath, fi.Mode()); err != nil { + return unexpected(err) + } + + return nil } -func (a archiveAsset) open(ctx context.Context, args Args) (io.ReadCloser, error) { - log := output.LoggerFrom(ctx) - log.Debug("Opening archive") - fp := path.Join(args.Destination, a.Name) - f, err := os.Open(fp) - if err != nil { - return nil, fmt.Errorf("%w: %v", ErrUnexpected, err) +type compressedBinary struct { + path string + fs.FileInfo +} + +func (b compressedBinary) String() string { + return fmt.Sprintf("%s %s %s", b.path, b.Mode().Perm(), b.ModTime()) +} + +func findBinaries(ctx context.Context, args Args, fsys fs.FS) ([]compressedBinary, error) { + l := output.LoggerFrom(ctx) + binaries := make([]compressedBinary, 0, 1) + if err := fs.WalkDir(fsys, ".", func(p string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + l.WithFields(slog.Fields{"type": d.Type().Perm()}). + Debugf("Checking in-archive file: %s", p) + filename := d.Name() + var fi fs.FileInfo + if fi, err = d.Info(); err != nil { + return unexpected(err) + } + if !d.IsDir() && isExecutable(fi.Mode().Perm()) && strings.Contains(filename, args.FileName.BaseName) { + binaries = append(binaries, compressedBinary{p, fi}) + } + return nil + }); err != nil { + return nil, unexpected(err) } + l.WithFields(slog.Fields{"binaries": fmt.Sprintf("%q", binaries)}). + Debugf("Found %d binaries", len(binaries)) + return binaries, nil +} - switch a.ty() { - case archiveTypeZip: - zip.OpenReader() +func chooseBinaries(ctx context.Context, args Args, binaries []compressedBinary) ([]compressedBinary, error) { + if args.MultipleBinaries { + return binaries, nil + } + var ( + err error + binary compressedBinary + ) + if binary, err = chooseBinary(ctx, binaries); err != nil { + return nil, err } + return []compressedBinary{binary}, nil +} + +func chooseBinary(ctx context.Context, binaries []compressedBinary) (compressedBinary, error) { + l := output.LoggerFrom(ctx) + if len(binaries) != 1 { + l.Warnf("Can't choose binary automatically: %q", binaries) + if widgets, err := tui.Interactive[compressedBinary](ctx); err != nil { + return compressedBinary{}, fmt.Errorf("%w: can't choose binary: %q", + err, binaries) + } else { + return widgets.Choose(ctx, binaries, "Choose the binary"), nil + } + } + return binaries[0], nil +} + +func isExecutable(mode os.FileMode) bool { + return mode&0o111 != 0 } diff --git a/pkg/ghet/download/move.go b/pkg/ghet/download/move.go new file mode 100644 index 0000000..3a80333 --- /dev/null +++ b/pkg/ghet/download/move.go @@ -0,0 +1,37 @@ +package download + +import ( + "context" + "os" + "path" + "strings" + + "github.com/1set/gut/yos" + githubapi "github.com/cardil/ghet/pkg/github/api" + "github.com/cardil/ghet/pkg/output" + slog "github.com/go-eden/slf4go" +) + +func (p Plan) moveBinaries(ctx context.Context, args Args) error { + l := output.LoggerFrom(ctx) + index := githubapi.CreateIndex(p.Assets) + binaryName := args.FileName.ToString() + for _, binary := range index.Binaries { + if len(index.Binaries) > 1 { + binaryName = binary.Name + } + l.WithFields(slog.Fields{"binary": binary}).Debug("Moving binary") + source := p.cachePath(ctx, binary) + target := path.Join(args.Destination, binaryName) + if err := yos.MoveFile(source, target); err != nil { + return unexpected(err) + } + + if strings.Contains(binary.ContentType, "octet-stream") { + if err := os.Chmod(target, executablePerm); err != nil { + return unexpected(err) + } + } + } + return nil +} diff --git a/pkg/ghet/download/plan.go b/pkg/ghet/download/plan.go index 1514c7c..89b000f 100644 --- a/pkg/ghet/download/plan.go +++ b/pkg/ghet/download/plan.go @@ -97,27 +97,31 @@ func (p Plan) Download(ctx context.Context, args Args) error { total: len(p.Assets), longestName: longestName, } - if err := downloadAsset(ctx, ai, args); err != nil { + if err := p.downloadAsset(ctx, ai); err != nil { return err } } - if err := verifyChecksums(ctx, p.Assets, args); err != nil { + if err := p.verifyChecksums(ctx); err != nil { return err } - if err := extract(ctx, p.Assets, args); err != nil { + if err := p.extractArchives(ctx, args); err != nil { return err } - return nil + if err := p.moveBinaries(ctx, args); err != nil { + return err + } + + return p.cleanCache(ctx) } func prioritizeArchives(idx githubapi.IndexedAssets) []githubapi.Asset { - if len(idx.Archives) > 0 && len(idx.Other) > 0 { + if len(idx.Archives) > 0 && len(idx.Binaries) > 0 { assets := make([]githubapi.Asset, 0, len(idx.Archives)+len(idx.Checksums)) assets = append(assets, idx.Archives...) return append(assets, idx.Checksums...) } - assets := make([]githubapi.Asset, 0, len(idx.Archives)+len(idx.Other)+len(idx.Checksums)) - assets = append(assets, idx.Other...) + assets := make([]githubapi.Asset, 0, len(idx.Archives)+len(idx.Binaries)+len(idx.Checksums)) + assets = append(assets, idx.Binaries...) assets = append(assets, idx.Archives...) assets = append(assets, idx.Checksums...) return assets diff --git a/pkg/ghet/download/testdata/diskus-v0.7.0-x86_64-unknown-linux-gnu.tar.gz b/pkg/ghet/download/testdata/diskus-v0.7.0-x86_64-unknown-linux-gnu.tar.gz new file mode 100644 index 0000000..8249da6 Binary files /dev/null and b/pkg/ghet/download/testdata/diskus-v0.7.0-x86_64-unknown-linux-gnu.tar.gz differ diff --git a/pkg/ghet/download/testdata/kn-event-checksums.txt b/pkg/ghet/download/testdata/kn-event-checksums.txt new file mode 100644 index 0000000..0c336f2 --- /dev/null +++ b/pkg/ghet/download/testdata/kn-event-checksums.txt @@ -0,0 +1 @@ +5659d75b4f762396892c983bcc50dff69659a92c5d3a21f1b196c36c612d5fe2 kn-event-linux-amd64 diff --git a/pkg/ghet/download/testdata/kn-event-linux-amd64 b/pkg/ghet/download/testdata/kn-event-linux-amd64 new file mode 100755 index 0000000..e60d229 --- /dev/null +++ b/pkg/ghet/download/testdata/kn-event-linux-amd64 @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 + +print("kn event") diff --git a/pkg/ghet/download/testdata/minikube-linux-amd64.sha256 b/pkg/ghet/download/testdata/minikube-linux-amd64.sha256 new file mode 100644 index 0000000..f0815a5 --- /dev/null +++ b/pkg/ghet/download/testdata/minikube-linux-amd64.sha256 @@ -0,0 +1 @@ +88ac2fa04a48077d5b348d23014ee2e4e1098e6df8b00356a43b45e66e2db781 diff --git a/pkg/ghet/download/testdata/minikube-linux-amd64.tar.gz b/pkg/ghet/download/testdata/minikube-linux-amd64.tar.gz new file mode 100644 index 0000000..f80496a Binary files /dev/null and b/pkg/ghet/download/testdata/minikube-linux-amd64.tar.gz differ diff --git a/pkg/ghet/download/testdata/pulumi-3.71.0-checksums.txt b/pkg/ghet/download/testdata/pulumi-3.71.0-checksums.txt new file mode 100644 index 0000000..32d34ad --- /dev/null +++ b/pkg/ghet/download/testdata/pulumi-3.71.0-checksums.txt @@ -0,0 +1 @@ +SHA384 (pulumi-v3.71.0-linux-x64.tar.gz) = 9ec3b6bb3ac70090d5ec1165f54305f77c539bf2fbaf8f6e7af0a0be1efba61a0f5e83490a4f5f29ddc536c332d8d805 diff --git a/pkg/ghet/download/testdata/pulumi-v3.71.0-linux-x64.tar.gz b/pkg/ghet/download/testdata/pulumi-v3.71.0-linux-x64.tar.gz new file mode 100644 index 0000000..add3ec3 Binary files /dev/null and b/pkg/ghet/download/testdata/pulumi-v3.71.0-linux-x64.tar.gz differ diff --git a/pkg/ghet/install/args.go b/pkg/ghet/install/args.go index f14002d..c203f87 100644 --- a/pkg/ghet/install/args.go +++ b/pkg/ghet/install/args.go @@ -10,6 +10,7 @@ import ( type Args struct { github.Asset config.Site + MultipleBinaries bool } func (a Args) WithDefaults() Args { diff --git a/pkg/github/api/asset.go b/pkg/github/api/asset.go index 817e0b5..9e8132a 100644 --- a/pkg/github/api/asset.go +++ b/pkg/github/api/asset.go @@ -1,6 +1,8 @@ package api -import "github.com/cardil/ghet/pkg/match" +import ( + "github.com/cardil/ghet/pkg/match" +) type Asset struct { ID int64 @@ -10,10 +12,14 @@ type Asset struct { URL string } +func (a Asset) String() string { + return a.Name +} + type IndexedAssets struct { Archives []Asset Checksums []Asset - Other []Asset + Binaries []Asset } func CreateIndex(assets []Asset) IndexedAssets { @@ -26,7 +32,7 @@ func CreateIndex(assets []Asset) IndexedAssets { case isChecksum().Matches(name): index.Checksums = append(index.Checksums, asset) default: - index.Other = append(index.Other, asset) + index.Binaries = append(index.Binaries, asset) } } return index @@ -34,13 +40,18 @@ func CreateIndex(assets []Asset) IndexedAssets { func isArchive() match.Matcher { return match.Any( - match.EndsWith(".tar.gz"), + match.EndsWith(".gz"), match.EndsWith(".tgz"), - match.EndsWith(".tar.bz2"), + match.EndsWith(".bz2"), match.EndsWith(".tbz2"), - match.EndsWith(".tar.xz"), + match.EndsWith(".xz"), match.EndsWith(".txz"), + match.EndsWith(".lz"), + match.EndsWith(".tlz"), + match.EndsWith(".lz4"), match.EndsWith(".zip"), + match.EndsWith(".7z"), + match.EndsWith(".rar"), ) } diff --git a/pkg/output/logfile.go b/pkg/output/logfile.go index 240b7ba..8effba6 100644 --- a/pkg/output/logfile.go +++ b/pkg/output/logfile.go @@ -5,15 +5,14 @@ import ( "os" "path" - "github.com/cardil/ghet/pkg/metadata" - "github.com/kirsle/configdir" + configdir "github.com/cardil/ghet/pkg/config/dir" ) type logFileKey struct{} func EnsureLogFile(ctx context.Context) context.Context { if f := LogFileFrom(ctx); f == nil { - f = createLogFile() + f = createLogFile(ctx) return WithLogFile(ctx, f) } return ctx @@ -30,12 +29,8 @@ func WithLogFile(ctx context.Context, f *os.File) context.Context { return context.WithValue(ctx, logFileKey{}, f) } -func createLogFile() *os.File { - cachePath := configdir.LocalCache(metadata.Name) - // Ensure it exists. - if err := configdir.MakePath(cachePath); err != nil { - panic(err) - } +func createLogFile(ctx context.Context) *os.File { + cachePath := configdir.Cache(ctx) logPath := path.Join(cachePath, "last-exec.log.jsonl") if logFile, err := os.Create(logPath); err != nil { panic(err) diff --git a/pkg/output/tui/ask.go b/pkg/output/tui/choose.go similarity index 69% rename from pkg/output/tui/ask.go rename to pkg/output/tui/choose.go index 10aa491..8303572 100644 --- a/pkg/output/tui/ask.go +++ b/pkg/output/tui/choose.go @@ -8,9 +8,9 @@ import ( "github.com/erikgeiser/promptkit/selection" ) -type AskFunc func(ctx context.Context, options []string, format string, a ...any) string +type Chooser[T any] func(ctx context.Context, options []T, format string, a ...any) T -func NewBubbleAsk(ctx context.Context, options []string, format string, a ...any) string { +func BubbleChooser[T any](ctx context.Context, options []T, format string, a ...any) T { prt := output.PrinterFrom(ctx) l := output.LoggerFrom(ctx) sel := selection.New(fmt.Sprintf(format, a...), options) diff --git a/pkg/output/tui/progress.go b/pkg/output/tui/progress.go index f6ea47a..5ba8508 100644 --- a/pkg/output/tui/progress.go +++ b/pkg/output/tui/progress.go @@ -202,7 +202,7 @@ func (b *BubbleProgress) start() { go func() { t := b.tea _, _ = t.Run() - b.ended <- struct{}{} + close(b.ended) if err := t.ReleaseTerminal(); err != nil { panic(err) } diff --git a/pkg/output/tui/widgets.go b/pkg/output/tui/widgets.go index acd8ef4..f1f9455 100644 --- a/pkg/output/tui/widgets.go +++ b/pkg/output/tui/widgets.go @@ -41,16 +41,17 @@ func defaultWidgets() *Widgets { } } -func (w *Widgets) Interactive(ctx context.Context) (*InteractiveWidgets, error) { +func Interactive[T any](ctx context.Context) (*InteractiveWidgets[T], error) { prt := output.PrinterFrom(ctx) if !output.IsTerminal(prt.InOrStdin()) { return nil, errors.WithStack(ErrNotInteractive) } - return &InteractiveWidgets{ - Ask: NewBubbleAsk, + + return &InteractiveWidgets[T]{ + Choose: BubbleChooser[T], }, nil } -type InteractiveWidgets struct { - Ask AskFunc +type InteractiveWidgets[T any] struct { + Choose Chooser[T] }