From da6e32f05d418a999e657049ae852d4faba7c6ed Mon Sep 17 00:00:00 2001 From: Brian Zimmer Date: Thu, 14 Oct 2021 19:06:07 -0700 Subject: [PATCH] addressed linting issues (#5) --- .github/workflows/build.yaml | 12 +-- .github/workflows/golangci-lint.yml | 31 ++++++ .golangci.yaml | 119 ++++++++++++++++++++++ Taskfile.yml | 6 ++ cyclinganalytics/cyclinganalytics.go | 14 +-- cyclinganalytics/cyclinganalytics_with.go | 4 +- cyclinganalytics/rides.go | 57 +++++------ rwgps/rwgps_test.go | 17 ++-- strava/activity.go | 39 ++++--- strava/activity_test.go | 31 +----- strava/strava.go | 14 +-- strava/strava_with.go | 4 +- strava/testdata/example.gpx | 26 +++++ zwift/auth.go | 3 +- zwift/zwift.go | 8 +- 15 files changed, 272 insertions(+), 113 deletions(-) create mode 100644 .github/workflows/golangci-lint.yml create mode 100644 .golangci.yaml create mode 100644 strava/testdata/example.gpx diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c270826..b290947 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -5,7 +5,7 @@ jobs: strategy: matrix: go-version: [1.16.x, 1.17.x] - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: - name: Install Go @@ -14,16 +14,14 @@ jobs: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v2 + - name: Install Task + uses: arduino/setup-task@v1 - name: Test - run: go test ./... - if: matrix.os == 'macos-latest' || matrix.os == 'windows-latest' - - name: Test Coverage - run: go test -race -coverprofile=coverage.out -covermode=atomic ./... - if: matrix.os == 'ubuntu-latest' + run: task test - name: Upload coverage to Codecov uses: codecov/codecov-action@v2 with: token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.out + files: ./dist/coverage.txt verbose: true if: matrix.os == 'ubuntu-latest' diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..f99035a --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,31 @@ +name: golangci-lint +on: [push, pull_request] +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: latest + + # Optional: working directory, useful for monorepos + # working-directory: somedir + + # Optional: golangci-lint command line arguments. + args: -v + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true then the action will use pre-installed Go. + # skip-go-installation: true + + # Optional: if set to true then the action don't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. + # skip-build-cache: true diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..2dae0b2 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,119 @@ +linters-settings: + dupl: + threshold: 100 + funlen: + lines: 100 + statements: 50 + gci: + local-prefixes: github.com/golangci/golangci-lint + goconst: + min-len: 2 + min-occurrences: 2 + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + disabled-checks: + - dupImport # https://github.com/go-critic/go-critic/issues/845 + - ifElseChain + - octalLiteral + - whyNoLint + - wrapperFunc + gocyclo: + min-complexity: 15 + goimports: + local-prefixes: github.com/golangci/golangci-lint + golint: + min-confidence: 0 + gomnd: + settings: + mnd: + # don't include the "operation" and "assign" + checks: argument,case,condition,return + govet: + check-shadowing: true + lll: + line-length: 140 + maligned: + suggest-new: true + misspell: + locale: US + nolintlint: + allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space) + allow-unused: false # report any unused nolint directives + require-explanation: false # don't require an explanation for nolint directives + require-specific: false # don't require nolint directives to be specific about which linter is being skipped + +linters: + # please, do not use `enable-all`: it's deprecated and will be removed soon. + # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint + disable: + - gomnd + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - dupl + - errcheck + - exhaustive + - exportloopref + - forbidigo + - funlen + - gochecknoglobals + - gochecknoinits + - goconst + # - gocritic + - gocyclo + - gofmt + - goimports + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - lll + - misspell + - nakedret + - noctx + - nolintlint + - revive + - rowserrcheck + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck + - whitespace + +# issues: +# # Excluding configuration per-path, per-linter, per-text and per-source +# exclude-rules: +# - path: _test\.go +# linters: +# - gomnd + +# # https://github.com/go-critic/go-critic/issues/926 +# - linters: +# - gocritic +# text: "unnecessaryDefer:" + +# run: +# skip-dirs: +# - test/testdata_etc +# - internal/cache +# - internal/renameio +# - internal/robustio + +# # golangci.com configuration +# # https://github.com/golangci/golangci/wiki/Configuration +# service: +# golangci-lint-version: 1.23.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" \ No newline at end of file diff --git a/Taskfile.yml b/Taskfile.yml index a20fafd..8bd6e96 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -39,3 +39,9 @@ tasks: desc: Run code generation cmds: - go generate ./... + + lint: + desc: Runs the linter + cmds: + - go vet ./... + - golangci-lint -v run diff --git a/cyclinganalytics/cyclinganalytics.go b/cyclinganalytics/cyclinganalytics.go index 0d68134..ee7990c 100644 --- a/cyclinganalytics/cyclinganalytics.go +++ b/cyclinganalytics/cyclinganalytics.go @@ -1,6 +1,6 @@ package cyclinganalytics -//go:generate genwith --do --client --endpoint --config --token --ratelimit --package cyclinganalytics +//go:generate genwith --do --client --endpoint-func --config --token --ratelimit --package cyclinganalytics import ( "context" @@ -16,7 +16,7 @@ import ( const baseURL = "https://www.cyclinganalytics.com/api" -// Client . +// Client for accessing Cycling Analytics' API type Client struct { config oauth2.Config token *oauth2.Token @@ -27,10 +27,12 @@ type Client struct { } // Endpoint is CyclingAnalytics's OAuth 2.0 endpoint -var Endpoint = oauth2.Endpoint{ - AuthURL: "https://www.cyclinganalytics.com/api/auth", - TokenURL: "https://www.cyclinganalytics.com/api/token", - AuthStyle: oauth2.AuthStyleAutoDetect, +func Endpoint() oauth2.Endpoint { + return oauth2.Endpoint{ + AuthURL: "https://www.cyclinganalytics.com/api/auth", + TokenURL: "https://www.cyclinganalytics.com/api/token", + AuthStyle: oauth2.AuthStyleAutoDetect, + } } func withServices() Option { diff --git a/cyclinganalytics/cyclinganalytics_with.go b/cyclinganalytics/cyclinganalytics_with.go index adb86c6..b8ac66e 100644 --- a/cyclinganalytics/cyclinganalytics_with.go +++ b/cyclinganalytics/cyclinganalytics_with.go @@ -1,4 +1,4 @@ -// Code generated by "genwith --do --client --endpoint --config --token --ratelimit --package cyclinganalytics"; DO NOT EDIT. +// Code generated by "genwith --do --client --endpoint-func --config --token --ratelimit --package cyclinganalytics"; DO NOT EDIT. package cyclinganalytics @@ -28,7 +28,7 @@ func NewClient(opts ...Option) (*Client, error) { client: &http.Client{}, token: &oauth2.Token{}, config: oauth2.Config{ - Endpoint: Endpoint, + Endpoint: Endpoint(), }, } opts = append(opts, withServices()) diff --git a/cyclinganalytics/rides.go b/cyclinganalytics/rides.go index e3d077f..097490f 100644 --- a/cyclinganalytics/rides.go +++ b/cyclinganalytics/rides.go @@ -166,18 +166,15 @@ func (s *RidesService) StatusWithUser(ctx context.Context, userID UserID, upload return res, nil } -// AvailableStreams returns the list of valid stream names +// StreamSets returns the list of valid stream names func (s *RidesService) StreamSets() map[string]string { - q := make(map[string]string) - for k, v := range streamsets { - q[k] = v - } - return q + return streamsets() } func validateStreams(streams []string) error { + x := streamsets() for i := range streams { - _, ok := streamsets[streams[i]] + _, ok := x[streams[i]] if !ok { return fmt.Errorf("invalid stream '%s'", streams[i]) } @@ -186,26 +183,28 @@ func validateStreams(streams []string) error { } // https://www.cyclinganalytics.com/developer/api#/ride/ride_id -var streamsets = map[string]string{ - "cadence": "", - "distance": "The sequence of distance values for this stream, in kilometers [float]", - "elevation": "The sequence of elevation values for this stream, in meters [float]", - "gears": "", - "gradient": "The sequence of grade values for this stream, as percents of a grade [float]", - "heart_rate_variability": "", - "heartrate": "The sequence of heart rate values for this stream, in beats per minute [integer]", - "latitude": "", - "longitude": "", - "lrbalance": "", - "pedal_smoothness": "", - "platform_center_offset": "", - "power_direction": "", - "power_phase": "", - "power": "", - "respiration_rate": "", - "smo2": "", - "speed": "The sequence of speed values for this stream, in meters per second [float]", - "temperature": "The sequence of temperature values for this stream, in celsius degrees [float]", - "thb": "", - "torque_effectiveness": "", +func streamsets() map[string]string { + return map[string]string{ + "cadence": "", + "distance": "The sequence of distance values for this stream, in kilometers [float]", + "elevation": "The sequence of elevation values for this stream, in meters [float]", + "gears": "", + "gradient": "The sequence of grade values for this stream, as percents of a grade [float]", + "heart_rate_variability": "", + "heartrate": "The sequence of heart rate values for this stream, in beats per minute [integer]", + "latitude": "", + "longitude": "", + "lrbalance": "", + "pedal_smoothness": "", + "platform_center_offset": "", + "power_direction": "", + "power_phase": "", + "power": "", + "respiration_rate": "", + "smo2": "", + "speed": "The sequence of speed values for this stream, in meters per second [float]", + "temperature": "The sequence of temperature values for this stream, in celsius degrees [float]", + "thb": "", + "torque_effectiveness": "", + } } diff --git a/rwgps/rwgps_test.go b/rwgps/rwgps_test.go index ad15c5c..4527887 100644 --- a/rwgps/rwgps_test.go +++ b/rwgps/rwgps_test.go @@ -10,14 +10,6 @@ import ( "github.com/bzimmer/httpwares" ) -var ( - tests = map[string]string{ - "apikey": "fooKey", - "version": "2", - "auth_token": "barToken", - } -) - func newClient(status int, filename string) (*rwgps.Client, error) { return rwgps.NewClient( rwgps.WithTransport(&httpwares.TestDataTransport{ @@ -27,12 +19,15 @@ func newClient(status int, filename string) (*rwgps.Client, error) { Requester: func(req *http.Request) error { var body map[string]interface{} decoder := json.NewDecoder(req.Body) - err := decoder.Decode(&body) - if err != nil { + if err := decoder.Decode(&body); err != nil { return err } // confirm the body has the expected key:value pairs - for key, value := range tests { + for key, value := range map[string]string{ + "apikey": "fooKey", + "version": "2", + "auth_token": "barToken", + } { v := body[key] if v != value { return fmt.Errorf("expected %s == '%v', not '%v'", key, value, v) diff --git a/strava/activity.go b/strava/activity.go index 433c430..54a6a81 100644 --- a/strava/activity.go +++ b/strava/activity.go @@ -109,7 +109,7 @@ func (s *ActivityService) Activity(ctx context.Context, activityID int64, stream return nil, err } act.Streams = sms - return act, err + return act, nil } // Activities returns a channel for activities and errors for an athlete @@ -208,32 +208,31 @@ func (s *ActivityService) Status(ctx context.Context, uploadID int64) (*Upload, } // https://developers.strava.com/docs/reference/#api-models-StreamSet -var streamsets = map[string]string{ - "altitude": "The sequence of altitude values for this stream, in meters [float]", - "cadence": "The sequence of cadence values for this stream, in rotations per minute [integer]", - "distance": "The sequence of distance values for this stream, in meters [float]", - "grade_smooth": "The sequence of grade values for this stream, as percents of a grade [float]", - "heartrate": "The sequence of heart rate values for this stream, in beats per minute [integer]", - "latlng": "The sequence of lat/long values for this stream [float, float]", - "moving": "The sequence of moving values for this stream, as boolean values [boolean]", - "temp": "The sequence of temperature values for this stream, in celsius degrees [float]", - "time": "The sequence of time values for this stream, in seconds [integer]", - "velocity_smooth": "The sequence of velocity values for this stream, in meters per second [float]", - "watts": "The sequence of power values for this stream, in watts [integer]", +func streamsets() map[string]string { + return map[string]string{ + "altitude": "The sequence of altitude values for this stream, in meters [float]", + "cadence": "The sequence of cadence values for this stream, in rotations per minute [integer]", + "distance": "The sequence of distance values for this stream, in meters [float]", + "grade_smooth": "The sequence of grade values for this stream, as percents of a grade [float]", + "heartrate": "The sequence of heart rate values for this stream, in beats per minute [integer]", + "latlng": "The sequence of lat/long values for this stream [float, float]", + "moving": "The sequence of moving values for this stream, as boolean values [boolean]", + "temp": "The sequence of temperature values for this stream, in celsius degrees [float]", + "time": "The sequence of time values for this stream, in seconds [integer]", + "velocity_smooth": "The sequence of velocity values for this stream, in meters per second [float]", + "watts": "The sequence of power values for this stream, in watts [integer]", + } } -// AvailableStreams returns the list of valid stream names +// StreamSets returns the list of valid stream names func (s *ActivityService) StreamSets() map[string]string { - q := make(map[string]string) - for k, v := range streamsets { - q[k] = v - } - return q + return streamsets() } func (s *ActivityService) validateStreams(streams []string) error { + x := streamsets() for i := range streams { - _, ok := streamsets[streams[i]] + _, ok := x[streams[i]] if !ok { return fmt.Errorf("invalid stream '%s'", streams[i]) } diff --git a/strava/activity_test.go b/strava/activity_test.go index 686503c..dbbaedf 100644 --- a/strava/activity_test.go +++ b/strava/activity_test.go @@ -309,32 +309,11 @@ func TestUpload(t *testing.T) { Name: "LongHike.gpx", Filename: "/tmp/LongHike.gpx", Format: activity.FormatGPX, - Reader: bytes.NewBufferString(` - - - - Garmin International - - - - - Example GPX Document - - - 4.46 - - - - 4.94 - - - - 6.87 - - - - -`), + Reader: func() io.Reader { + var buf bytes.Buffer + a.NoError(copyFile(&buf, "testdata/example.gpx")) + return &buf + }(), }, }, } diff --git a/strava/strava.go b/strava/strava.go index 60cb2b4..39ab20e 100644 --- a/strava/strava.go +++ b/strava/strava.go @@ -1,6 +1,6 @@ package strava -//go:generate genwith --do --client --endpoint --config --token --ratelimit --package strava +//go:generate genwith --do --client --endpoint-func --config --token --ratelimit --package strava import ( "bytes" @@ -23,13 +23,15 @@ const ( ) // Endpoint is Strava's OAuth 2.0 endpoint -var Endpoint = oauth2.Endpoint{ - AuthURL: "https://www.strava.com/oauth/authorize", - TokenURL: "https://www.strava.com/oauth/token", - AuthStyle: oauth2.AuthStyleAutoDetect, +func Endpoint() oauth2.Endpoint { + return oauth2.Endpoint{ + AuthURL: "https://www.strava.com/oauth/authorize", + TokenURL: "https://www.strava.com/oauth/token", + AuthStyle: oauth2.AuthStyleAutoDetect, + } } -// Client for communicating with Strava +// Client for accessing Strava's API type Client struct { client *http.Client token *oauth2.Token diff --git a/strava/strava_with.go b/strava/strava_with.go index 5d7fda2..7e448e8 100644 --- a/strava/strava_with.go +++ b/strava/strava_with.go @@ -1,4 +1,4 @@ -// Code generated by "genwith --do --client --endpoint --config --token --ratelimit --package strava"; DO NOT EDIT. +// Code generated by "genwith --do --client --endpoint-func --config --token --ratelimit --package strava"; DO NOT EDIT. package strava @@ -28,7 +28,7 @@ func NewClient(opts ...Option) (*Client, error) { client: &http.Client{}, token: &oauth2.Token{}, config: oauth2.Config{ - Endpoint: Endpoint, + Endpoint: Endpoint(), }, } opts = append(opts, withServices()) diff --git a/strava/testdata/example.gpx b/strava/testdata/example.gpx new file mode 100644 index 0000000..25ea2b0 --- /dev/null +++ b/strava/testdata/example.gpx @@ -0,0 +1,26 @@ + + + + + Garmin International + + + + + Example GPX Document + + + 4.46 + + + + 4.94 + + + + 6.87 + + + + + diff --git a/zwift/auth.go b/zwift/auth.go index ed833ad..cd1abf4 100644 --- a/zwift/auth.go +++ b/zwift/auth.go @@ -26,8 +26,9 @@ func (s *AuthService) Refresh(ctx context.Context, username, password string) (* "password": {password}, "client_id": {"Zwift_Mobile_Link"}, } + endpoint := Endpoint() body := strings.NewReader(values.Encode()) - req, err := http.NewRequestWithContext(ctx, http.MethodPost, Endpoint.TokenURL, body) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.TokenURL, body) if err != nil { return nil, err } diff --git a/zwift/zwift.go b/zwift/zwift.go index 608cda9..eb53f3b 100644 --- a/zwift/zwift.go +++ b/zwift/zwift.go @@ -17,9 +17,11 @@ const baseURL = "https://us-or-rly101.zwift.com" const userAgent = "CNL/3.4.1 (Darwin Kernel 20.3.0) zwift/1.0.61590 curl/7.64.1" // Endpoint is Zwifts's OAuth 2.0 endpoint -var Endpoint = oauth2.Endpoint{ - TokenURL: "https://secure.zwift.com/auth/realms/zwift/tokens/access/codes", - AuthStyle: oauth2.AuthStyleAutoDetect, +func Endpoint() oauth2.Endpoint { + return oauth2.Endpoint{ + TokenURL: "https://secure.zwift.com/auth/realms/zwift/tokens/access/codes", + AuthStyle: oauth2.AuthStyleAutoDetect, + } } // Client for communicating with Zwift