Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Use golangci-lint for linting #1323

Merged
merged 2 commits into from
Aug 18, 2023
Merged

RFC: Use golangci-lint for linting #1323

merged 2 commits into from
Aug 18, 2023

Conversation

abhinav
Copy link
Collaborator

@abhinav abhinav commented Aug 16, 2023

This is more of a proposal than just a PR.

Zap has been maintaining its own bespoke linter setup for a while
and it's starting to show its age.
For example, the upgrade in #1289 can't go through
without either making a change which we disagree with,
or configuring revive separately to ignore that linter check.

This PR switches to using golangci-lint to run linters.
staticcheck is included and enabled by default.
For revive, this adds the relevant opt-outs with adequate justification.

It also enables a couple govet linter checks
that would otherwise be difficult to enable.

The result also simplifies our Makefile greatly
with the minor added complexity that it assumes golangci-lint
is already installed.

Another advantage of this setup is that lint runs in a separate parallel CI job
while the tests are doing their thing.

@codecov
Copy link

codecov bot commented Aug 16, 2023

Codecov Report

Merging #1323 (e642692) into master (613271a) will increase coverage by 0.14%.
Report is 1 commits behind head on master.
The diff coverage is 100.00%.

❗ Current head e642692 differs from pull request most recent head 5cbd053. Consider uploading reports for the commit 5cbd053 to get more accurate results

@@            Coverage Diff             @@
##           master    #1323      +/-   ##
==========================================
+ Coverage   97.62%   97.77%   +0.14%     
==========================================
  Files          51       51              
  Lines        3366     3366              
==========================================
+ Hits         3286     3291       +5     
+ Misses         69       65       -4     
+ Partials       11       10       -1     
Files Changed Coverage Δ
logger.go 96.55% <100.00%> (ø)
writer.go 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

with:
go-version: ${{ matrix.go }}
cache: true
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the default

@mway
Copy link
Member

mway commented Aug 17, 2023

I'm on board, I think using golangci-lint is a better idea than rolling more custom lint execution. I can't think of any downsides.

I think the more interesting bit is which linters to enable. At a minimum, I'd use gofumpt instead of gofmt, and add staticcheck and unused. I'd personally prefer to err on the side of more aggressive/pedantic linting for a library like Zap (potentially even using vet's fieldalignment pass).

I think we should also not disable errcheck but instead fix the lint warnings pre-emptively.

Thoughts?

@abhinav
Copy link
Collaborator Author

abhinav commented Aug 17, 2023

We're mostly in agreement on this:

  • staticcheck is enabled by default by golangci-lint.
  • unused is deprecated. Staticcheck finds some unused cases.
  • I'm in favor of gofumpt but as a separate commit/PR
  • fixing errcheck would make this PR too large. That's gotta be its own thing. I wanted this to be an incremental net positive without being too large.
  • for anything more aggressive or pedantic, I'd prefer to have a per linter discussion. I don't like littering code with nolint directives. Anything more aggressive must have a very high confidence on not producing false positives. Our code review practices for this codebase are rigorous enough that the value of some of the pedantic linters is diminished.

@mway
Copy link
Member

mway commented Aug 17, 2023

Agreed in general.

staticcheck is enabled by default by golangci-lint

I thought that it did not use defaults if you explicitly enumerated linters, but maybe that's historical behavior or maybe I'm conflating with something else. Either way, we should definitely enable all of them.

fixing errcheck would make this PR too large

Yeah, not saying it should be done in this PR - I meant that ideally we (1) run with the intended config before landing the change, (2) fix/PR lint issues, then (3) merge the linter change. I don't think it's a huge deal, I just think that it adds more impetus to address errcheck failures than disabling the linter does. I also haven't run it on the repo yet so I'm not sure how widespread of a problem it is (not advocating for blocking this on a massive/annoying undertaking).

I don't like littering code with nolint directives.

Me either! But I don't think we'd enable linters that we didn't intend to follow (and in general, I would imagine the unavoidable cases where we need nolint would be pretty isolated).

Copy link
Member

@mway mway left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

@abhinav
Copy link
Collaborator Author

abhinav commented Aug 17, 2023

I thought that it did not use defaults if you explicitly enumerated linters

No, the defaults are enabled by default. 'enabled' and 'disabled' are extras on top of that.
There's a separate configuration option to disable the default linters ('disable-all', IIRC).

I also haven't run it on the repo yet so I'm not sure how widespread of a problem it is

It's a bit:

buffer/buffer_test.go:49:37: Error return value of `buf.Write` is not checked (errcheck)
buffer/buffer_test.go:51:39: Error return value of `buf.WriteByte` is not checked (errcheck)
buffer/buffer_test.go:52:43: Error return value of `buf.WriteString` is not checked (errcheck)
encoder_test.go:44:18: Error return value is not checked (errcheck)
encoder_test.go:55:18: Error return value is not checked (errcheck)
error.go:64:19: Error return value of `arr.AppendObject` is not checked (errcheck)
example_test.go:38:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:95:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:149:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:155:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:190:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:206:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:216:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:228:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:250:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:279:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:291:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:309:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:334:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:352:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:364:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:377:19: Error return value of `logger.Sync` is not checked (errcheck)
example_test.go:393:19: Error return value of `logger.Sync` is not checked (errcheck)
http_handler.go:83:13: Error return value of `enc.Encode` is not checked (errcheck)
http_handler.go:88:14: Error return value of `enc.Encode` is not checked (errcheck)
http_handler.go:92:13: Error return value of `enc.Encode` is not checked (errcheck)
http_handler.go:95:13: Error return value of `enc.Encode` is not checked (errcheck)
http_handler_test.go:170:24: Error return value of `res.Body.Close` is not checked (errcheck)
logger.go:373:24: Error return value of `log.errorOutput.Sync` is not checked (errcheck)
sink.go:69:17: Error return value of `sr.RegisterSink` is not checked (errcheck)
stacktrace_ext_test.go:178:13: Error return value of `os.MkdirAll` is not checked (errcheck)
writer.go:65:11: Error return value of `c.Close` is not checked (errcheck)
writer_test.go:94:11: Error return value of `os.Remove` is not checked (errcheck)
writer_test.go:258:9: Error return value of `w.Write` is not checked (errcheck)
zapcore/buffered_write_syncer_bench_test.go:43:15: Error return value of `w.Stop` is not checked (errcheck)
zapcore/buffered_write_syncer_bench_test.go:47:12: Error return value of `w.Write` is not checked (errcheck)
zapcore/buffered_write_syncer_test.go:104:11: Error return value of `ws.Write` is not checked (errcheck)
zapcore/console_encoder.go:149:12: Error return value of `line.Write` is not checked (errcheck)
zapcore/core.go:107:9: Error return value of `c.Sync` is not checked (errcheck)
zapcore/core_test.go:151:13: Error return value of `core.Write` is not checked (errcheck)
zapcore/encoder_test.go:288:17: Error return value of `enc.AddArray` is not checked (errcheck)
zapcore/encoder_test.go:316:17: Error return value of `enc.AddArray` is not checked (errcheck)
zapcore/encoder_test.go:723:14: Error return value of `mem.AddArray` is not checked (errcheck)
zapcore/entry.go:245:23: Error return value of `ce.ErrorOutput.Sync` is not checked (errcheck)
zapcore/entry.go:257:22: Error return value of `ce.ErrorOutput.Sync` is not checked (errcheck)
zapcore/error.go:101:19: Error return value of `arr.AppendObject` is not checked (errcheck)
zapcore/error_test.go:45:17: Error return value of `io.WriteString` is not checked (errcheck)
zapcore/json_encoder.go:348:17: Error return value of `clone.buf.Write` is not checked (errcheck)
zapcore/json_encoder.go:418:18: Error return value of `final.buf.Write` is not checked (errcheck)
zapcore/json_encoder.go:516:16: Error return value of `enc.buf.Write` is not checked (errcheck)
zapcore/json_encoder_bench_test.go:34:16: Error return value of `enc.AddObject` is not checked (errcheck)
zapcore/json_encoder_bench_test.go:98:16: Error return value of `json.Marshal` is not checked (errcheck)
zapcore/json_encoder_impl_test.go:252:16: Error return value of `e.AddObject` is not checked (errcheck)
zapcore/json_encoder_impl_test.go:260:16: Error return value of `e.AddObject` is not checked (errcheck)
zapcore/json_encoder_impl_test.go:268:16: Error return value of `e.AddObject` is not checked (errcheck)
zapcore/json_encoder_impl_test.go:292:13: Error return value of `e.AddArray` is not checked (errcheck)
zapcore/json_encoder_impl_test.go:423:21: Error return value of `arr.AppendObject` is not checked (errcheck)
zapcore/json_encoder_impl_test.go:431:21: Error return value of `arr.AppendObject` is not checked (errcheck)
zapcore/json_encoder_impl_test.go:533:20: Error return value of `arr.AppendObject` is not checked (errcheck)
zapcore/level_test.go:162:16: Error return value of `l.MarshalText` is not checked (errcheck)
zapcore/memory_encoder_test.go:221:16: Error return value of `e.AddObject` is not checked (errcheck)
zapcore/memory_encoder_test.go:235:16: Error return value of `e.AddObject` is not checked (errcheck)
zapcore/memory_encoder_test.go:288:54: Error return value of `e.AppendReflected` is not checked (errcheck)
zapcore/memory_encoder_test.go:294:18: Error return value of `e.AppendArray` is not checked (errcheck)
zapcore/memory_encoder_test.go:305:18: Error return value of `e.AppendArray` is not checked (errcheck)
zapcore/memory_encoder_test.go:306:24: Error return value of `inner.AppendObject` is not checked (errcheck)
zapcore/memory_encoder_test.go:321:18: Error return value of `e.AppendArray` is not checked (errcheck)
zapcore/memory_encoder_test.go:322:24: Error return value of `inner.AppendObject` is not checked (errcheck)
zapcore/tee_test.go:123:13: Error return value of `tee.Write` is not checked (errcheck)
zapcore/write_syncer_bench_test.go:40:12: Error return value of `w.Write` is not checked (errcheck)
zapcore/write_syncer_bench_test.go:54:12: Error return value of `w.Write` is not checked (errcheck)
zapcore/write_syncer_bench_test.go:67:15: Error return value of `w.Stop` is not checked (errcheck)
zapcore/write_syncer_bench_test.go:71:12: Error return value of `w.Write` is not checked (errcheck)
zapcore/write_syncer_bench_test.go:86:12: Error return value of `w.Write` is not checked (errcheck)
zapcore/write_syncer_test.go:73:9: Error return value of `ws.Sync` is not checked (errcheck)
zapio/example_test.go:35:16: Error return value of `io.WriteString` is not checked (errcheck)
zapio/example_test.go:36:16: Error return value of `io.WriteString` is not checked (errcheck)
zapio/example_test.go:37:16: Error return value of `io.WriteString` is not checked (errcheck)
zapio/writer_test.go:170:16: Error return value of `io.WriteString` is not checked (errcheck)
zapio/writer_test.go:171:16: Error return value of `io.WriteString` is not checked (errcheck)
zapio/writer_test.go:226:18: Error return value of `writer.Write` is not checked (errcheck)
zaptest/observer/observer_test.go:176:15: Error return value of `logger.Write` is not checked (errcheck)

I would've fixed them in the same PR with a separate commit if it was a small handful.

@mway
Copy link
Member

mway commented Aug 17, 2023

Yeah, that's too many. Mostly tests though, which at least is better than the other way around. :)

@abhinav
Copy link
Collaborator Author

abhinav commented Aug 17, 2023

Will await another maintainer's opinion before merging.

CC @sywhang @prashantv

Copy link
Contributor

@sywhang sywhang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. looks like most issues are errcheck in tests (which is good).

Copy link
Collaborator

@prashantv prashantv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not the biggest fan of golangci-lint, but it's also become the standard so I think it makes sense to use. That said, the default linters can change with updates, and can introduce either linters with false positives, or remove a linter previously being relied on for correctness, so in recent usages, we've been very specific about which linters we want enabled rather than relying on the default.

@abhinav
Copy link
Collaborator Author

abhinav commented Aug 18, 2023

I'm not the biggest fan of golangci-lint, but it's also become the standard so I think it makes sense to use. That said, the default linters can change with updates, and can introduce either linters with false positives, or remove a linter previously being relied on for correctness, so in recent usages, we've been very specific about which linters we want enabled rather than relying on the default.

Fair. Yeah, the revive unused-param check is one such example.
I'll disable all default linters and explicitly add the default set we care about.

Use golangci-lint instead of bespoke linter setup.
Adds exclusions based on current practices.
Fixes the following issues reported by revive:

```
logger.go:291:2: redefines-builtin-id: redefinition of the built-in function copy (revive)
sink_test.go:71:2: redefines-builtin-id: redefinition of the built-in function close (revive)
writer.go:51:2: redefines-builtin-id: redefinition of the built-in function close (revive)
writer.go:63:2: redefines-builtin-id: redefinition of the built-in function close (revive)
```
@abhinav abhinav merged commit c94c2bb into master Aug 18, 2023
9 checks passed
@abhinav abhinav deleted the golangci-lint branch August 18, 2023 18:29
abhinav added a commit to abhinav/sally that referenced this pull request Oct 15, 2023
Instead of hand-managing and running linters, use golangci-lint.

Along with the golangci-lint defaults,
enable a couple other linters we generally agree with.
See also uber-go/zap#1323 for a similar change.

As a result of this, we can:

- Drop the dependabot for tools
- Run the lint job in parallel with build/test
- Simplify the Makefile
abhinav added a commit to abhinav/sally that referenced this pull request Oct 15, 2023
Instead of hand-managing and running linters, use golangci-lint.

Along with the golangci-lint defaults,
enable a couple other linters we generally agree with.
See also uber-go/zap#1323 for a similar change.

As a result of this, we can:

- Drop the dependabot for tools
- Run the lint job in parallel with build/test
- Simplify the Makefile
abhinav added a commit to abhinav/sally that referenced this pull request Oct 21, 2023
Instead of hand-managing and running linters, use golangci-lint.

Along with the golangci-lint defaults,
enable a couple other linters we generally agree with.
See also uber-go/zap#1323 for a similar change.

As a result of this, we can:

- Drop the dependabot for tools
- Run the lint job in parallel with build/test
- Simplify the Makefile
sywhang pushed a commit to uber-go/goleak that referenced this pull request Oct 21, 2023
Instead of hand-managing and running linters, use golangci-lint.
This simplifies the Makefile and allows lint to run in parallel
with the build/test job.

Along with the golangci-lint defaults,
enable a couple other linters we generally agree with.
See also uber-go/zap#1323 and uber-go/sally#121 for similar changes.
sywhang pushed a commit to uber-go/sally that referenced this pull request Oct 21, 2023
Instead of hand-managing and running linters, use golangci-lint.

Along with the golangci-lint defaults,
enable a couple other linters we generally agree with.
See also uber-go/zap#1323 for a similar change.

As a result of this, we can:

- Drop the dependabot for tools
- Run the lint job in parallel with build/test
- Simplify the Makefile
abhinav added a commit to abhinav/fx that referenced this pull request Feb 6, 2024
Instead of home-rolled linting workflows,
use golangci-lint to manage linting.

See also uber-go/zap#1323
abhinav added a commit to abhinav/dig that referenced this pull request Feb 11, 2024
This PR switches to using golangci-lint to run linters.
staticcheck is included and enabled by default.
We add revive and a few others.
All issues reported by the linters have been fixed.

This also allows the linter to run in parallel with the tests.

License header check has also been moved to golangci-lint,
similarly to uber-go/fx#1157.

Refs uber-go/fx#1150
Refs uber-go/zap#1323
JacobOaks pushed a commit to uber-go/dig that referenced this pull request Feb 13, 2024
This PR switches to using golangci-lint to run linters.
staticcheck is included and enabled by default.
We add revive and a few others.
All issues reported by the linters have been fixed.

This also allows the linter to run in parallel with the tests.

License header check has also been moved to golangci-lint,
similarly to uber-go/fx#1157.

Refs uber-go/fx#1150
Refs uber-go/zap#1323
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants