diff --git a/go.sum b/go.sum index 4950aa62c..88da6182a 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= -github.com/alexellis/go-execute/v2 v2.0.0 h1:e2fB9kZcPG0yg65XHL1/t6efcCUdt32AMbr/mv7A2tc= -github.com/alexellis/go-execute/v2 v2.0.0/go.mod h1:FMdRnUTiFAmYXcv23txrp3VYZfLo24nMpiIneWgKHTQ= github.com/alexellis/go-execute/v2 v2.1.0 h1:0HccwWLNzAonu9Mei2bL8dQThHoaS1c/vq2hQwQW0XY= github.com/alexellis/go-execute/v2 v2.1.0/go.mod h1:FMdRnUTiFAmYXcv23txrp3VYZfLo24nMpiIneWgKHTQ= github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo= @@ -13,7 +11,6 @@ github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNA github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc= github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -27,18 +24,13 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ= github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -56,22 +48,17 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= -github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/otiai10/copy v1.12.0 h1:cLMgSQnXBs1eehF0Wy/FAGsgDTDmAqFR7rQylBb1nDY= github.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= -github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= @@ -83,7 +70,6 @@ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRM 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/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= @@ -99,10 +85,6 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= diff --git a/pkg/get/download.go b/pkg/get/download.go index a250aafbb..b0ea1615e 100644 --- a/pkg/get/download.go +++ b/pkg/get/download.go @@ -89,11 +89,17 @@ func Download(tool *Tool, arch, operatingSystem, version string, movePath string if !quiet { log.Printf("Copying %s to %s\n", outFilePath, localPath) } - _, err = CopyFile(outFilePath, localPath) - if err != nil { + + if _, err = CopyFile(outFilePath, localPath); err != nil { return "", "", err } + // Remove parent folder of the binary + tempPath := filepath.Dir(outFilePath) + if err := os.RemoveAll(tempPath); err != nil { + log.Printf("Error removing temporary directory: %s", err) + } + outFilePath = localPath return outFilePath, finalName, nil diff --git a/pkg/update/update.go b/pkg/update/update.go index 241043331..f73ef21d8 100644 --- a/pkg/update/update.go +++ b/pkg/update/update.go @@ -1,20 +1,19 @@ package update import ( - "context" - "crypto/sha256" "fmt" "io" - "net/http" "os" "path/filepath" - "strings" - "github.com/alexellis/arkade/pkg" "github.com/alexellis/arkade/pkg/get" - "github.com/alexellis/go-execute/v2" ) +type Resolver interface { + GetRelease() (string, error) + GetDownloadURL(release string) (string, error) +} + type Updater struct { resolver Resolver verify bool @@ -23,87 +22,12 @@ type Updater struct { versionCheck VersionCheck } -type Resolver interface { - GetRelease() (string, error) - GetDownloadURL(release string) (string, error) -} - -type Verifier interface { - Verify(digestUrl, newBinary string) error -} - -type DefaultVerifier struct { -} - -func (d DefaultVerifier) Verify(downloadUrl, newBinary string) error { - digest, err := downloadDigest(downloadUrl + ".sha256") - if err != nil { - return err - } - - if err := compareSHA(digest, newBinary); err != nil { - return fmt.Errorf("checksum failed for %s, error: %w", newBinary, err) - } - - fmt.Printf("Checksum verified..OK.\n") - return nil -} - -func downloadDigest(uri string) (string, error) { - req, err := http.NewRequest(http.MethodGet, uri, nil) - if err != nil { - return "", err - } - - req.Header.Set("User-Agent", pkg.UserAgent()) - - res, err := http.DefaultClient.Do(req) - if err != nil { - return "", err - } - - var body []byte - if res.Body != nil { - defer res.Body.Close() - body, _ = io.ReadAll(res.Body) - } - - if res.StatusCode != http.StatusOK { - return "", fmt.Errorf("unexpected status code %d, body: %s", res.StatusCode, string(body)) - } - - return string(body), nil -} - -// compareSHA returns a nil error if the local digest matches the remote digest -func compareSHA(remoteDigest, localFile string) error { - - // GitHub format may sometimes include the binary name and a space, i.e. - // "9dcfd1611440aa15333980b860220bcd55ca1d6875692facc458caf7eb1cd042 bin/arkade-darwin-arm64" - if strings.Contains(remoteDigest, " ") { - t, _, _ := strings.Cut(remoteDigest, " ") - remoteDigest = t - } - - localDigest, err := getSHA256Checksum(localFile) - if err != nil { - return err - } - - if remoteDigest != localDigest { - return fmt.Errorf("checksum mismatch, want: %s, but got: %s", remoteDigest, localDigest) - } - - return nil -} - -func getSHA256Checksum(path string) (string, error) { - f, err := os.ReadFile(path) - if err != nil { - return "", err +func NewUpdater() Updater { + return Updater{ + verify: true, + force: false, + versionCheck: DefaultVersionCheck{}, } - - return fmt.Sprintf("%x", sha256.Sum256(f)), nil } func (u Updater) WithVerifier(verifier Verifier) Updater { @@ -116,14 +40,6 @@ func (u Updater) WithResolver(resolver Resolver) Updater { return u } -func NewUpdater() Updater { - return Updater{ - verify: true, - force: false, - versionCheck: DefaultVersionCheck{}, - } -} - func (u Updater) WithVersionCheck(check VersionCheck) Updater { u.versionCheck = check return u @@ -224,35 +140,3 @@ func replaceExec(currentExec, newBinary string) error { return nil } - -type VersionCheck interface { - UpdateRequired(target string) (bool, error) -} - -type DefaultVersionCheck struct { - Command string - Argument string -} - -func (d DefaultVersionCheck) UpdateRequired(target string) (bool, error) { - executable, err := os.Executable() - if err != nil { - return false, err - } - - task := execute.ExecTask{ - Command: executable, - Args: []string{"version"}, - } - - res, err := task.Execute(context.TODO()) - if err != nil { - return false, err - } - - if !strings.Contains(res.Stdout, target) { - return true, nil - } - - return false, nil -} diff --git a/pkg/update/verifier.go b/pkg/update/verifier.go new file mode 100644 index 000000000..efa7d163c --- /dev/null +++ b/pkg/update/verifier.go @@ -0,0 +1,90 @@ +package update + +import ( + "crypto/sha256" + "fmt" + "io" + "net/http" + "os" + "strings" + + "github.com/alexellis/arkade/pkg" +) + +type Verifier interface { + Verify(digestUrl, newBinary string) error +} + +type DefaultVerifier struct { +} + +func (d DefaultVerifier) Verify(downloadUrl, newBinary string) error { + digest, err := downloadDigest(downloadUrl + ".sha256") + if err != nil { + return err + } + + if err := compareSHA(digest, newBinary); err != nil { + return fmt.Errorf("checksum failed for %s, error: %w", newBinary, err) + } + + fmt.Printf("Checksum verified..OK.\n") + return nil +} + +func downloadDigest(uri string) (string, error) { + req, err := http.NewRequest(http.MethodGet, uri, nil) + if err != nil { + return "", err + } + + req.Header.Set("User-Agent", pkg.UserAgent()) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + + var body []byte + if res.Body != nil { + defer res.Body.Close() + body, _ = io.ReadAll(res.Body) + } + + if res.StatusCode != http.StatusOK { + return "", fmt.Errorf("unexpected status code %d, body: %s", res.StatusCode, string(body)) + } + + return string(body), nil +} + +// compareSHA returns a nil error if the local digest matches the remote digest +func compareSHA(remoteDigest, localFile string) error { + + // GitHub format may sometimes include the binary name and a space, i.e. + // "9dcfd1611440aa15333980b860220bcd55ca1d6875692facc458caf7eb1cd042 bin/arkade-darwin-arm64" + if strings.Contains(remoteDigest, " ") { + t, _, _ := strings.Cut(remoteDigest, " ") + remoteDigest = t + } + + localDigest, err := getSHA256Checksum(localFile) + if err != nil { + return err + } + + if remoteDigest != localDigest { + return fmt.Errorf("checksum mismatch, want: %s, but got: %s", remoteDigest, localDigest) + } + + return nil +} + +func getSHA256Checksum(path string) (string, error) { + f, err := os.ReadFile(path) + if err != nil { + return "", err + } + + return fmt.Sprintf("%x", sha256.Sum256(f)), nil +} diff --git a/pkg/update/version_check.go b/pkg/update/version_check.go new file mode 100644 index 000000000..376cd277e --- /dev/null +++ b/pkg/update/version_check.go @@ -0,0 +1,41 @@ +package update + +import ( + "context" + "os" + "strings" + + "github.com/alexellis/go-execute/v2" +) + +type VersionCheck interface { + UpdateRequired(target string) (bool, error) +} + +type DefaultVersionCheck struct { + Command string + Argument string +} + +func (d DefaultVersionCheck) UpdateRequired(target string) (bool, error) { + executable, err := os.Executable() + if err != nil { + return false, err + } + + task := execute.ExecTask{ + Command: executable, + Args: []string{"version"}, + } + + res, err := task.Execute(context.TODO()) + if err != nil { + return false, err + } + + if !strings.Contains(res.Stdout, target) { + return true, nil + } + + return false, nil +}