diff --git a/.circleci/config.yml b/.circleci/config.yml index 303209559250..214309410db7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -79,17 +79,11 @@ jobs: at: /tmp/workspace - checkout - *dependencies - - run: - name: Get metalinter - command: | - export PATH="$GOBIN:$PATH" - make devtools-clean - make devtools - run: name: Lint source command: | export PATH="$GOBIN:$PATH" - make test_lint + make lint integration_tests: <<: *linux_defaults diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000000..ce8010e7aba6 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,17 @@ +linters: + disable-all: true + enable: + - errcheck + - golint + - ineffassign + - unconvert + - misspell +linters-settings: + gocyclo: + min-complexity: 11 + errcheck: + ignore: fmt:.*,io/ioutil:^Read.*,github.com/spf13/cobra:MarkFlagRequired,github.com/spf13/viper:BindPFlag + golint: + min-confidence: 1.1 +run: + tests: false diff --git a/.pending/improvements/sdk/Fixed-various-linter b/.pending/improvements/sdk/Fixed-various-linter new file mode 100644 index 000000000000..5b6cd8bb208d --- /dev/null +++ b/.pending/improvements/sdk/Fixed-various-linter @@ -0,0 +1 @@ +Fixed various linters warnings in the context of the gometalinter -> golangci-lint migration #3896. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3ebab274dd9..a6cd69fb45a8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -165,7 +165,7 @@ only pull requests targeted directly against master. ### Development Procedure: - the latest state of development is on `develop` - `develop` must never fail `make test` or `make test_cli` - - `develop` should not fail `make test_lint` + - `develop` should not fail `make lint` - no --force onto `develop` (except when reverting a broken commit, which should seldom happen) - create a development branch either on github.com/cosmos/cosmos-sdk, or your fork (using `git remote add origin`) - before submitting a pull request, begin `git rebase` on top of `develop` diff --git a/Makefile b/Makefile index 091c6f82bc69..156b2715a789 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,6 @@ VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') COMMIT := $(shell git log -1 --format='%H') CAT := $(if $(filter $(OS),Windows_NT),type,cat) LEDGER_ENABLED ?= true -GOTOOLS = \ - github.com/alecthomas/gometalinter \ - github.com/rakyll/statik GOBIN ?= $(GOPATH)/bin GOSUM := $(shell which gosum) @@ -62,7 +59,7 @@ ldflags := $(strip $(ldflags)) BUILD_FLAGS := -tags "$(build_tags)" -ldflags '$(ldflags)' -all: devtools install test_lint test +all: tools install lint test # The below include contains the tools target. include scripts/Makefile @@ -70,7 +67,7 @@ include scripts/Makefile ######################################## ### CI -ci: devtools install test_cover test_lint test +ci: tools install test_cover lint test ######################################## ### Build/Install @@ -108,31 +105,6 @@ dist: ######################################## ### Tools & dependencies -check_tools: - @# https://stackoverflow.com/a/25668869 - @echo "Found tools: $(foreach tool,$(notdir $(GOTOOLS)),\ - $(if $(shell which $(tool)),$(tool),$(error "No $(tool) in PATH")))" - -update_tools: - @echo "--> Updating tools to correct version" - $(MAKE) --always-make tools - -update_dev_tools: - @echo "--> Downloading linters (this may take awhile)" - $(GOPATH)/src/github.com/alecthomas/gometalinter/scripts/install.sh -b $(GOBIN) - go get -u github.com/tendermint/lint/golint - -devtools: devtools-stamp -devtools-stamp: tools - @echo "--> Downloading linters (this may take awhile)" - $(GOPATH)/src/github.com/alecthomas/gometalinter/scripts/install.sh -b $(GOBIN) - go get github.com/tendermint/lint/golint - go install -mod=readonly ./cmd/sdkch - touch $@ - -devtools-clean: tools-clean - rm -f devtools-stamp - go-mod-cache: go.sum @echo "--> Download go modules to local cache" @go mod download @@ -147,7 +119,7 @@ draw_deps: tools @goviz -i github.com/cosmos/cosmos-sdk/cmd/gaia/cmd/gaiad -d 2 | dot -Tpng -o dependency-graph.png clean: - rm -f devtools-stamp snapcraft-local.yaml + rm -f snapcraft-local.yaml distclean: clean rm -rf vendor/ @@ -227,13 +199,13 @@ test_sim_gaia_profile: test_cover: @export VERSION=$(VERSION); bash -x tests/test_cover.sh -test_lint: - gometalinter --config=tools/gometalinter.json ./... - !(gometalinter --exclude /usr/lib/go/src/ --exclude client/lcd/statik/statik.go --exclude 'vendor/*' --disable-all --enable='errcheck' --vendor ./... | grep -v "client/") +lint: tools + golangci-lint run + go vet -composites=false -tests=false ./... find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s go mod verify -format: +format: tools find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs gofmt -w -s find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs misspell -w find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs goimports -w -local github.com/cosmos/cosmos-sdk @@ -292,10 +264,10 @@ snapcraft-local.yaml: snapcraft-local.yaml.in # unless there is a reason not to. # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html .PHONY: build install install_debug dist clean distclean \ -check_tools check_dev_tools get_vendor_deps draw_deps test test_cli test_unit \ -test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \ +draw_deps test test_cli test_unit \ +test_cover lint benchmark devdoc_init devdoc devdoc_save devdoc_update \ build-linux build-docker-gaiadnode localnet-start localnet-stop \ format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \ test_sim_gaia_custom_genesis_fast test_sim_gaia_custom_genesis_multi_seed \ -test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools \ -devtools-clean go-mod-cache +test_sim_gaia_multi_seed test_sim_gaia_import_export \ +go-mod-cache diff --git a/client/keys/types.go b/client/keys/types.go index 75ecabe835b3..079ef4962d24 100644 --- a/client/keys/types.go +++ b/client/keys/types.go @@ -11,6 +11,17 @@ type AddNewKey struct { Index int `json:"index,string,omitempty"` } +// NewAddNewKey constructs a new AddNewKey request structure. +func NewAddNewKey(name, password, mnemonic string, account, index int) AddNewKey { + return AddNewKey{ + Name: name, + Password: password, + Mnemonic: mnemonic, + Account: account, + Index: index, + } +} + // RecoverKeyBody recovers a key type RecoverKey struct { Password string `json:"password"` @@ -19,13 +30,26 @@ type RecoverKey struct { Index int `json:"index,string,omitempty"` } +// NewRecoverKey constructs a new RecoverKey request structure. +func NewRecoverKey(password, mnemonic string, account, index int) RecoverKey { + return RecoverKey{Password: password, Mnemonic: mnemonic, Account: account, Index: index} +} + // UpdateKeyReq requests updating a key type UpdateKeyReq struct { OldPassword string `json:"old_password"` NewPassword string `json:"new_password"` } +// NewUpdateKeyReq constructs a new UpdateKeyReq structure. +func NewUpdateKeyReq(old, new string) UpdateKeyReq { + return UpdateKeyReq{OldPassword: old, NewPassword: new} +} + // DeleteKeyReq requests deleting a key type DeleteKeyReq struct { Password string `json:"password"` } + +// NewDeleteKeyReq constructs a new DeleteKeyReq structure. +func NewDeleteKeyReq(password string) DeleteKeyReq { return DeleteKeyReq{Password: password} } diff --git a/client/lcd/certificates.go b/client/lcd/certificates.go index 0ec527e134ac..3260ebfc2dff 100644 --- a/client/lcd/certificates.go +++ b/client/lcd/certificates.go @@ -145,7 +145,9 @@ func fingerprintForCertificate(certBytes []byte) (string, error) { return "", err } h := sha256.New() - h.Write(cert.Raw) + if _, err := h.Write(cert.Raw); err != nil { + return "", err + } fingerprintBytes := h.Sum(nil) var buf bytes.Buffer for i, b := range fingerprintBytes { diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index d80c060df49b..3360e0112256 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -227,7 +227,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress genDoc, err := tmtypes.GenesisDocFromFile(genesisFile) require.Nil(t, err) genDoc.Validators = nil - genDoc.SaveAs(genesisFile) + require.NoError(t, genDoc.SaveAs(genesisFile)) genTxs := []json.RawMessage{} // append any additional (non-proposing) validators @@ -337,7 +337,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress cleanup = func() { logger.Debug("cleaning up LCD initialization") - node.Stop() + node.Stop() //nolint:errcheck node.Wait() lcd.Close() } @@ -394,7 +394,7 @@ func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec, t *testing if err != nil { return nil, err } - go tmrpc.StartHTTPServer(listener, rs.Mux, logger) + go tmrpc.StartHTTPServer(listener, rs.Mux, logger) //nolint:errcheck return listener, nil } @@ -559,7 +559,7 @@ func getKeys(t *testing.T, port string) []keys.KeyOutput { // POST /keys Create a new account locally func doKeysPost(t *testing.T, port, name, password, mnemonic string, account int, index int) keys.KeyOutput { - pk := clientkeys.AddNewKey{name, password, mnemonic, account, index} + pk := clientkeys.NewAddNewKey(name, password, mnemonic, account, index) req, err := cdc.MarshalJSON(pk) require.NoError(t, err) @@ -585,7 +585,7 @@ func getKeysSeed(t *testing.T, port string) string { // POST /keys/{name}/recove Recover a account from a seed func doRecoverKey(t *testing.T, port, recoverName, recoverPassword, mnemonic string, account uint32, index uint32) { - pk := clientkeys.RecoverKey{recoverPassword, mnemonic, int(account), int(index)} + pk := clientkeys.NewRecoverKey(recoverPassword, mnemonic, int(account), int(index)) req, err := cdc.MarshalJSON(pk) require.NoError(t, err) @@ -613,7 +613,7 @@ func getKey(t *testing.T, port, name string) keys.KeyOutput { // PUT /keys/{name} Update the password for this account in the KMS func updateKey(t *testing.T, port, name, oldPassword, newPassword string, fail bool) { - kr := clientkeys.UpdateKeyReq{oldPassword, newPassword} + kr := clientkeys.NewUpdateKeyReq(oldPassword, newPassword) req, err := cdc.MarshalJSON(kr) require.NoError(t, err) keyEndpoint := fmt.Sprintf("/keys/%s", name) @@ -627,7 +627,7 @@ func updateKey(t *testing.T, port, name, oldPassword, newPassword string, fail b // DELETE /keys/{name} Remove an account func deleteKey(t *testing.T, port, name, password string) { - dk := clientkeys.DeleteKeyReq{password} + dk := clientkeys.NewDeleteKeyReq(password) req, err := cdc.MarshalJSON(dk) require.NoError(t, err) keyEndpoint := fmt.Sprintf("/keys/%s", name) diff --git a/client/rest/rest.go b/client/rest/rest.go index 81d2cc57b933..e44358317486 100644 --- a/client/rest/rest.go +++ b/client/rest/rest.go @@ -1,6 +1,7 @@ package rest import ( + "log" "net/http" "github.com/cosmos/cosmos-sdk/client" @@ -67,6 +68,8 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec, } w.Header().Set("Content-Type", "application/json") - w.Write(output) + if _, err := w.Write(output); err != nil { + log.Printf("couldn't write response: %v", err) + } return } diff --git a/client/rpc/block.go b/client/rpc/block.go index 127e9c9c8d91..6d6e0a9fd00e 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -115,20 +115,19 @@ func BlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { vars := mux.Vars(r) height, err := strconv.ParseInt(vars["height"], 10, 64) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/block/{height}'.")) + rest.WriteErrorResponse(w, http.StatusBadRequest, + "ERROR: Couldn't parse block height. Assumed format is '/block/{height}'.") return } chainHeight, err := GetChainHeight(cliCtx) if height > chainHeight { - w.WriteHeader(http.StatusNotFound) - w.Write([]byte("ERROR: Requested block height is bigger then the chain length.")) + rest.WriteErrorResponse(w, http.StatusNotFound, + "ERROR: Requested block height is bigger then the chain length.") return } output, err := getBlock(cliCtx, &height) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) @@ -140,14 +139,12 @@ func LatestBlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { height, err := GetChainHeight(cliCtx) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } output, err := getBlock(cliCtx, &height) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) diff --git a/client/rpc/root.go b/client/rpc/root.go index 45533e3eaacf..e7c9ff36fd31 100644 --- a/client/rpc/root.go +++ b/client/rpc/root.go @@ -2,6 +2,7 @@ package rpc import ( "fmt" + "log" "net/http" "github.com/gorilla/mux" @@ -26,7 +27,9 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { // cli version REST handler endpoint func CLIVersionRequestHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Write([]byte(fmt.Sprintf("{\"version\": \"%s\"}", version.Version))) + if _, err := w.Write([]byte(fmt.Sprintf("{\"version\": \"%s\"}", version.Version))); err != nil { + log.Printf("couldn't write response: %v", err) + } } // connected node version REST handler endpoint @@ -39,6 +42,8 @@ func NodeVersionRequestHandler(cliCtx context.CLIContext) http.HandlerFunc { } w.Header().Set("Content-Type", "application/json") - w.Write(version) + if _, err := w.Write(version); err != nil { + log.Printf("couldn't write response: %v", err) + } } } diff --git a/client/rpc/status.go b/client/rpc/status.go index e017fa860c2b..ba13183d3d8b 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -2,6 +2,7 @@ package rpc import ( "fmt" + "log" "net/http" "strconv" @@ -71,8 +72,7 @@ func NodeInfoRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { status, err := getNodeStatus(cliCtx) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } @@ -86,18 +86,18 @@ func NodeSyncingRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { status, err := getNodeStatus(cliCtx) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } syncing := status.SyncInfo.CatchingUp if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - w.Write([]byte(strconv.FormatBool(syncing))) + if _, err := w.Write([]byte(strconv.FormatBool(syncing))); err != nil { + log.Printf("couldn'tv write response: %v", err) + } } } diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 90389bf643ed..2e5b0cb73a2e 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -160,22 +160,19 @@ func ValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { height, err := strconv.ParseInt(vars["height"], 10, 64) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/validatorsets/{height}'.")) + rest.WriteErrorResponse(w, http.StatusBadRequest, "ERROR: Couldn't parse block height. Assumed format is '/validatorsets/{height}'.") return } chainHeight, err := GetChainHeight(cliCtx) if height > chainHeight { - w.WriteHeader(http.StatusNotFound) - w.Write([]byte("ERROR: Requested block height is bigger then the chain length.")) + rest.WriteErrorResponse(w, http.StatusNotFound, "ERROR: Requested block height is bigger then the chain length.") return } output, err := getValidators(cliCtx, &height) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) @@ -187,15 +184,13 @@ func LatestValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerF return func(w http.ResponseWriter, r *http.Request) { height, err := GetChainHeight(cliCtx) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } output, err := getValidators(cliCtx, &height) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) diff --git a/client/tx/broadcast.go b/client/tx/broadcast.go index a0437b1cd1d1..d80475c63840 100644 --- a/client/tx/broadcast.go +++ b/client/tx/broadcast.go @@ -110,7 +110,7 @@ $ gaiacli tx broadcast ./mytxn.json } res, err := cliCtx.BroadcastTx(txBytes) - cliCtx.PrintOutput(res) + cliCtx.PrintOutput(res) // nolint:errcheck return err }, } diff --git a/client/tx/encode.go b/client/tx/encode.go index 75670aade91d..5ea64ea720cc 100644 --- a/client/tx/encode.go +++ b/client/tx/encode.go @@ -97,7 +97,7 @@ If you supply a dash (-) argument in place of an input filename, the command rea txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes) response := txEncodeRespStr(txBytesBase64) - cliCtx.PrintOutput(response) + cliCtx.PrintOutput(response) // nolint:errcheck return nil }, diff --git a/client/utils/utils.go b/client/utils/utils.go index d924d9204254..8bd8671fb58f 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -3,10 +3,11 @@ package utils import ( "bytes" "fmt" - "github.com/spf13/viper" "io/ioutil" "os" + "github.com/spf13/viper" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -102,7 +103,7 @@ func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIConte // broadcast to a Tendermint node res, err := cliCtx.BroadcastTx(txBytes) - cliCtx.PrintOutput(res) + cliCtx.PrintOutput(res) // nolint:errcheck return err } diff --git a/scripts/Makefile b/scripts/Makefile index 24e12fffef63..2ff10d46898b 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -33,26 +33,32 @@ cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && git fetch origin && git checkout -q $(3) go_install = $(call go_get,$(1),$(2),$(3)) && cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && $(GO) install +mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) +mkfile_dir := $(shell cd $(shell dirname $(mkfile_path)); pwd) + ### # tools ### all: tools -tools: $(GOPATH)/bin/gometalinter $(GOPATH)/bin/statik $(GOPATH)/bin/goimports $(GOPATH)/bin/gosum +tools: $(GOBIN)/golangci-lint $(GOBIN)/statik $(GOBIN)/goimports $(GOBIN)/gosum $(GOBIN)/sdkch #v2.0.11 -$(GOPATH)/bin/gometalinter: - $(call go_install,alecthomas,gometalinter,v3.0.0) +$(GOBIN)/golangci-lint: + sh tools/install-golangci.sh -d -b $(GOBIN) -$(GOPATH)/bin/statik: +$(GOBIN)/statik: $(call go_install,rakyll,statik,v0.1.5) -$(GOPATH)/bin/goimports: +$(GOBIN)/goimports: go get golang.org/x/tools/cmd/goimports@v0.0.0-20190114222345-bf090417da8b -$(GOPATH)/bin/gosum: - go install ./cmd/gosum/ +$(GOBIN)/gosum: + go install -mod=readonly ./cmd/gosum/ + +$(GOBIN)/sdkch: + go install -mod=readonly ./cmd/sdkch/ tools-clean: - cd $(GOPATH)/bin && rm -f gometalinter statik goimports gosum + cd $(GOBIN) && rm -f golangci-lint statik goimports gosum sdkch .PHONY: all tools-clean diff --git a/tools/gometalinter.json b/tools/gometalinter.json deleted file mode 100644 index 0c4b49b3d411..000000000000 --- a/tools/gometalinter.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Linters": { - "vet": "go tool vet -composites=false :PATH:LINE:MESSAGE" - }, - "Enable": ["golint", "vet", "ineffassign", "misspell"], - "Deadline": "500s", - "Vendor": true, - "Cyclo": 11, - "Exclude": ["/usr/lib/go/src/", "client/lcd/statik/statik.go", "vendor/*"] -} diff --git a/tools/install-golangci.sh b/tools/install-golangci.sh new file mode 100755 index 000000000000..752f2d4cb15f --- /dev/null +++ b/tools/install-golangci.sh @@ -0,0 +1,388 @@ +#!/bin/sh +set -e +# Code generated by godownloader on 2018-06-05T12:04:55Z. DO NOT EDIT. +# + +usage() { + this=$1 + cat </dev/null +} +echoerr() { + echo "$@" 1>&2 +} +log_prefix() { + echo "$0" +} +_logp=6 +log_set_priority() { + _logp="$1" +} +log_priority() { + if test -z "$1"; then + echo "$_logp" + return + fi + [ "$1" -le "$_logp" ] +} +log_tag() { + case $1 in + 0) echo "emerg" ;; + 1) echo "alert" ;; + 2) echo "crit" ;; + 3) echo "err" ;; + 4) echo "warning" ;; + 5) echo "notice" ;; + 6) echo "info" ;; + 7) echo "debug" ;; + *) echo "$1" ;; + esac +} +log_debug() { + log_priority 7 || return 0 + echoerr "$(log_prefix)" "$(log_tag 7)" "$@" +} +log_info() { + log_priority 6 || return 0 + echoerr "$(log_prefix)" "$(log_tag 6)" "$@" +} +log_err() { + log_priority 3 || return 0 + echoerr "$(log_prefix)" "$(log_tag 3)" "$@" +} +log_crit() { + log_priority 2 || return 0 + echoerr "$(log_prefix)" "$(log_tag 2)" "$@" +} +uname_os() { + os=$(uname -s | tr '[:upper:]' '[:lower:]') + case "$os" in + msys_nt) os="windows" ;; + esac + echo "$os" +} +uname_arch() { + arch=$(uname -m) + case $arch in + x86_64) arch="amd64" ;; + x86) arch="386" ;; + i686) arch="386" ;; + i386) arch="386" ;; + aarch64) arch="arm64" ;; + armv5*) arch="armv5" ;; + armv6*) arch="armv6" ;; + armv7*) arch="armv7" ;; + esac + echo ${arch} +} +uname_os_check() { + os=$(uname_os) + case "$os" in + darwin) return 0 ;; + dragonfly) return 0 ;; + freebsd) return 0 ;; + linux) return 0 ;; + android) return 0 ;; + nacl) return 0 ;; + netbsd) return 0 ;; + openbsd) return 0 ;; + plan9) return 0 ;; + solaris) return 0 ;; + windows) return 0 ;; + esac + log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib" + return 1 +} +uname_arch_check() { + arch=$(uname_arch) + case "$arch" in + 386) return 0 ;; + amd64) return 0 ;; + arm64) return 0 ;; + armv5) return 0 ;; + armv6) return 0 ;; + armv7) return 0 ;; + ppc64) return 0 ;; + ppc64le) return 0 ;; + mips) return 0 ;; + mipsle) return 0 ;; + mips64) return 0 ;; + mips64le) return 0 ;; + s390x) return 0 ;; + amd64p32) return 0 ;; + esac + log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib" + return 1 +} +untar() { + tarball=$1 + case "${tarball}" in + *.tar.gz | *.tgz) tar -xzf "${tarball}" ;; + *.tar) tar -xf "${tarball}" ;; + *.zip) unzip "${tarball}" ;; + *) + log_err "untar unknown archive format for ${tarball}" + return 1 + ;; + esac +} +mktmpdir() { + test -z "$TMPDIR" && TMPDIR="$(mktemp -d)" + mkdir -p "${TMPDIR}" + echo "${TMPDIR}" +} +http_download_curl() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") + else + code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") + fi + if [ "$code" != "200" ]; then + log_debug "http_download_curl received HTTP status $code" + return 1 + fi + return 0 +} +http_download_wget() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + wget -q -O "$local_file" "$source_url" + else + wget -q --header "$header" -O "$local_file" "$source_url" + fi +} +http_download() { + log_debug "http_download $2" + if is_command curl; then + http_download_curl "$@" + return + elif is_command wget; then + http_download_wget "$@" + return + fi + log_crit "http_download unable to find wget or curl" + return 1 +} +http_copy() { + tmp=$(mktemp) + http_download "${tmp}" "$1" "$2" || return 1 + body=$(cat "$tmp") + rm -f "${tmp}" + echo "$body" +} +github_release() { + owner_repo=$1 + version=$2 + test -z "$version" && version="latest" + giturl="https://github.com/${owner_repo}/releases/${version}" + json=$(http_copy "$giturl" "Accept:application/json") + test -z "$json" && return 1 + version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//') + test -z "$version" && return 1 + echo "$version" +} +hash_sha256() { + TARGET=${1:-/dev/stdin} + if is_command gsha256sum; then + hash=$(gsha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command sha256sum; then + hash=$(sha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command shasum; then + hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command openssl; then + hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f a + else + log_crit "hash_sha256 unable to find command to compute sha-256 hash" + return 1 + fi +} +hash_sha256_verify() { + TARGET=$1 + checksums=$2 + if [ -z "$checksums" ]; then + log_err "hash_sha256_verify checksum file not specified in arg2" + return 1 + fi + BASENAME=${TARGET##*/} + want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1) + if [ -z "$want" ]; then + log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'" + return 1 + fi + got=$(hash_sha256 "$TARGET") + if [ "$want" != "$got" ]; then + log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got" + return 1 + fi +} +cat /dev/null <