diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b37e9b..521a2af 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v2 with: - go-version: '1.15.0' # The Go version to download (if necessary) and use. + go-version: '1.18.3' # The Go version to download (if necessary) and use. # - name: Install dependencies # run: | # go version @@ -29,7 +29,7 @@ jobs: uses: golangci/golangci-lint-action@v2 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: v1.31 + version: v1.52.2 # Optional: show only new issues if it's a pull request. The default value is `false`. only-new-issues: true # Run build of the application diff --git a/.golangci.yml b/.golangci.yml index a7518af..b04cbae 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -18,7 +18,7 @@ linters-settings: lll: line-length: 140 goimports: - local-prefixes: github.com/avarabyeu/goRP/v5 + local-prefixes: github.com/reportportal/goRP/v5 gocritic: enabled-tags: - performance @@ -41,6 +41,15 @@ linters: - testpackage - nlreturn - stylecheck + - exhaustivestruct + - exhaustruct + - varnamelen + - tagliatelle + - wrapcheck + - gomnd + - gci + - revive + - testableexamples #run: # skip-dirs: @@ -53,9 +62,3 @@ issues: linters: - gosec -# golangci.com configuration -# https://github.com/golangci/golangci/wiki/Configuration -service: - golangci-lint-version: 1.15.x # use the fixed version to not introduce new linters unexpectedly -# prepare: -# - echo "here I can run custom commands, but no preparation needed for this repo" diff --git a/.goreleaser.yml b/.goreleaser.yml index 543289c..0eb1438 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,17 +1,25 @@ builds: - - env: + - id: gorp + main: ./cmd/gorp + binary: gorp + env: - CGO_ENABLED=0 goos: - linux - windows - darwin + goarch: + - amd64 + - arm + - arm64 archives: - - replacements: - darwin: Darwin - linux: Linux - windows: Windows - 386: i386 - amd64: x86_64 + - name_template: >- + {{- .ProjectName }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} + {{- if .Arm }}v{{ .Arm }}{{ end -}} checksum: name_template: 'checksums.txt' snapshot: diff --git a/.travis.yml b/.travis.yml index d82a79f..38b83ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,4 +13,4 @@ script: - make checkstyle build after_success: - - curl --request POST "https://goreportcard.com/checks" --data "repo=github.com/avarabyeu/goRP" + - curl --request POST "https://goreportcard.com/checks" --data "repo=github.com/reportportal/goRP" diff --git a/Dockerfile b/Dockerfile index 19336fa..c55250a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ ARG dockerTag RUN echo $dockerTag | awk -F"v" '{ print $2 }' > version RUN cat ./version -RUN version=$(cat version) && curl -L >gorp.tar.gz https://github.com/avarabyeu/goRP/releases/download/$dockerTag/goRP_${version}_linux_amd64.tar.gz \ +RUN version=$(cat version) && curl -L >gorp.tar.gz https://github.com/reportportal/goRP/releases/download/$dockerTag/goRP_${version}_linux_amd64.tar.gz \ && tar -xzvf gorp.tar.gz -C /usr/bin \ && rm gorp.tar.gz diff --git a/Makefile b/Makefile index 2e4ec25..38f1752 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,5 @@ .DEFAULT_GOAL := build - BUILD_DATE = `date +%FT%T%z` - GO = go BINARY_DIR=bin @@ -17,11 +15,7 @@ help: @echo "checkstyle - gofmt+golint+misspell" init-deps: - # installs gometalinter -# curl -L https://git.io/vp6lP | sh -# gometalinter --install - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.31.0 - + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.52.2 #vendor: # dep ensure --vendor-only @@ -30,19 +24,18 @@ test: $(GO) test -cover ${GODIRS_NOVENDOR} lint: - golangci-lint run --enable-all --deadline 10m ./... + bin/golangci-lint run --enable-all --deadline 10m ./... fmt: - gofumpt -extra -l -w -s ${GOFILES_NOVENDOR} - gofumports -local -l -w ${GOFILES_NOVENDOR} - gci -local github.com/avarabyeu/goRP/v5 -w ${GOFILES_NOVENDOR} + gofumpt -extra -l -w ${GOFILES_NOVENDOR} + gci write --section Standard --section Default --section "Prefix(github.com/reportportal/goRP/v5)" ${GOFILES_NOVENDOR} #build: checkstyle test build: - $(GO) build ${BUILD_INFO_LDFLAGS} -o ${BINARY_DIR}/gorp ./ + $(GO) build ${BUILD_INFO_LDFLAGS} -o ${BINARY_DIR}/gorp ./cmd/gorp cross-build: - gox ${BUILD_INFO_LDFLAGS} -arch="amd64 386" -os="linux windows darwin" -output="dist/{{.Dir}}_{{.OS}}_{{.Arch}}" + gox ${BUILD_INFO_LDFLAGS} -arch="amd64 arm64" -os="linux windows darwin" -output="dist/{{.Dir}}_{{.OS}}_{{.Arch}}" ./cmd/gorp clean: if [ -d ${BINARY_DIR} ] ; then rm -r ${BINARY_DIR} ; fi diff --git a/README.md b/README.md index 41f7432..16ed29b 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,88 @@ -![GitHub Workflow Status](https://img.shields.io/github/workflow/status/avarabyeu/goRP/Build) +![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/reportportal/goRP/build.yml?branch=master) [![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/eBay/fabio/master/LICENSE) -[![Go Report Card](https://goreportcard.com/badge/github.com/avarabyeu/goRP)](https://goreportcard.com/report/github.com/avarabyeu/goRP) +[![Go Report Card](https://goreportcard.com/badge/github.com/reportportal/goRP)](https://goreportcard.com/report/github.com/reportportal/goRP) # goRP + Golang Client and CLI Utility for [ReportPortal](https://reportportal.io) ## Installation ## Usage + ``` gorp [global options] command [command options] [arguments...] COMMANDS: - launch Operations over launches - help, h Shows a list of commands or help for one command + launch Operations over launches + report Reports input to report portal + init Initializes configuration cache + help, h Shows a list of commands or help for one command GLOBAL OPTIONS: - -u value, --uuid value Access Token [$GORP_UUID] - -p value, --project value ReportPortal Project Name [$GORP_PROJECT] + --uuid value, -u value Access Token [$GORP_UUID] + --project value, -p value ReportPortal Project Name [$GORP_PROJECT] --host value ReportPortal Server Name - --help, -h show help - --version, -v print the version + --help, -h show help (default: false) + --version, -v print the version (default: false) ``` +### Init command + + NAME: + gorp init - Initializes configuration cache + USAGE: + gorp init [command options] [arguments...] + OPTIONS: + --help, -h show help (default: false) + ### Launch command + ``` USAGE: goRP launch command [command options] [arguments...] COMMANDS: - list List launches + list List launches + merge Merge Launches + help, h Shows a list of commands or help for one command ``` #### List Launches + ``` USAGE: goRP launch list [command options] [arguments...] OPTIONS: - --fn value, --filter-name value Filter Name [$FILTER_NAME] - -f value, --filter value Filter [$Filter] + --filter-name value, --fn value Filter Name [$FILTER_NAME] + --filter value, -f value Filter [$Filter] + --help, -h show help (default: false) +``` + +### Report command + + NAME: + goRP report - Reports input to report portal + USAGE: + goRP report command [command options] [arguments...] + COMMANDS: + test2json Input format: test2json + help, h Shows a list of commands or help for one command + OPTIONS: + --help, -h show help (default: false) + + +## Using as Golang Test Results Agent +Run tests with JSON output +``` +go test -json ./... > results.txt +``` +Report The results +``` +gorp report test2json -f results.txt +``` +Report directly from go test output ``` +go test -json ./... | gorp report test2json +``` \ No newline at end of file diff --git a/main.go b/cmd/gorp/main.go similarity index 84% rename from main.go rename to cmd/gorp/main.go index c065215..64518e9 100644 --- a/main.go +++ b/cmd/gorp/main.go @@ -6,8 +6,9 @@ import ( "os" "github.com/urfave/cli/v2" + "go.uber.org/zap" - rp "github.com/avarabyeu/goRP/v5/cli" + rp "github.com/reportportal/goRP/v5/internal/commands" ) var ( @@ -16,8 +17,11 @@ var ( ) func main() { - log.SetFlags(0) - log.SetOutput(os.Stdout) + logger, _ := zap.NewProduction() + zap.ReplaceGlobals(logger) + defer func() { + _ = logger.Sync() + }() app := cli.NewApp() app.Name = "goRP" diff --git a/go.mod b/go.mod index dec4bf3..9c14cdd 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,25 @@ -module github.com/avarabyeu/goRP/v5 +module github.com/reportportal/goRP/v5 -go 1.13 +go 1.18 require ( - github.com/go-resty/resty/v2 v2.7.0 - github.com/google/uuid v1.3.0 + github.com/go-resty/resty/v2 v2.11.0 + github.com/google/uuid v1.5.0 github.com/manifoldco/promptui v0.9.0 - github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.7.0 - github.com/urfave/cli/v2 v2.3.0 + github.com/stretchr/testify v1.8.4 + github.com/urfave/cli/v2 v2.27.1 + go.uber.org/zap v1.26.0 +) + +require ( + github.com/chzyer/readline v1.5.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4d3bcea..e7c2f17 100644 --- a/go.sum +++ b/go.sum @@ -1,47 +1,103 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= +github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= +github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= 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/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= +github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= +github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.3.0/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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -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= diff --git a/cli/commands.go b/internal/commands/commands.go similarity index 89% rename from cli/commands.go rename to internal/commands/commands.go index cbe1995..e8c13e3 100644 --- a/cli/commands.go +++ b/internal/commands/commands.go @@ -1,4 +1,4 @@ -package cli +package commands import ( "encoding/json" @@ -7,16 +7,16 @@ import ( "os" "github.com/manifoldco/promptui" - "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" + "go.uber.org/zap" - "github.com/avarabyeu/goRP/v5/gorp" + "github.com/reportportal/goRP/v5/pkg/gorp" ) type config struct { - UUID string - Project string - Host string + UUID string `json:"uuid"` + Project string `json:"project"` + Host string `json:"host"` } var ( @@ -48,13 +48,15 @@ func initConfiguration(c *cli.Context) error { return nil } } + + //nolint:nosnakecase // sdk uppercase constants f, err := os.OpenFile(getConfigFile(), os.O_CREATE|os.O_WRONLY, 0o600) if err != nil { return cli.Exit(fmt.Sprintf("Cannot open config file, %s", err), 1) } defer func() { if closeErr := f.Close(); closeErr != nil { - logrus.Error(closeErr) + zap.S().Error(closeErr) } }() @@ -96,6 +98,7 @@ func initConfiguration(c *cli.Context) error { return cli.Exit(fmt.Sprintf("Cannot read config file. %s", err), 1) } + //nolint:forbidigo //expected output fmt.Println("Configuration has been successfully saved!") return nil diff --git a/cli/launch.go b/internal/commands/launch.go similarity index 87% rename from cli/launch.go rename to internal/commands/launch.go index 1bf86da..adab109 100644 --- a/cli/launch.go +++ b/internal/commands/launch.go @@ -1,4 +1,4 @@ -package cli +package commands import ( "errors" @@ -7,7 +7,7 @@ import ( "github.com/urfave/cli/v2" - "github.com/avarabyeu/goRP/v5/gorp" + gorp2 "github.com/reportportal/goRP/v5/pkg/gorp" ) var ( @@ -88,15 +88,17 @@ func mergeLaunches(c *cli.Context) error { if err != nil { return err } - rq := &gorp.MergeLaunchesRQ{ + rq := &gorp2.MergeLaunchesRQ{ Name: c.String("name"), - MergeType: gorp.MergeType(c.String("type")), + MergeType: gorp2.MergeType(c.String("type")), Launches: ids, } launchResource, err := rpClient.MergeLaunches(rq) if err != nil { return fmt.Errorf("unable to merge launches: %w", err) } + + //nolint:forbidigo //expected output fmt.Println(launchResource.ID) return nil @@ -108,7 +110,7 @@ func listLaunches(c *cli.Context) error { return err } - var launches *gorp.LaunchPage + var launches *gorp2.LaunchPage if filters := c.StringSlice("filter"); len(filters) > 0 { filter := strings.Join(filters, "&") @@ -122,6 +124,7 @@ func listLaunches(c *cli.Context) error { return err } + //nolint:forbidigo //expected output for _, launch := range launches.Content { fmt.Printf("%d #%d \"%s\"\n", launch.ID, launch.Number, launch.Name) } @@ -129,12 +132,12 @@ func listLaunches(c *cli.Context) error { return nil } -func getMergeIDs(c *cli.Context, rpClient *gorp.Client) ([]int, error) { +func getMergeIDs(c *cli.Context, rpClient *gorp2.Client) ([]int, error) { if ids := c.IntSlice("ids"); len(ids) > 0 { return ids, nil } - var launches *gorp.LaunchPage + var launches *gorp2.LaunchPage var err error filter := c.String("filter") @@ -148,7 +151,7 @@ func getMergeIDs(c *cli.Context, rpClient *gorp.Client) ([]int, error) { return nil, errors.New("no either IDs or filter provided") } if err != nil { - return nil, fmt.Errorf("unable to find launches by filter: %s", err.Error()) + return nil, fmt.Errorf("unable to find launches by filter: %w", err) } ids := make([]int, len(launches.Content)) diff --git a/cli/report.go b/internal/commands/report.go similarity index 51% rename from cli/report.go rename to internal/commands/report.go index 4044d9a..93fd558 100644 --- a/cli/report.go +++ b/internal/commands/report.go @@ -1,4 +1,4 @@ -package cli +package commands import ( "bufio" @@ -11,10 +11,10 @@ import ( "sync" "time" - "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" + "go.uber.org/zap" - "github.com/avarabyeu/goRP/gorp" + gorp2 "github.com/reportportal/goRP/v5/pkg/gorp" ) const logsBatchSize = 10 @@ -43,6 +43,11 @@ var ( EnvVars: []string{"LAUNCH_NAME"}, Value: "gorp launch", }, + &cli.StringSliceFlag{ + Name: "attr", + Aliases: []string{"a"}, + Usage: "Launch attribute with format 'key:value'. Omitting a ':' separator will tag the launch with the value.", + }, }, Action: reportTest2json, } @@ -57,7 +62,8 @@ func reportTest2json(c *cli.Context) error { // run in separate goroutine launchNameArg := c.String("launchName") - rep := newReporter(rpClient, launchNameArg, input) + attrArgs := c.StringSlice("attr") + rep := newReporter(rpClient, launchNameArg, input, attrArgs...) wg := &sync.WaitGroup{} wg.Add(1) @@ -77,7 +83,7 @@ func reportTest2json(c *cli.Context) error { } defer func() { if cErr := f.Close(); cErr != nil { - logrus.Error(cErr) + zap.S().Error(cErr) } }() reader = f @@ -91,7 +97,7 @@ func reportTest2json(c *cli.Context) error { var ev testEvent if err := json.Unmarshal([]byte(data), &ev); err != nil { - logrus.Error(err) + zap.S().Error(err) return err } input <- &ev @@ -100,46 +106,66 @@ func reportTest2json(c *cli.Context) error { } type testEvent struct { - Time time.Time // encodes as an RFC3339-format string - Action string - Package string - Test string - Elapsed float64 // seconds - Output string + Time time.Time `json:"time"` // encodes as an RFC3339-format string + Action string `json:"action"` + Package string `json:"package"` + Test string `json:"test"` + Elapsed float64 `json:"elapsed"` // seconds + Output string `json:"output"` } type reporter struct { - input <-chan *testEvent - client *gorp.Client - launchName string - launchID string - launchOnce sync.Once - tests map[string]string - suites map[string]string - logs []*gorp.SaveLogRQ - logsBatchSize int - waitQueue sync.WaitGroup + input <-chan *testEvent + client *gorp2.Client + launchName string + launchID string + launchOnce sync.Once + launchAttributes []*gorp2.Attribute + tests map[string]string + suites map[string]string + logs []*gorp2.SaveLogRQ + logsBatchSize int + waitQueue sync.WaitGroup } -func newReporter(client *gorp.Client, launchName string, input <-chan *testEvent) *reporter { +func newReporter(client *gorp2.Client, launchName string, input <-chan *testEvent, launchAttrArgs ...string) *reporter { + launchAttributes := make([]*gorp2.Attribute, 0, len(launchAttrArgs)) + for _, attr := range launchAttrArgs { + // Separate the key:value pair. If `:` is not present, the entire string is considered the value and an empty key is used + var p gorp2.Parameter + if key, value, ok := strings.Cut(attr, ":"); ok { + p.Key = key + p.Value = value + } else { + p.Value = attr + } + launchAttributes = append(launchAttributes, &gorp2.Attribute{ + Parameter: p, + System: false, + }) + } + return &reporter{ - input: input, - launchName: launchName, - client: client, - launchOnce: sync.Once{}, - tests: map[string]string{}, - suites: map[string]string{}, - logs: []*gorp.SaveLogRQ{}, - logsBatchSize: logsBatchSize, + input: input, + launchName: launchName, + launchAttributes: launchAttributes, + client: client, + launchOnce: sync.Once{}, + tests: map[string]string{}, + suites: map[string]string{}, + logs: []*gorp2.SaveLogRQ{}, + logsBatchSize: logsBatchSize, } } func (r *reporter) receive() { + prevEventTime := time.Now() for ev := range r.input { var err error + startTime := ev.Time r.launchOnce.Do(func() { - if err = r.startLaunch(); err != nil { - logrus.Error(err) + if err = r.startLaunch(startTime); err != nil { + zap.S().Error(err) } }) @@ -149,13 +175,14 @@ func (r *reporter) receive() { case "output": r.log(ev) case "pass": - err = r.finish(ev, gorp.Statuses.Passed) + err = r.finish(ev, gorp2.Statuses.Passed) case "fail": - err = r.finish(ev, gorp.Statuses.Failed) + err = r.finish(ev, gorp2.Statuses.Failed) } if err != nil { - logrus.Fatal(err) + zap.S().Fatal(err) } + prevEventTime = ev.Time } // make sure we flush all logs that are left r.flushLogs(true) @@ -163,21 +190,21 @@ func (r *reporter) receive() { r.waitQueue.Wait() if r.launchID != "" { - if err := r.finishLaunch(gorp.Statuses.Passed); err != nil { - logrus.Fatal(err) + if err := r.finishLaunch(gorp2.Statuses.Passed, prevEventTime); err != nil { + zap.S().Fatal(err) } } } func (r *reporter) startSuite(ev *testEvent) (string, error) { - rs, err := r.client.StartTest(&gorp.StartTestRQ{ - StartRQ: gorp.StartRQ{ + rs, err := r.client.StartTest(&gorp2.StartTestRQ{ + StartRQ: gorp2.StartRQ{ Name: ev.Package, - StartTime: gorp.NewTimestamp(time.Now()), + StartTime: gorp2.NewTimestamp(ev.Time), }, LaunchID: r.launchID, HasStats: false, - Type: gorp.TestItemTypes.Suite, + Type: gorp2.TestItemTypes.Suite, Retry: false, }) if err != nil { @@ -197,17 +224,17 @@ func (r *reporter) startTest(ev *testEvent) (string, error) { return "", err } } - rs, err := r.client.StartChildTest(parentID, &gorp.StartTestRQ{ - StartRQ: gorp.StartRQ{ + rs, err := r.client.StartChildTest(parentID, &gorp2.StartTestRQ{ + StartRQ: gorp2.StartRQ{ Name: ev.Test, - StartTime: gorp.NewTimestamp(time.Now()), + StartTime: gorp2.NewTimestamp(ev.Time), }, LaunchID: r.launchID, HasStats: true, UniqueID: testID, CodeRef: testID, TestCaseID: testID, - Type: gorp.TestItemTypes.Test, + Type: gorp2.TestItemTypes.Test, Retry: false, }) if err != nil { @@ -228,15 +255,15 @@ func (r *reporter) log(ev *testEvent) { if strings.HasPrefix(strings.TrimLeft(ev.Output, " "), "\t") && len(r.logs) > 0 { lastLog := r.logs[len(r.logs)-1] lastLog.Message = lastLog.Message + "\n" + ev.Output - lastLog.Level = gorp.LogLevelError + lastLog.Level = gorp2.LogLevelError return } - rq := &gorp.SaveLogRQ{ + rq := &gorp2.SaveLogRQ{ ItemID: testID, LaunchUUID: r.launchID, - Level: gorp.LogLevelInfo, - LogTime: gorp.NewTimestamp(time.Now()), + Level: gorp2.LogLevelInfo, + LogTime: gorp2.NewTimestamp(ev.Time), Message: ev.Output, } r.logs = append(r.logs, rq) @@ -247,14 +274,14 @@ func (r *reporter) flushLogs(force bool) { if force || (len(r.logs) >= r.logsBatchSize) { batch := r.logs r.waitQueue.Add(1) - go func(logs []*gorp.SaveLogRQ) { + go func(logs []*gorp2.SaveLogRQ) { defer r.waitQueue.Done() if _, err := r.client.SaveLogs(logs...); err != nil { - logrus.Errorf("unable to report logs: %v. Batch len: %d", err, len(logs)) + zap.S().Errorf("unable to report logs: %v. Batch len: %d", err, len(logs)) } }(batch) - r.logs = []*gorp.SaveLogRQ{} + r.logs = []*gorp2.SaveLogRQ{} } } @@ -262,14 +289,15 @@ func (r *reporter) getTestName(ev *testEvent) string { return fmt.Sprintf("%s/%s", ev.Package, ev.Test) } -func (r *reporter) startLaunch() error { - var launch *gorp.EntryCreatedRS - launch, err := r.client.StartLaunch(&gorp.StartLaunchRQ{ - StartRQ: gorp.StartRQ{ - Name: r.launchName, - StartTime: gorp.NewTimestamp(time.Now()), +func (r *reporter) startLaunch(startTime time.Time) error { + var launch *gorp2.EntryCreatedRS + launch, err := r.client.StartLaunch(&gorp2.StartLaunchRQ{ + StartRQ: gorp2.StartRQ{ + Name: r.launchName, + StartTime: gorp2.NewTimestamp(startTime), + Attributes: r.launchAttributes, }, - Mode: gorp.LaunchModes.Default, + Mode: gorp2.LaunchModes.Default, }) if err != nil { return err @@ -278,21 +306,21 @@ func (r *reporter) startLaunch() error { return err } -func (r *reporter) finishLaunch(status gorp.Status) error { - _, err := r.client.FinishLaunch(r.launchID, &gorp.FinishExecutionRQ{ +func (r *reporter) finishLaunch(status gorp2.Status, endTime time.Time) error { + _, err := r.client.FinishLaunch(r.launchID, &gorp2.FinishExecutionRQ{ Status: status, - EndTime: gorp.NewTimestamp(time.Now()), + EndTime: gorp2.NewTimestamp(endTime), }) return err } -func (r *reporter) finishTest(ev *testEvent, status gorp.Status) error { +func (r *reporter) finishTest(ev *testEvent, status gorp2.Status) error { testName := r.getTestName(ev) testID := r.tests[testName] - _, err := r.client.FinishTest(testID, &gorp.FinishTestRQ{ - FinishExecutionRQ: gorp.FinishExecutionRQ{ - EndTime: gorp.NewTimestamp(time.Now()), + _, err := r.client.FinishTest(testID, &gorp2.FinishTestRQ{ + FinishExecutionRQ: gorp2.FinishExecutionRQ{ + EndTime: gorp2.NewTimestamp(ev.Time), Status: status, }, LaunchUUID: r.launchID, @@ -300,7 +328,7 @@ func (r *reporter) finishTest(ev *testEvent, status gorp.Status) error { return err } -func (r *reporter) finish(ev *testEvent, status gorp.Status) error { +func (r *reporter) finish(ev *testEvent, status gorp2.Status) error { var err error if ev.Test == "" { err = r.finishSuite(ev, status) @@ -310,12 +338,12 @@ func (r *reporter) finish(ev *testEvent, status gorp.Status) error { return err } -func (r *reporter) finishSuite(ev *testEvent, status gorp.Status) error { +func (r *reporter) finishSuite(ev *testEvent, status gorp2.Status) error { suiteID := r.suites[ev.Package] - _, err := r.client.FinishTest(suiteID, &gorp.FinishTestRQ{ - FinishExecutionRQ: gorp.FinishExecutionRQ{ - EndTime: gorp.NewTimestamp(time.Now()), + _, err := r.client.FinishTest(suiteID, &gorp2.FinishTestRQ{ + FinishExecutionRQ: gorp2.FinishExecutionRQ{ + EndTime: gorp2.NewTimestamp(ev.Time), Status: status, }, LaunchUUID: r.launchID, diff --git a/cli/util.go b/internal/commands/util.go similarity index 98% rename from cli/util.go rename to internal/commands/util.go index 89624af..ef880af 100644 --- a/cli/util.go +++ b/internal/commands/util.go @@ -1,4 +1,4 @@ -package cli +package commands import ( "errors" diff --git a/cli/util_test.go b/internal/commands/util_test.go similarity index 84% rename from cli/util_test.go rename to internal/commands/util_test.go index 0d75286..7c42c8d 100644 --- a/cli/util_test.go +++ b/internal/commands/util_test.go @@ -1,4 +1,4 @@ -package cli +package commands import ( "testing" @@ -7,17 +7,21 @@ import ( ) func TestUnderstandsYes(t *testing.T) { + t.Parallel() assert.Equal(t, true, answerYes("yes")) } func TestUnderstandsYesUpper(t *testing.T) { + t.Parallel() assert.Equal(t, true, answerYes("YES")) } func TestEmptyAnswer(t *testing.T) { + t.Parallel() assert.Equal(t, false, answerYes("")) } func TestUnderstandsNo(t *testing.T) { + t.Parallel() assert.Equal(t, false, answerYes("no")) } diff --git a/gorp/client.go b/pkg/gorp/client.go similarity index 89% rename from gorp/client.go rename to pkg/gorp/client.go index db94afa..0b15fc1 100644 --- a/gorp/client.go +++ b/pkg/gorp/client.go @@ -4,7 +4,9 @@ import ( "bytes" "encoding/json" "fmt" + "mime" "os" + "path/filepath" "time" "github.com/go-resty/resty/v2" @@ -26,7 +28,7 @@ func NewClient(host, project, uuid string) *Client { SetBaseURL(host). SetAuthToken(uuid). OnAfterResponse(func(client *resty.Client, rs *resty.Response) error { - // nolint:gomnd // 4xx errors + //nolint:gomnd // 4xx errors if (rs.StatusCode() / 100) >= 4 { return fmt.Errorf("status code error: %d\n%s", rs.StatusCode(), rs.String()) } @@ -194,7 +196,29 @@ func (c *Client) SaveLogs(logs ...*SaveLogRQ) (*EntryCreatedRS, error) { return c.SaveLogMultipart(logs, nil) } -// SaveLogMultipart attaches log in RP +// SaveLogMultipart saves a batch of logs in RP, along with any associated files (if any). +// +// Example usage: +// +// f, _ := os.Open("someFile.txt") +// +// logs := []*SaveLogRQ{{ +// File: FileAttachment{ +// // note that this value must present in 'files' map as key (see below) +// Name: "fileAttachment.txt", +// }, +// LaunchUUID: launchID, +// ItemID: itemID, +// Level: gorp.LogLevelError, +// LogTime: NewTimestamp(time.Now()), +// Message: "Important message!", +// }} +// +// files := map[string]*os.File{ +// "fileAttachment.txt": f, // key must match the FileAttachment.Name field +// } +// +// resp, err := client.SaveLogMultipart(log, files) func (c *Client) SaveLogMultipart(log []*SaveLogRQ, files map[string]*os.File) (*EntryCreatedRS, error) { var bodyBuf bytes.Buffer err := json.NewEncoder(&bodyBuf).Encode(log) @@ -218,7 +242,11 @@ func (c *Client) SaveLogMultipart(log []*SaveLogRQ, files map[string]*os.File) ( if _, sErr := os.Stat(v.Name()); os.IsNotExist(sErr) { return nil, fmt.Errorf("file %s does not exist", v.Name()) } - rq.SetMultipartField(k, k, "", v) + mimeType := mime.TypeByExtension(filepath.Ext(k)) + if mimeType == "" { + mimeType = "application/octet-stream" + } + rq.SetMultipartField("file", k, mimeType, v) } var rs EntryCreatedRS diff --git a/gorp/client_test.go b/pkg/gorp/client_test.go similarity index 95% rename from gorp/client_test.go rename to pkg/gorp/client_test.go index 1bd6a17..ba6c31b 100644 --- a/gorp/client_test.go +++ b/pkg/gorp/client_test.go @@ -9,6 +9,7 @@ import ( ) func TestCreateRPClient(t *testing.T) { + t.Parallel() client := NewClient("http://host.com", "prj", "uuid") assert.Equal(t, "prj", client.project) @@ -17,6 +18,7 @@ func TestCreateRPClient(t *testing.T) { } func TestHandleErrors(t *testing.T) { + t.Parallel() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) })) diff --git a/gorp/example_test.go b/pkg/gorp/example_test.go similarity index 69% rename from gorp/example_test.go rename to pkg/gorp/example_test.go index 6b50818..993201e 100644 --- a/gorp/example_test.go +++ b/pkg/gorp/example_test.go @@ -3,13 +3,15 @@ package gorp import ( "log" "os" + "path/filepath" "time" "github.com/google/uuid" ) func ExampleClient() { - client := NewClient("xxx", "xxx", "xxx") + client := NewClient("", + "", "") launchUUID := uuid.New() launch, err := client.StartLaunch(&StartLaunchRQ{ @@ -47,13 +49,31 @@ func ExampleClient() { }) checkErr(err, "unable to save log") - file, _ := os.Open("../go.mod") - _, err = client.SaveLogMultipart([]*SaveLogRQ{{ - LaunchUUID: launchUUID.String(), - ItemID: testUUID.String(), - Level: LogLevelInfo, - Message: "Log with binary", - }}, map[string]*os.File{"go.mod": file}) + file1, _ := os.Open("../go.mod") + file2, _ := os.Open("../go.sum") + _, err = client.SaveLogMultipart([]*SaveLogRQ{ + { + LaunchUUID: launchUUID.String(), + ItemID: testUUID.String(), + Level: LogLevelInfo, + Message: "Log with binary one", + Attachment: FileAttachment{ + Name: "go.mod", + }, + }, + { + LaunchUUID: launchUUID.String(), + ItemID: testUUID.String(), + Level: LogLevelInfo, + Message: "Log with binary two", + Attachment: FileAttachment{ + Name: "go.sum", + }, + }, + }, map[string]*os.File{ + filepath.Base(file1.Name()): file1, + filepath.Base(file2.Name()): file2, + }) checkErr(err, "unable to save log multipart") diff --git a/gorp/model_api.go b/pkg/gorp/model_api.go similarity index 98% rename from gorp/model_api.go rename to pkg/gorp/model_api.go index 62004e4..267c7ff 100644 --- a/gorp/model_api.go +++ b/pkg/gorp/model_api.go @@ -37,7 +37,7 @@ type ( ApproximateDuration float32 `json:"approximateDuration,omitempty"` HasRetries bool `json:"hasRetries,omitempty"` Statistics *Statistics `json:"statistics,omitempty"` - Analyzers []string `json:"analysing,omitempty"` // nolint:misspell // defined as described on server end + Analyzers []string `json:"analysing,omitempty"` //nolint:misspell // defined as described on server end } // FilterResource - GET Filter response model diff --git a/gorp/model_api_test.go b/pkg/gorp/model_api_test.go similarity index 96% rename from gorp/model_api_test.go rename to pkg/gorp/model_api_test.go index 51f8520..d569b6b 100644 --- a/gorp/model_api_test.go +++ b/pkg/gorp/model_api_test.go @@ -9,6 +9,7 @@ import ( ) func TestUnixTimeDeserialization(t *testing.T) { + t.Parallel() const jsonStr = `"1512114178671"` const expTime = "2017-12-01T07:42:59+00:00" @@ -25,6 +26,7 @@ func TestUnixTimeDeserialization(t *testing.T) { } func TestUnixTimeSerialization(t *testing.T) { + t.Parallel() const jsonStr = `1512114179000` const expTime = "2017-12-01T07:42:59+00:00" @@ -35,6 +37,7 @@ func TestUnixTimeSerialization(t *testing.T) { } func TestErrOnIncorrectTime(t *testing.T) { + t.Parallel() const jsonStr = `"hello-world"` var unitTime Timestamp @@ -43,11 +46,13 @@ func TestErrOnIncorrectTime(t *testing.T) { } func TestDirectionConverter(t *testing.T) { + t.Parallel() assert.Equal(t, "ASC", directionToStr(true)) assert.Equal(t, "DESC", directionToStr(false)) } func TestFiltersConverter(t *testing.T) { + t.Parallel() fp := ConvertToFilterParams(&FilterResource{ Entities: []*FilterEntity{ { diff --git a/gorp/model_enums.go b/pkg/gorp/model_enums.go similarity index 96% rename from gorp/model_enums.go rename to pkg/gorp/model_enums.go index 45c31fa..480570c 100644 --- a/gorp/model_enums.go +++ b/pkg/gorp/model_enums.go @@ -70,7 +70,7 @@ var Statuses = statusValuesType{ Stopped: "STOPPED", Skipped: "SKIPPED", Interrupted: "INTERRUPTED", - Canceled: "CANCELLED", // nolint:misspell // defined as described on server end + Canceled: "CANCELLED", //nolint:misspell // defined as described on server end Info: "INFO", Warn: "WARN", } diff --git a/gorp/model_reporting.go b/pkg/gorp/model_reporting.go similarity index 87% rename from gorp/model_reporting.go rename to pkg/gorp/model_reporting.go index 71c1453..990f07c 100644 --- a/gorp/model_reporting.go +++ b/pkg/gorp/model_reporting.go @@ -26,13 +26,14 @@ type ( RetryOf string `json:"retryOf,omitempty"` } - // SaveLogRQ payload representation. Without attaches. + // SaveLogRQ payload representation. SaveLogRQ struct { - LaunchUUID string `json:"launchUuid,omitempty"` - ItemID string `json:"itemUuid,omitempty"` - LogTime Timestamp `json:"time,omitempty"` - Message string `json:"message,omitempty"` - Level string `json:"level,omitempty"` + LaunchUUID string `json:"launchUuid,omitempty"` + ItemID string `json:"itemUuid,omitempty"` + LogTime Timestamp `json:"time,omitempty"` + Message string `json:"message,omitempty"` + Level string `json:"level,omitempty"` + Attachment FileAttachment `json:"file,omitempty"` } // StartTestRQ payload representation @@ -102,6 +103,11 @@ type ( Timestamp struct { time.Time } + + // FileAttachment represents file attachment in log entries + FileAttachment struct { + Name string `json:"name,omitempty"` + } ) // UnmarshalJSON converts Epoch milliseconds (timestamp) to appropriate object diff --git a/gorp/util.go b/pkg/gorp/util.go similarity index 100% rename from gorp/util.go rename to pkg/gorp/util.go diff --git a/util/util.go b/util/util.go deleted file mode 100644 index 3ab4ad1..0000000 --- a/util/util.go +++ /dev/null @@ -1,26 +0,0 @@ -package util - -import ( - "fmt" - "time" - - log "github.com/sirupsen/logrus" -) - -// Retry executes callback func until it executes successfully -func Retry(attempts int, timeout time.Duration, callback func() (interface{}, error)) (interface{}, error) { - var err error - for i := 0; i < attempts; i++ { - var res interface{} - res, err = callback() - if err == nil { - return res, nil - } - log.Warnf("Retry failed with the following error: %v", err) - - <-time.After(timeout) - log.Infof("Retrying... Attempt: %d. Left: %d", i+1, attempts-1-i) - } - - return nil, fmt.Errorf("after %d attempts, last error: %s", attempts, err) -}