docs: use information from the previous release to create pages (#4457)

This commit is contained in:
Ludovic Fernandez 2024-03-11 20:24:12 +01:00 committed by GitHub
parent 5cb16561d5
commit 85e1dee09a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 5496 additions and 747 deletions

View File

@ -1,4 +1,4 @@
name: "Publish release documentation"
name: "Post release"
on:
release:
types:
@ -44,7 +44,7 @@ jobs:
delete-branch: true
update-assets:
name: "Update GitHub Action assets"
name: "Update assets"
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GOLANGCI_LINT_TOKEN }}
@ -62,12 +62,18 @@ jobs:
- name: Update GitHub action config
run: make assets/github-action-config.json
- name: Update reference files
run: cp .golangci.next.reference.yml .golangci.reference.yml
- name: Update information
run: make website_dump_info
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
with:
base: master
token: ${{ secrets.GOLANGCI_LINT_TOKEN }}
branch-suffix: timestamp
title: "docs: update GitHub Action assets"
title: "docs: update assets"
team-reviewers: golangci/team
delete-branch: true

2904
.golangci.next.reference.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,7 @@
# Options for analysis running.
run:
# Number of operating system threads (`GOMAXPROCS`) that can execute golangci-lint simultaneously.
# If it is explicitly set to 0 (i.e. not the default) then golangci-lint will automatically set the value to match Linux container CPU quota.
# Number of CPUs to use when running golangci-lint.
# Default: the number of logical CPUs in the machine
concurrency: 4
@ -81,6 +80,10 @@ run:
# Default: use Go version from the go.mod file, fallback on the env var `GOVERSION`, fallback on 1.17
go: '1.19'
# Show statistics per linter.
# Default: false
show-stats: true
# output configuration options
output:
@ -110,32 +113,10 @@ output:
# Default: ""
path-prefix: ""
# Sort results by the order defined in `sort-order`.
# Sort results by: filepath, line and column.
# Default: false
sort-results: true
# Order to use when sorting results.
# Require `sort-results` to `true`.
# Possible values: `file`, `linter`, and `severity`.
#
# If the severity values are inside the following list, they are ordered in this order:
# 1. error
# 2. warning
# 3. high
# 4. medium
# 5. low
# Either they are sorted alphabetically.
#
# Default: ["file"]
sort-order:
- linter
- severity
- file # filepath, line, and column.
# Show statistics per linter.
# Default: false
show-stats: true
# All available settings of specific linters.
linters-settings:
@ -168,11 +149,6 @@ linters-settings:
first-strong-isolate: false
pop-directional-isolate: false
copyloopvar:
# If true, ignore aliasing of loop variables.
# Default: false
ignore-alias: true
cyclop:
# The maximal code complexity to report.
# Default: 10
@ -388,6 +364,16 @@ linters-settings:
# Default: false
default-case-required: true
exhaustivestruct:
# Struct Patterns is list of expressions to match struct packages and names.
# The struct packages have the form `example.com/package.ExampleStruct`.
# The matching patterns can use matching syntax from https://pkg.go.dev/path#Match.
# If this list is empty, all structs are tested.
# Default: []
struct-patterns:
- '*.Test'
- 'example.com/package.ExampleStruct'
exhaustruct:
# List of regular expressions to match struct packages and their names.
# Regular expressions must match complete canonical struct package/name/structname.
@ -555,9 +541,6 @@ linters-settings:
- ruleguard
- truncateCmp
# Enable all checks.
# Default: false
enable-all: true
# Which checks should be disabled; can't be combined with 'enabled-checks'.
# Default: []
disabled-checks:
@ -780,6 +763,11 @@ linters-settings:
# Default: ""
local-prefixes: github.com/org/project
golint:
# Minimal confidence for issues.
# Default: 0.8
min-confidence: 0.7
gomnd:
# List of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.
# Default: ["argument", "case", "condition", "operation", "return", "assign"]
@ -1119,7 +1107,7 @@ linters-settings:
# stdmethods, stringintconv, structtag, testinggoroutine, tests, timeformat, unmarshal, unreachable, unsafeptr,
# unusedresult
# ).
# Run `GL_DEBUG=govet golangci-lint run --enable=govet` to see default, all available analyzers, and enabled analyzers.
# Run `go tool vet help` to see all analyzers.
# Default: []
enable:
- appends
@ -1171,7 +1159,7 @@ linters-settings:
# atomicalign, deepequalerrors, fieldalignment, findcall, nilness, reflectvaluecompare, shadow, sortslice,
# timeformat, unusedwrite
# ).
# Run `GL_DEBUG=govet golangci-lint run --enable=govet` to see default, all available analyzers, and enabled analyzers.
# Run `go tool vet help` to see all analyzers.
# Default: []
disable:
- appends
@ -1244,6 +1232,15 @@ linters-settings:
# Default: false
var-require-grouping: true
ifshort:
# Maximum length of variable declaration measured in number of lines, after which linter won't suggest using short syntax.
# Has higher priority than max-decl-chars.
# Default: 1
max-decl-lines: 2
# Maximum length of variable declaration measured in number of characters, after which linter won't suggest using short syntax.
# Default: 30
max-decl-chars: 40
importas:
# Do not allow unaliased imports of aliased packages.
# Default: false
@ -1347,25 +1344,19 @@ linters-settings:
# Default: false
always: true
maligned:
# Print struct with more effective memory layout or not.
# Default: false
suggest-new: true
misspell:
# Correct spellings using locale preferences for US or UK.
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
# Default is to use a neutral variety of English.
locale: US
# Typos to ignore.
# Should be in lower case.
# Default: []
ignore-words:
- someword
# Extra word corrections.
# `typo` and `correction` should only contain letters.
# The words are case-insensitive.
# Default: []
extra-words:
- typo: "iff"
correction: "if"
- typo: "cancelation"
correction: "cancellation"
# Mode of the analysis:
# - default: checks all the file content.
# - restricted: checks only comments.
@ -1450,9 +1441,6 @@ linters-settings:
# Optimizes `fmt.Sprintf` with only one argument.
# Default: true
sprintf1: false
# Optimizes into strings concatenation.
# Default: true
strconcat: false
prealloc:
# IMPORTANT: we don't recommend using this linter before doing performance profiling.
@ -1549,7 +1537,6 @@ linters-settings:
- name: add-constant
severity: warning
disabled: false
exclude: [""]
arguments:
- maxLitCount: "3"
allowStrs: '""'
@ -1559,50 +1546,41 @@ linters-settings:
- name: argument-limit
severity: warning
disabled: false
exclude: [""]
arguments: [ 4 ]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#atomic
- name: atomic
severity: warning
exclude: [""]
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#banned-characters
- name: banned-characters
severity: warning
disabled: false
exclude: [""]
arguments: [ "Ω","Σ","σ", "7" ]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bare-return
- name: bare-return
severity: warning
exclude: [""]
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#blank-imports
- name: blank-imports
severity: warning
exclude: [""]
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr
- name: bool-literal-in-expr
severity: warning
exclude: [""]
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#call-to-gc
- name: call-to-gc
severity: warning
exclude: [""]
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#cognitive-complexity
- name: cognitive-complexity
severity: warning
disabled: false
exclude: [""]
arguments: [ 7 ]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#comment-spacings
- name: comment-spacings
severity: warning
disabled: false
exclude: [""]
arguments:
- mypragma
- otherpragma
@ -1610,125 +1588,103 @@ linters-settings:
- name: confusing-naming
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#confusing-results
- name: confusing-results
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#constant-logical-expr
- name: constant-logical-expr
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument
- name: context-as-argument
severity: warning
disabled: false
exclude: [""]
arguments:
- allowTypesBefore: "*testing.T,*github.com/user/repo/testing.Harness"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-keys-type
- name: context-keys-type
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#cyclomatic
- name: cyclomatic
severity: warning
disabled: false
exclude: [""]
arguments: [ 3 ]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#datarace
- name: datarace
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#deep-exit
- name: deep-exit
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#defer
- name: defer
severity: warning
disabled: false
exclude: [""]
arguments:
- [ "call-chain", "loop" ]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#dot-imports
- name: dot-imports
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#duplicated-imports
- name: duplicated-imports
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return
- name: early-return
severity: warning
disabled: false
exclude: [""]
arguments:
- "preserveScope"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-block
- name: empty-block
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines
- name: empty-lines
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#enforce-map-style
- name: enforce-map-style
severity: warning
disabled: false
exclude: [""]
arguments:
- "make"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#enforce-repeated-arg-type-style
- name: enforce-repeated-arg-type-style
severity: warning
disabled: false
exclude: [""]
arguments:
- "short"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#enforce-slice-style
- name: enforce-slice-style
severity: warning
disabled: false
exclude: [""]
arguments:
- "make"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-naming
- name: error-naming
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-return
- name: error-return
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-strings
- name: error-strings
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#errorf
- name: errorf
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#exported
- name: exported
severity: warning
disabled: false
exclude: [""]
arguments:
- "preserveScope"
- "checkPrivateReceivers"
@ -1737,65 +1693,54 @@ linters-settings:
- name: file-header
severity: warning
disabled: false
exclude: [""]
arguments:
- This is the text that must appear at the top of source files.
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#flag-parameter
- name: flag-parameter
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#function-result-limit
- name: function-result-limit
severity: warning
disabled: false
exclude: [""]
arguments: [ 2 ]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#function-length
- name: function-length
severity: warning
disabled: false
exclude: [""]
arguments: [ 10, 0 ]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#get-return
- name: get-return
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#identical-branches
- name: identical-branches
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return
- name: if-return
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#increment-decrement
- name: increment-decrement
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#indent-error-flow
- name: indent-error-flow
severity: warning
disabled: false
exclude: [""]
arguments:
- "preserveScope"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-alias-naming
- name: import-alias-naming
severity: warning
disabled: false
exclude: [""]
arguments:
- "^[a-z][a-z0-9]{0,}$"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#imports-blocklist
- name: imports-blocklist
severity: warning
disabled: false
exclude: [""]
arguments:
- "crypto/md5"
- "crypto/sha1"
@ -1803,90 +1748,73 @@ linters-settings:
- name: import-shadowing
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#line-length-limit
- name: line-length-limit
severity: warning
disabled: false
exclude: [""]
arguments: [ 80 ]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#max-control-nesting
- name: max-control-nesting
severity: warning
disabled: false
exclude: [""]
arguments: [ 3 ]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#max-public-structs
- name: max-public-structs
severity: warning
disabled: false
exclude: [""]
arguments: [ 3 ]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#modifies-parameter
- name: modifies-parameter
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#modifies-value-receiver
- name: modifies-value-receiver
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#nested-structs
- name: nested-structs
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#optimize-operands-order
- name: optimize-operands-order
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#package-comments
- name: package-comments
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range
- name: range
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-in-closure
- name: range-val-in-closure
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-address
- name: range-val-address
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#receiver-naming
- name: receiver-naming
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redundant-import-alias
- name: redundant-import-alias
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redefines-builtin-id
- name: redefines-builtin-id
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-of-int
- name: string-of-int
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-format
- name: string-format
severity: warning
disabled: false
exclude: [""]
arguments:
- - 'core.WriteError[1].Message'
- '/^([^A-Z]|$)/'
@ -1904,29 +1832,24 @@ linters-settings:
- "bson,outline,gnu"
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#superfluous-else
- name: superfluous-else
severity: warning
disabled: false
exclude: [""]
arguments:
- "preserveScope"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-equal
- name: time-equal
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-naming
- name: time-naming
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-naming
- name: var-naming
severity: warning
disabled: false
exclude: [""]
arguments:
- [ "ID" ] # AllowList
- [ "VM" ] # DenyList
@ -1935,27 +1858,22 @@ linters-settings:
- name: var-declaration
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unconditional-recursion
- name: unconditional-recursion
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-naming
- name: unexported-naming
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-return
- name: unexported-return
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unhandled-error
- name: unhandled-error
severity: warning
disabled: false
exclude: [""]
arguments:
- "fmt.Printf"
- "myFunction"
@ -1963,36 +1881,30 @@ linters-settings:
- name: unnecessary-stmt
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unreachable-code
- name: unreachable-code
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-parameter
- name: unused-parameter
severity: warning
disabled: false
exclude: [""]
arguments:
- allowRegex: "^_"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-receiver
- name: unused-receiver
severity: warning
disabled: false
exclude: [""]
arguments:
- allowRegex: "^_"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break
- name: useless-break
severity: warning
disabled: false
exclude: [""]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#waitgroup-by-value
- name: waitgroup-by-value
severity: warning
disabled: false
exclude: [""]
rowserrcheck:
# database/sql is always checked
@ -2186,10 +2098,6 @@ linters-settings:
- suite-thelper
- useless-assert
bool-compare:
# To ignore user defined types (over builtin bool).
# Default: false
ignore-custom-types: true
expected-actual:
# Regexp for expected variable name.
# Default: (^(exp(ected)?|want(ed)?)([A-Z]\w*)?$)|(^(\w*[a-z])?(Exp(ected)?|Want(ed)?)$)
@ -2293,14 +2201,6 @@ linters-settings:
# Default: false
syslog-priority: true
unconvert:
# Remove conversions that force intermediate rounding.
# Default: false
fast-math: true
# Be more conservative (experimental).
# Default: false
safe: true
unparam:
# Inspect exported functions.
#
@ -2335,6 +2235,11 @@ linters-settings:
# Default: true
generated-is-used: false
varcheck:
# Check usage of exported fields and variables.
# Default: false
exported-fields: true
varnamelen:
# The longest distance, in source lines, that is being considered a "small scope".
# Variables used in at most this many lines will be ignored.
@ -2480,10 +2385,6 @@ linters-settings:
custom:
# Each custom linter should have a unique name.
example:
# The plugin type.
# It can be `goplugin` or `module`.
# Default: goplugin
type: module
# The path to the plugin *.so. Can be absolute or local.
# Required for each custom linter.
path: /path/to/example.so
@ -2493,11 +2394,6 @@ linters-settings:
# Intended to point to the repo location of the linter.
# Optional.
original-url: github.com/golangci/example-linter
# Plugins settings/configuration.
# Only work with plugin based on `linterdb.PluginConstructor`.
# Optional.
settings:
foo: bar
linters:
@ -2513,8 +2409,8 @@ linters:
- bodyclose
- containedctx
- contextcheck
- copyloopvar
- cyclop
- deadcode
- decorder
- depguard
- dogsled
@ -2527,6 +2423,7 @@ linters:
- errorlint
- execinquery
- exhaustive
- exhaustivestruct
- exhaustruct
- exportloopref
- forbidigo
@ -2549,6 +2446,7 @@ linters:
- gofumpt
- goheader
- goimports
- golint
- gomnd
- gomoddirectives
- gomodguard
@ -2558,16 +2456,18 @@ linters:
- gosmopolitan
- govet
- grouper
- ifshort
- importas
- inamedparam
- ineffassign
- interfacebloat
- intrange
- interfacer
- ireturn
- lll
- loggercheck
- maintidx
- makezero
- maligned
- mirror
- misspell
- musttag
@ -2579,6 +2479,7 @@ linters:
- noctx
- nolintlint
- nonamedreturns
- nosnakecase
- nosprintfhostport
- paralleltest
- perfsprint
@ -2589,10 +2490,12 @@ linters:
- reassign
- revive
- rowserrcheck
- scopelint
- sloglint
- spancheck
- sqlclosecheck
- staticcheck
- structcheck
- stylecheck
- tagalign
- tagliatelle
@ -2607,6 +2510,7 @@ linters:
- unparam
- unused
- usestdlibvars
- varcheck
- varnamelen
- wastedassign
- whitespace
@ -2626,8 +2530,8 @@ linters:
- bodyclose
- containedctx
- contextcheck
- copyloopvar
- cyclop
- deadcode
- decorder
- depguard
- dogsled
@ -2640,6 +2544,7 @@ linters:
- errorlint
- execinquery
- exhaustive
- exhaustivestruct
- exhaustruct
- exportloopref
- forbidigo
@ -2662,6 +2567,7 @@ linters:
- gofumpt
- goheader
- goimports
- golint
- gomnd
- gomoddirectives
- gomodguard
@ -2671,16 +2577,18 @@ linters:
- gosmopolitan
- govet
- grouper
- ifshort
- importas
- inamedparam
- ineffassign
- interfacebloat
- intrange
- interfacer
- ireturn
- lll
- loggercheck
- maintidx
- makezero
- maligned
- mirror
- misspell
- musttag
@ -2692,6 +2600,7 @@ linters:
- noctx
- nolintlint
- nonamedreturns
- nosnakecase
- nosprintfhostport
- paralleltest
- perfsprint
@ -2702,10 +2611,12 @@ linters:
- reassign
- revive
- rowserrcheck
- scopelint
- sloglint
- spancheck
- sqlclosecheck
- staticcheck
- structcheck
- stylecheck
- tagalign
- tagliatelle
@ -2720,22 +2631,13 @@ linters:
- unparam
- unused
- usestdlibvars
- varcheck
- varnamelen
- wastedassign
- whitespace
- wrapcheck
- wsl
- zerologlint
- deadcode # Deprecated
- exhaustivestruct # Deprecated
- golint # Deprecated
- ifshort # Deprecated
- interfacer # Deprecated
- maligned # Deprecated
- nosnakecase # Deprecated
- scopelint # Deprecated
- structcheck # Deprecated
- varcheck # Deprecated
# Enable presets.
# https://golangci-lint.run/usage/linters
@ -2880,8 +2782,6 @@ severity:
# - GitHub: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message
# - TeamCity: https://www.jetbrains.com/help/teamcity/service-messages.html#Inspection+Instance
#
# `@linter` can be used as severity value to keep the severity from linters (e.g. revive, gosec, ...)
#
# Default: ""
default-severity: error
@ -2892,9 +2792,6 @@ severity:
# When a list of severity rules are provided, severity information will be added to lint issues.
# Severity rules have the same filtering capability as exclude rules
# except you are allowed to specify one matcher per severity rule.
#
# `@linter` can be used as severity value to keep the severity from linters (e.g. revive, gosec, ...)
#
# Only affects out formats that support setting severity information.
#
# Default: []

View File

@ -88,9 +88,13 @@ go.mod: FORCE
go mod verify
go.sum: go.mod
expand_website_templates:
go run ./scripts/expand_website_templates/main.go
.PHONY: expand_website_templates
website_expand_templates:
go run ./scripts/website/expand_templates/
.PHONY: website_expand_templates
website_dump_info:
go run ./scripts/website/dump_info/
.PHONY: dump_info
update_contributors_list:
cd .github/contributors && npm run all

5
assets/cli-help.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,92 @@
[
{
"id": "EXC0001",
"pattern": "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*print(f|ln)?|os\\.(Un)?Setenv). is not checked",
"linter": "errcheck",
"why": "Almost all programs ignore errors on these functions and in most cases it's ok"
},
{
"id": "EXC0002",
"pattern": "(comment on exported (method|function|type|const)|should have( a package)? comment|comment should be of the form)",
"linter": "golint",
"why": "Annoying issue about not having a comment. The rare codebase has such comments"
},
{
"id": "EXC0003",
"pattern": "func name will be used as test\\.Test.* by other packages, and that stutters; consider calling this",
"linter": "golint",
"why": "False positive when tests are defined in package 'test'"
},
{
"id": "EXC0004",
"pattern": "(possible misuse of unsafe.Pointer|should have signature)",
"linter": "govet",
"why": "Common false positives"
},
{
"id": "EXC0005",
"pattern": "ineffective break statement. Did you mean to break out of the outer loop",
"linter": "staticcheck",
"why": "Developers tend to write in C-style with an explicit 'break' in a 'switch', so it's ok to ignore"
},
{
"id": "EXC0006",
"pattern": "Use of unsafe calls should be audited",
"linter": "gosec",
"why": "Too many false-positives on 'unsafe' usage"
},
{
"id": "EXC0007",
"pattern": "Subprocess launch(ed with variable|ing should be audited)",
"linter": "gosec",
"why": "Too many false-positives for parametrized shell calls"
},
{
"id": "EXC0008",
"pattern": "(G104)",
"linter": "gosec",
"why": "Duplicated errcheck checks"
},
{
"id": "EXC0009",
"pattern": "(Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)",
"linter": "gosec",
"why": "Too many issues in popular repos"
},
{
"id": "EXC0010",
"pattern": "Potential file inclusion via variable",
"linter": "gosec",
"why": "False positive is triggered by 'src, err := ioutil.ReadFile(filename)'"
},
{
"id": "EXC0011",
"pattern": "(comment on exported (method|function|type|const)|should have( a package)? comment|comment should be of the form)",
"linter": "stylecheck",
"why": "Annoying issue about not having a comment. The rare codebase has such comments"
},
{
"id": "EXC0012",
"pattern": "exported (.+) should have comment( \\(or a comment on this block\\))? or be unexported",
"linter": "revive",
"why": "Annoying issue about not having a comment. The rare codebase has such comments"
},
{
"id": "EXC0013",
"pattern": "package comment should be of the form \"(.+)...",
"linter": "revive",
"why": "Annoying issue about not having a comment. The rare codebase has such comments"
},
{
"id": "EXC0014",
"pattern": "comment on exported (.+) should be of the form \"(.+)...\"",
"linter": "revive",
"why": "Annoying issue about not having a comment. The rare codebase has such comments"
},
{
"id": "EXC0015",
"pattern": "should have a package comment",
"linter": "revive",
"why": "Annoying issue about not having a comment. The rare codebase has such comments"
}
]

1600
assets/linters-info.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -51,7 +51,7 @@
"gatsby-plugin-netlify": "^5.1.0"
},
"scripts": {
"build": "make -C .. expand_website_templates && gatsby build",
"build": "make -C .. website_expand_templates && gatsby build",
"start": "gatsby develop",
"serve": "gatsby serve",
"clean": "gatsby clean"

View File

@ -30,8 +30,8 @@ allowing to use `React` components.
## Templating
We use templates like `{.SomeField}` inside our `mdx` files.
There templates are expanded by running `make expand_website_templates` in the root of the repository.
It runs script `scripts/expand_website_templates/main.go` that rewrites `mdx` files with replaced templates.
There templates are expanded by running `make website_expand_templates` in the root of the repository.
It runs script `scripts/website/expand_templates/` that rewrites `mdx` files with replaced templates.
## Hosting
@ -59,5 +59,5 @@ Also, there is no need to refresh a webpage: hot reload updates changed content
To do it run:
```sh
go run ./scripts/expand_website_templates/main.go
go run ./scripts/website/expand_templates/
```

View File

@ -1,572 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"
"reflect"
"sort"
"strings"
"unicode"
"unicode/utf8"
"golang.org/x/exp/maps"
"gopkg.in/yaml.v3"
"github.com/golangci/golangci-lint/internal/renameio"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/lint/lintersdb"
)
const listItemPrefix = "list-item-"
func main() {
replacements, err := buildTemplateContext()
if err != nil {
log.Fatalf("Failed to build template context: %s", err)
}
if err := rewriteDocs(replacements); err != nil {
log.Fatalf("Failed to rewrite docs: %s", err)
}
log.Print("Successfully expanded templates")
}
func rewriteDocs(replacements map[string]string) error {
madeReplacements := map[string]bool{}
err := filepath.Walk(filepath.Join("docs", "src", "docs"),
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
return processDoc(path, replacements, madeReplacements)
})
if err != nil {
return fmt.Errorf("failed to walk dir: %w", err)
}
if len(madeReplacements) != len(replacements) {
for key := range replacements {
if !madeReplacements[key] {
log.Printf("Replacement %q wasn't performed", key)
}
}
return fmt.Errorf("%d replacements weren't performed", len(replacements)-len(madeReplacements))
}
return nil
}
func processDoc(path string, replacements map[string]string, madeReplacements map[string]bool) error {
contentBytes, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read %s: %w", path, err)
}
content := string(contentBytes)
hasReplacements := false
for key, replacement := range replacements {
nextContent := content
nextContent = strings.ReplaceAll(nextContent, fmt.Sprintf("{.%s}", key), replacement)
// Yaml formatter in mdx code section makes extra spaces, need to match them too.
nextContent = strings.ReplaceAll(nextContent, fmt.Sprintf("{ .%s }", key), replacement)
if nextContent != content {
hasReplacements = true
madeReplacements[key] = true
content = nextContent
}
}
if !hasReplacements {
return nil
}
log.Printf("Expanded template in %s, saving it", path)
if err = renameio.WriteFile(path, []byte(content), os.ModePerm); err != nil {
return fmt.Errorf("failed to write changes to file %s: %w", path, err)
}
return nil
}
type latestRelease struct {
TagName string `json:"tag_name"`
}
func getLatestVersion() (string, error) {
req, err := http.NewRequest( //nolint:noctx
http.MethodGet,
"https://api.github.com/repos/golangci/golangci-lint/releases/latest",
http.NoBody,
)
if err != nil {
return "", fmt.Errorf("failed to prepare a http request: %w", err)
}
req.Header.Add("Accept", "application/vnd.github.v3+json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", fmt.Errorf("failed to get http response for the latest tag: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read a body for the latest tag: %w", err)
}
release := latestRelease{}
err = json.Unmarshal(body, &release)
if err != nil {
return "", fmt.Errorf("failed to unmarshal the body for the latest tag: %w", err)
}
return release.TagName, nil
}
func buildTemplateContext() (map[string]string, error) {
golangciYamlExample, err := os.ReadFile(".golangci.reference.yml")
if err != nil {
return nil, fmt.Errorf("can't read .golangci.reference.yml: %w", err)
}
snippets, err := extractExampleSnippets(golangciYamlExample)
if err != nil {
return nil, fmt.Errorf("can't read .golangci.reference.yml: %w", err)
}
if err = exec.Command("make", "build").Run(); err != nil {
return nil, fmt.Errorf("can't run go install: %w", err)
}
lintersOut, err := exec.Command("./golangci-lint", "help", "linters").Output()
if err != nil {
return nil, fmt.Errorf("can't run linters cmd: %w", err)
}
lintersOutParts := bytes.Split(lintersOut, []byte("\n\n"))
helpCmd := exec.Command("./golangci-lint", "run", "-h")
helpCmd.Env = append(helpCmd.Env, os.Environ()...)
helpCmd.Env = append(helpCmd.Env, "HELP_RUN=1") // make default concurrency stable: don't depend on machine CPU number
help, err := helpCmd.Output()
if err != nil {
return nil, fmt.Errorf("can't run help cmd: %w", err)
}
helpLines := bytes.Split(help, []byte("\n"))
shortHelp := bytes.Join(helpLines[2:], []byte("\n"))
changeLog, err := os.ReadFile("CHANGELOG.md")
if err != nil {
return nil, err
}
latestVersion, err := getLatestVersion()
if err != nil {
return nil, fmt.Errorf("failed to get the latest version: %w", err)
}
return map[string]string{
"LintersExample": snippets.LintersSettings,
"ConfigurationExample": snippets.ConfigurationFile,
"LintersCommandOutputEnabledOnly": string(lintersOutParts[0]),
"LintersCommandOutputDisabledOnly": string(lintersOutParts[1]),
"EnabledByDefaultLinters": getLintersListMarkdown(true),
"DisabledByDefaultLinters": getLintersListMarkdown(false),
"DefaultExclusions": getDefaultExclusions(),
"ThanksList": getThanksList(),
"RunHelpText": string(shortHelp),
"ChangeLog": string(changeLog),
"LatestVersion": latestVersion,
}, nil
}
func getDefaultExclusions() string {
bufferString := bytes.NewBufferString("")
for _, pattern := range config.DefaultExcludePatterns {
_, _ = fmt.Fprintln(bufferString)
_, _ = fmt.Fprintf(bufferString, "### %s\n", pattern.ID)
_, _ = fmt.Fprintln(bufferString)
_, _ = fmt.Fprintf(bufferString, "- linter: `%s`\n", pattern.Linter)
_, _ = fmt.Fprintf(bufferString, "- pattern: `%s`\n", strings.ReplaceAll(pattern.Pattern, "`", "`"))
_, _ = fmt.Fprintf(bufferString, "- why: %s\n", pattern.Why)
}
return bufferString.String()
}
func getLintersListMarkdown(enabled bool) string {
dbManager, _ := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder())
lcs := dbManager.GetAllSupportedLinterConfigs()
var neededLcs []*linter.Config
for _, lc := range lcs {
if lc.Internal {
continue
}
if lc.EnabledByDefault == enabled {
neededLcs = append(neededLcs, lc)
}
}
sort.Slice(neededLcs, func(i, j int) bool {
return neededLcs[i].Name() < neededLcs[j].Name()
})
lines := []string{
"|Name|Description|Presets|AutoFix|Since|",
"|---|---|---|---|---|---|",
}
for _, lc := range neededLcs {
line := fmt.Sprintf("|%s|%s|%s|%v|%s|",
getName(lc),
getDesc(lc),
strings.Join(lc.InPresets, ", "),
check(lc.CanAutoFix, "Auto fix supported"),
lc.Since,
)
lines = append(lines, line)
}
return strings.Join(lines, "\n")
}
func getName(lc *linter.Config) string {
name := lc.Name()
if lc.OriginalURL != "" {
name = fmt.Sprintf("[%s](%s)", name, lc.OriginalURL)
}
if hasSettings(lc.Name()) {
name = fmt.Sprintf("%s&nbsp;[%s](#%s)", name, spanWithID(listItemPrefix+lc.Name(), "Configuration", "⚙️"), lc.Name())
}
if !lc.IsDeprecated() {
return name
}
title := "deprecated"
if lc.Deprecation.Replacement != "" {
title += fmt.Sprintf(" since %s", lc.Deprecation.Since)
}
return name + "&nbsp;" + span(title, "⚠")
}
func getDesc(lc *linter.Config) string {
desc := lc.Linter.Desc()
if lc.IsDeprecated() {
desc = lc.Deprecation.Message
if lc.Deprecation.Replacement != "" {
desc += fmt.Sprintf(" Replaced by %s.", lc.Deprecation.Replacement)
}
}
return formatDesc(desc)
}
func formatDesc(desc string) string {
runes := []rune(desc)
r, _ := utf8.DecodeRuneInString(desc)
runes[0] = unicode.ToUpper(r)
if runes[len(runes)-1] != '.' {
runes = append(runes, '.')
}
return strings.ReplaceAll(string(runes), "\n", "<br/>")
}
func check(b bool, title string) string {
if b {
return span(title, "✔")
}
return ""
}
func hasSettings(name string) bool {
tp := reflect.TypeOf(config.LintersSettings{})
for i := 0; i < tp.NumField(); i++ {
if strings.EqualFold(name, tp.Field(i).Name) {
return true
}
}
return false
}
func span(title, icon string) string {
return fmt.Sprintf(`<span title=%q>%s</span>`, title, icon)
}
func spanWithID(id, title, icon string) string {
return fmt.Sprintf(`<span id=%q title=%q>%s</span>`, id, title, icon)
}
type authorDetails struct {
Linters []string
Profile string
Avatar string
}
func getThanksList() string {
addedAuthors := map[string]*authorDetails{}
dbManager, _ := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder())
for _, lc := range dbManager.GetAllSupportedLinterConfigs() {
if lc.Internal {
continue
}
if lc.OriginalURL == "" {
continue
}
linterURL := lc.OriginalURL
if lc.Name() == "staticcheck" {
linterURL = "https://github.com/dominikh/go-tools"
}
if author := extractAuthor(linterURL, "https://github.com/"); author != "" && author != "golangci" {
if _, ok := addedAuthors[author]; ok {
addedAuthors[author].Linters = append(addedAuthors[author].Linters, lc.Name())
} else {
addedAuthors[author] = &authorDetails{
Linters: []string{lc.Name()},
Profile: fmt.Sprintf("[%[1]s](https://github.com/sponsors/%[1]s)", author),
Avatar: fmt.Sprintf(`<img src="https://github.com/%[1]s.png" alt="%[1]s" style="max-width: 100%%;" width="20px;" />`, author),
}
}
} else if author := extractAuthor(linterURL, "https://gitlab.com/"); author != "" {
if _, ok := addedAuthors[author]; ok {
addedAuthors[author].Linters = append(addedAuthors[author].Linters, lc.Name())
} else {
addedAuthors[author] = &authorDetails{
Linters: []string{lc.Name()},
Profile: fmt.Sprintf("[%[1]s](https://gitlab.com/%[1]s)", author),
}
}
} else {
continue
}
}
authors := maps.Keys(addedAuthors)
sort.Slice(authors, func(i, j int) bool {
return strings.ToLower(authors[i]) < strings.ToLower(authors[j])
})
lines := []string{
"|Author|Linter(s)|",
"|---|---|",
}
for _, author := range authors {
lines = append(lines, fmt.Sprintf("|%s %s|%s|",
addedAuthors[author].Avatar, addedAuthors[author].Profile, strings.Join(addedAuthors[author].Linters, ", ")))
}
return strings.Join(lines, "\n")
}
func extractAuthor(originalURL, prefix string) string {
if !strings.HasPrefix(originalURL, prefix) {
return ""
}
return strings.SplitN(strings.TrimPrefix(originalURL, prefix), "/", 2)[0]
}
type SettingSnippets struct {
ConfigurationFile string
LintersSettings string
}
func extractExampleSnippets(example []byte) (*SettingSnippets, error) {
var data yaml.Node
err := yaml.Unmarshal(example, &data)
if err != nil {
return nil, err
}
root := data.Content[0]
globalNode := &yaml.Node{
Kind: root.Kind,
Style: root.Style,
Tag: root.Tag,
Value: root.Value,
Anchor: root.Anchor,
Alias: root.Alias,
HeadComment: root.HeadComment,
LineComment: root.LineComment,
FootComment: root.FootComment,
Line: root.Line,
Column: root.Column,
}
snippets := SettingSnippets{}
builder := strings.Builder{}
for j, node := range root.Content {
switch node.Value {
case "run", "output", "linters", "linters-settings", "issues", "severity":
default:
continue
}
nextNode := root.Content[j+1]
newNode := &yaml.Node{
Kind: nextNode.Kind,
Content: []*yaml.Node{
{
HeadComment: fmt.Sprintf("See the dedicated %q documentation section.", node.Value),
Kind: node.Kind,
Style: node.Style,
Tag: node.Tag,
Value: "option",
},
{
Kind: node.Kind,
Style: node.Style,
Tag: node.Tag,
Value: "value",
},
},
}
globalNode.Content = append(globalNode.Content, node, newNode)
if node.Value == "linters-settings" {
snippets.LintersSettings, err = getLintersSettingSections(node, nextNode)
if err != nil {
return nil, err
}
_, _ = builder.WriteString(
fmt.Sprintf(
"### `%s` configuration\n\nSee the dedicated [linters-settings](/usage/linters) documentation section.\n\n",
node.Value,
),
)
continue
}
nodeSection := &yaml.Node{
Kind: root.Kind,
Style: root.Style,
Tag: root.Tag,
Value: root.Value,
Content: []*yaml.Node{node, nextNode},
}
snippet, errSnip := marshallSnippet(nodeSection)
if errSnip != nil {
return nil, errSnip
}
_, _ = builder.WriteString(fmt.Sprintf("### `%s` configuration\n\n%s", node.Value, snippet))
}
overview, err := marshallSnippet(globalNode)
if err != nil {
return nil, err
}
snippets.ConfigurationFile = overview + builder.String()
return &snippets, nil
}
func getLintersSettingSections(node, nextNode *yaml.Node) (string, error) {
dbManager, _ := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder())
lcs := dbManager.GetAllSupportedLinterConfigs()
var lintersDesc = make(map[string]string)
for _, lc := range lcs {
if lc.Internal {
continue
}
// it's important to use lc.Name() nor name because name can be alias
lintersDesc[lc.Name()] = getDesc(lc)
}
builder := &strings.Builder{}
for i := 0; i < len(nextNode.Content); i += 2 {
r := &yaml.Node{
Kind: nextNode.Kind,
Style: nextNode.Style,
Tag: nextNode.Tag,
Value: node.Value,
Content: []*yaml.Node{
{
Kind: node.Kind,
Value: node.Value,
},
{
Kind: nextNode.Kind,
Content: []*yaml.Node{nextNode.Content[i], nextNode.Content[i+1]},
},
},
}
_, _ = fmt.Fprintf(builder, "### %s\n\n", nextNode.Content[i].Value)
_, _ = fmt.Fprintf(builder, "%s\n\n", lintersDesc[nextNode.Content[i].Value])
_, _ = fmt.Fprintln(builder, "```yaml")
encoder := yaml.NewEncoder(builder)
encoder.SetIndent(2)
err := encoder.Encode(r)
if err != nil {
return "", err
}
_, _ = fmt.Fprintln(builder, "```")
_, _ = fmt.Fprintln(builder)
_, _ = fmt.Fprintf(builder, "[%s](#%s)\n\n", span("Back to the top", "🔼"), listItemPrefix+nextNode.Content[i].Value)
_, _ = fmt.Fprintln(builder)
}
return builder.String(), nil
}
func marshallSnippet(node *yaml.Node) (string, error) {
builder := &strings.Builder{}
if node.Value != "" {
_, _ = fmt.Fprintf(builder, "### %s\n\n", node.Value)
}
_, _ = fmt.Fprintln(builder, "```yaml")
encoder := yaml.NewEncoder(builder)
encoder.SetIndent(2)
err := encoder.Encode(node)
if err != nil {
return "", err
}
_, _ = fmt.Fprintln(builder, "```")
_, _ = fmt.Fprintln(builder)
return builder.String(), nil
}

View File

@ -0,0 +1,133 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/lint/lintersdb"
"github.com/golangci/golangci-lint/scripts/website/types"
)
func main() {
err := saveLinters()
if err != nil {
log.Fatalf("Save linters: %v", err)
}
err = saveDefaultExclusions()
if err != nil {
log.Fatalf("Save default exclusions: %v", err)
}
err = saveCLIHelp(filepath.Join("assets", "cli-help.json"))
if err != nil {
log.Fatalf("Save CLI help: %v", err)
}
}
func saveLinters() error {
linters, _ := lintersdb.NewLinterBuilder().Build(config.NewDefault())
var wraps []types.LinterWrapper
for _, l := range linters {
wrapper := types.LinterWrapper{
Name: l.Linter.Name(),
Desc: l.Linter.Desc(),
EnabledByDefault: l.EnabledByDefault,
LoadMode: l.LoadMode,
InPresets: l.InPresets,
AlternativeNames: l.AlternativeNames,
OriginalURL: l.OriginalURL,
Internal: l.Internal,
CanAutoFix: l.CanAutoFix,
IsSlow: l.IsSlow,
DoesChangeTypes: l.DoesChangeTypes,
Since: l.Since,
}
if l.Deprecation != nil {
wrapper.Deprecation = &types.Deprecation{
Since: l.Deprecation.Since,
Message: l.Deprecation.Message,
Replacement: l.Deprecation.Replacement,
}
}
wraps = append(wraps, wrapper)
}
return saveToJSONFile(filepath.Join("assets", "linters-info.json"), wraps)
}
func saveDefaultExclusions() error {
var excludePatterns []types.ExcludePattern
for _, pattern := range config.DefaultExcludePatterns {
excludePatterns = append(excludePatterns, types.ExcludePattern{
ID: pattern.ID,
Pattern: pattern.Pattern,
Linter: pattern.Linter,
Why: pattern.Why,
})
}
return saveToJSONFile(filepath.Join("assets", "default-exclusions.json"), excludePatterns)
}
func saveCLIHelp(dst string) error {
err := exec.Command("make", "build").Run()
if err != nil {
return fmt.Errorf("can't run make build: %w", err)
}
lintersOut, err := exec.Command("./golangci-lint", "help", "linters").Output()
if err != nil {
return fmt.Errorf("can't run linters cmd: %w", err)
}
lintersOutParts := bytes.Split(lintersOut, []byte("\n\n"))
helpCmd := exec.Command("./golangci-lint", "run", "-h")
helpCmd.Env = append(helpCmd.Env, os.Environ()...)
helpCmd.Env = append(helpCmd.Env, "HELP_RUN=1") // make default concurrency stable: don't depend on machine CPU number
help, err := helpCmd.Output()
if err != nil {
return fmt.Errorf("can't run help cmd: %w", err)
}
helpLines := bytes.Split(help, []byte("\n"))
shortHelp := bytes.Join(helpLines[2:], []byte("\n"))
data := types.CLIHelp{
Enable: string(lintersOutParts[0]),
Disable: string(lintersOutParts[1]),
Help: string(shortHelp),
}
return saveToJSONFile(dst, data)
}
func saveToJSONFile(dst string, data any) error {
file, err := os.Create(dst)
if err != nil {
return fmt.Errorf("open file (%s): %w", dst, err)
}
defer func() { _ = file.Close() }()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
err = encoder.Encode(data)
if err != nil {
return fmt.Errorf("encode JSON (%s): %w", dst, err)
}
return nil
}

View File

@ -0,0 +1,48 @@
package main
import (
"bytes"
"path/filepath"
"strings"
"text/template"
"github.com/golangci/golangci-lint/scripts/website/types"
)
const exclusionTmpl = `{{ $tick := "` + "`" + `" }}
### {{ .ID }}
- linter: {{ $tick }}{{ .Linter }}{{ $tick }}
- pattern: {{ $tick }}{{ .Pattern }}{{ $tick }}
- why: {{ .Why }}
`
func getDefaultExclusions() (string, error) {
defaultExcludePatterns, err := readJSONFile[[]types.ExcludePattern](filepath.Join("assets", "default-exclusions.json"))
if err != nil {
return "", err
}
bufferString := bytes.NewBufferString("")
tmpl, err := template.New("exclusions").Parse(exclusionTmpl)
if err != nil {
return "", err
}
for _, pattern := range defaultExcludePatterns {
data := map[string]any{
"ID": pattern.ID,
"Linter": pattern.Linter,
"Pattern": strings.ReplaceAll(pattern.Pattern, "`", "&grave;"),
"Why": pattern.Why,
}
err := tmpl.Execute(bufferString, data)
if err != nil {
return "", err
}
}
return bufferString.String(), nil
}

View File

@ -0,0 +1,315 @@
package main
import (
"fmt"
"path/filepath"
"reflect"
"sort"
"strings"
"unicode"
"unicode/utf8"
"gopkg.in/yaml.v3"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/scripts/website/types"
)
const listItemPrefix = "list-item-"
func getLintersListMarkdown(enabled bool) string {
linters, err := readJSONFile[[]*types.LinterWrapper](filepath.Join("assets", "linters-info.json"))
if err != nil {
panic(err)
}
var neededLcs []*types.LinterWrapper
for _, lc := range linters {
if lc.Internal {
continue
}
if lc.EnabledByDefault == enabled {
neededLcs = append(neededLcs, lc)
}
}
sort.Slice(neededLcs, func(i, j int) bool {
return neededLcs[i].Name < neededLcs[j].Name
})
lines := []string{
"|Name|Description|Presets|AutoFix|Since|",
"|---|---|---|---|---|---|",
}
for _, lc := range neededLcs {
line := fmt.Sprintf("|%s|%s|%s|%v|%s|",
getName(lc),
getDesc(lc),
strings.Join(lc.InPresets, ", "),
check(lc.CanAutoFix, "Auto fix supported"),
lc.Since,
)
lines = append(lines, line)
}
return strings.Join(lines, "\n")
}
func getName(lc *types.LinterWrapper) string {
name := lc.Name
if lc.OriginalURL != "" {
name = fmt.Sprintf("[%s](%s)", name, lc.OriginalURL)
}
if hasSettings(lc.Name) {
name = fmt.Sprintf("%s&nbsp;[%s](#%s)", name, spanWithID(listItemPrefix+lc.Name, "Configuration", "⚙️"), lc.Name)
}
if lc.Deprecation == nil {
return name
}
title := "deprecated"
if lc.Deprecation.Replacement != "" {
title += fmt.Sprintf(" since %s", lc.Deprecation.Since)
}
return name + "&nbsp;" + span(title, "⚠")
}
func check(b bool, title string) string {
if b {
return span(title, "✔")
}
return ""
}
func getDesc(lc *types.LinterWrapper) string {
desc := lc.Desc
if lc.Deprecation != nil {
desc = lc.Deprecation.Message
if lc.Deprecation.Replacement != "" {
desc += fmt.Sprintf(" Replaced by %s.", lc.Deprecation.Replacement)
}
}
return formatDesc(desc)
}
func formatDesc(desc string) string {
runes := []rune(desc)
r, _ := utf8.DecodeRuneInString(desc)
runes[0] = unicode.ToUpper(r)
if runes[len(runes)-1] != '.' {
runes = append(runes, '.')
}
return strings.ReplaceAll(string(runes), "\n", "<br/>")
}
func hasSettings(name string) bool {
tp := reflect.TypeOf(config.LintersSettings{})
for i := 0; i < tp.NumField(); i++ {
if strings.EqualFold(name, tp.Field(i).Name) {
return true
}
}
return false
}
func span(title, icon string) string {
return fmt.Sprintf(`<span title=%q>%s</span>`, title, icon)
}
func spanWithID(id, title, icon string) string {
return fmt.Sprintf(`<span id=%q title=%q>%s</span>`, id, title, icon)
}
type SettingSnippets struct {
ConfigurationFile string
LintersSettings string
}
func extractExampleSnippets(example []byte) (*SettingSnippets, error) {
var data yaml.Node
err := yaml.Unmarshal(example, &data)
if err != nil {
return nil, err
}
root := data.Content[0]
globalNode := &yaml.Node{
Kind: root.Kind,
Style: root.Style,
Tag: root.Tag,
Value: root.Value,
Anchor: root.Anchor,
Alias: root.Alias,
HeadComment: root.HeadComment,
LineComment: root.LineComment,
FootComment: root.FootComment,
Line: root.Line,
Column: root.Column,
}
snippets := SettingSnippets{}
builder := strings.Builder{}
for j, node := range root.Content {
switch node.Value {
case "run", "output", "linters", "linters-settings", "issues", "severity":
default:
continue
}
nextNode := root.Content[j+1]
newNode := &yaml.Node{
Kind: nextNode.Kind,
Content: []*yaml.Node{
{
HeadComment: fmt.Sprintf("See the dedicated %q documentation section.", node.Value),
Kind: node.Kind,
Style: node.Style,
Tag: node.Tag,
Value: "option",
},
{
Kind: node.Kind,
Style: node.Style,
Tag: node.Tag,
Value: "value",
},
},
}
globalNode.Content = append(globalNode.Content, node, newNode)
if node.Value == "linters-settings" {
snippets.LintersSettings, err = getLintersSettingSections(node, nextNode)
if err != nil {
return nil, err
}
_, _ = builder.WriteString(
fmt.Sprintf(
"### `%s` configuration\n\nSee the dedicated [linters-settings](/usage/linters) documentation section.\n\n",
node.Value,
),
)
continue
}
nodeSection := &yaml.Node{
Kind: root.Kind,
Style: root.Style,
Tag: root.Tag,
Value: root.Value,
Content: []*yaml.Node{node, nextNode},
}
snippet, errSnip := marshallSnippet(nodeSection)
if errSnip != nil {
return nil, errSnip
}
_, _ = builder.WriteString(fmt.Sprintf("### `%s` configuration\n\n%s", node.Value, snippet))
}
overview, err := marshallSnippet(globalNode)
if err != nil {
return nil, err
}
snippets.ConfigurationFile = overview + builder.String()
return &snippets, nil
}
func getLintersSettingSections(node, nextNode *yaml.Node) (string, error) {
linters, err := readJSONFile[[]*types.LinterWrapper](filepath.Join("assets", "linters-info.json"))
if err != nil {
return "", err
}
var lintersDesc = make(map[string]string)
for _, lc := range linters {
if lc.Internal {
continue
}
// it's important to use lc.Name() nor name because name can be alias
lintersDesc[lc.Name] = getDesc(lc)
}
builder := &strings.Builder{}
for i := 0; i < len(nextNode.Content); i += 2 {
r := &yaml.Node{
Kind: nextNode.Kind,
Style: nextNode.Style,
Tag: nextNode.Tag,
Value: node.Value,
Content: []*yaml.Node{
{
Kind: node.Kind,
Value: node.Value,
},
{
Kind: nextNode.Kind,
Content: []*yaml.Node{nextNode.Content[i], nextNode.Content[i+1]},
},
},
}
_, _ = fmt.Fprintf(builder, "### %s\n\n", nextNode.Content[i].Value)
_, _ = fmt.Fprintf(builder, "%s\n\n", lintersDesc[nextNode.Content[i].Value])
_, _ = fmt.Fprintln(builder, "```yaml")
encoder := yaml.NewEncoder(builder)
encoder.SetIndent(2)
err := encoder.Encode(r)
if err != nil {
return "", err
}
_, _ = fmt.Fprintln(builder, "```")
_, _ = fmt.Fprintln(builder)
_, _ = fmt.Fprintf(builder, "[%s](#%s)\n\n", span("Back to the top", "🔼"), listItemPrefix+nextNode.Content[i].Value)
_, _ = fmt.Fprintln(builder)
}
return builder.String(), nil
}
func marshallSnippet(node *yaml.Node) (string, error) {
builder := &strings.Builder{}
if node.Value != "" {
_, _ = fmt.Fprintf(builder, "### %s\n\n", node.Value)
}
_, _ = fmt.Fprintln(builder, "```yaml")
encoder := yaml.NewEncoder(builder)
encoder.SetIndent(2)
err := encoder.Encode(node)
if err != nil {
return "", err
}
_, _ = fmt.Fprintln(builder, "```")
_, _ = fmt.Fprintln(builder)
return builder.String(), nil
}

View File

@ -0,0 +1,182 @@
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/golangci/golangci-lint/internal/renameio"
"github.com/golangci/golangci-lint/scripts/website/types"
)
func main() {
replacements, err := buildTemplateContext()
if err != nil {
log.Fatalf("Failed to build template context: %s", err)
}
if err := rewriteDocs(replacements); err != nil {
log.Fatalf("Failed to rewrite docs: %s", err)
}
log.Print("Successfully expanded templates")
}
func rewriteDocs(replacements map[string]string) error {
madeReplacements := map[string]bool{}
err := filepath.Walk(filepath.Join("docs", "src", "docs"),
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
return processDoc(path, replacements, madeReplacements)
})
if err != nil {
return fmt.Errorf("failed to walk dir: %w", err)
}
if len(madeReplacements) != len(replacements) {
for key := range replacements {
if !madeReplacements[key] {
log.Printf("Replacement %q wasn't performed", key)
}
}
return fmt.Errorf("%d replacements weren't performed", len(replacements)-len(madeReplacements))
}
return nil
}
func processDoc(path string, replacements map[string]string, madeReplacements map[string]bool) error {
contentBytes, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read %s: %w", path, err)
}
content := string(contentBytes)
hasReplacements := false
for key, replacement := range replacements {
nextContent := content
nextContent = strings.ReplaceAll(nextContent, fmt.Sprintf("{.%s}", key), replacement)
// Yaml formatter in mdx code section makes extra spaces, need to match them too.
nextContent = strings.ReplaceAll(nextContent, fmt.Sprintf("{ .%s }", key), replacement)
if nextContent != content {
hasReplacements = true
madeReplacements[key] = true
content = nextContent
}
}
if !hasReplacements {
return nil
}
log.Printf("Expanded template in %s, saving it", path)
if err = renameio.WriteFile(path, []byte(content), os.ModePerm); err != nil {
return fmt.Errorf("failed to write changes to file %s: %w", path, err)
}
return nil
}
type latestRelease struct {
TagName string `json:"tag_name"`
}
func getLatestVersion() (string, error) {
req, err := http.NewRequest( //nolint:noctx
http.MethodGet,
"https://api.github.com/repos/golangci/golangci-lint/releases/latest",
http.NoBody,
)
if err != nil {
return "", fmt.Errorf("failed to prepare a http request: %w", err)
}
req.Header.Add("Accept", "application/vnd.github.v3+json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", fmt.Errorf("failed to get http response for the latest tag: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read a body for the latest tag: %w", err)
}
release := latestRelease{}
err = json.Unmarshal(body, &release)
if err != nil {
return "", fmt.Errorf("failed to unmarshal the body for the latest tag: %w", err)
}
return release.TagName, nil
}
func buildTemplateContext() (map[string]string, error) {
golangciYamlExample, err := os.ReadFile(".golangci.reference.yml")
if err != nil {
return nil, fmt.Errorf("can't read .golangci.reference.yml: %w", err)
}
snippets, err := extractExampleSnippets(golangciYamlExample)
if err != nil {
return nil, fmt.Errorf("can't read .golangci.reference.yml: %w", err)
}
helps, err := readJSONFile[types.CLIHelp](filepath.Join("assets", "cli-help.json"))
if err != nil {
return nil, err
}
changeLog, err := os.ReadFile("CHANGELOG.md")
if err != nil {
return nil, err
}
latestVersion, err := getLatestVersion()
if err != nil {
return nil, fmt.Errorf("failed to get the latest version: %w", err)
}
exclusions, err := getDefaultExclusions()
if err != nil {
return nil, fmt.Errorf("default exclusions: %w", err)
}
return map[string]string{
"LintersExample": snippets.LintersSettings,
"ConfigurationExample": snippets.ConfigurationFile,
"LintersCommandOutputEnabledOnly": helps.Enable,
"LintersCommandOutputDisabledOnly": helps.Disable,
"EnabledByDefaultLinters": getLintersListMarkdown(true),
"DisabledByDefaultLinters": getLintersListMarkdown(false),
"DefaultExclusions": exclusions,
"ThanksList": getThanksList(),
"RunHelpText": helps.Help,
"ChangeLog": string(changeLog),
"LatestVersion": latestVersion,
}, nil
}
func readJSONFile[T any](src string) (T, error) {
file, err := os.Open(src)
if err != nil {
var zero T
return zero, err
}
var result T
err = json.NewDecoder(file).Decode(&result)
if err != nil {
var zero T
return zero, err
}
return result, nil
}

View File

@ -0,0 +1,87 @@
package main
import (
"fmt"
"sort"
"strings"
"golang.org/x/exp/maps"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/lint/lintersdb"
)
type authorDetails struct {
Linters []string
Profile string
Avatar string
}
func getThanksList() string {
addedAuthors := map[string]*authorDetails{}
linters, _ := lintersdb.NewLinterBuilder().Build(config.NewDefault())
for _, lc := range linters {
if lc.Internal {
continue
}
if lc.OriginalURL == "" {
continue
}
linterURL := lc.OriginalURL
if lc.Name() == "staticcheck" {
linterURL = "https://github.com/dominikh/go-tools"
}
if author := extractAuthor(linterURL, "https://github.com/"); author != "" && author != "golangci" {
if _, ok := addedAuthors[author]; ok {
addedAuthors[author].Linters = append(addedAuthors[author].Linters, lc.Name())
} else {
addedAuthors[author] = &authorDetails{
Linters: []string{lc.Name()},
Profile: fmt.Sprintf("[%[1]s](https://github.com/sponsors/%[1]s)", author),
Avatar: fmt.Sprintf(`<img src="https://github.com/%[1]s.png" alt="%[1]s" style="max-width: 100%%;" width="20px;" />`, author),
}
}
} else if author := extractAuthor(linterURL, "https://gitlab.com/"); author != "" {
if _, ok := addedAuthors[author]; ok {
addedAuthors[author].Linters = append(addedAuthors[author].Linters, lc.Name())
} else {
addedAuthors[author] = &authorDetails{
Linters: []string{lc.Name()},
Profile: fmt.Sprintf("[%[1]s](https://gitlab.com/%[1]s)", author),
}
}
} else {
continue
}
}
authors := maps.Keys(addedAuthors)
sort.Slice(authors, func(i, j int) bool {
return strings.ToLower(authors[i]) < strings.ToLower(authors[j])
})
lines := []string{
"|Author|Linter(s)|",
"|---|---|",
}
for _, author := range authors {
lines = append(lines, fmt.Sprintf("|%s %s|%s|",
addedAuthors[author].Avatar, addedAuthors[author].Profile, strings.Join(addedAuthors[author].Linters, ", ")))
}
return strings.Join(lines, "\n")
}
func extractAuthor(originalURL, prefix string) string {
if !strings.HasPrefix(originalURL, prefix) {
return ""
}
return strings.SplitN(strings.TrimPrefix(originalURL, prefix), "/", 2)[0]
}

View File

@ -0,0 +1,48 @@
package types
import (
"golang.org/x/tools/go/packages"
)
type CLIHelp struct {
Enable string `json:"enable"`
Disable string `json:"disable"`
Help string `json:"help"`
}
type ExcludePattern struct {
ID string `json:"id,omitempty"`
Pattern string `json:"pattern,omitempty"`
Linter string `json:"linter,omitempty"`
Why string `json:"why,omitempty"`
}
type Deprecation struct {
Since string `json:"since,omitempty"`
Message string `json:"message,omitempty"`
Replacement string `json:"replacement,omitempty"`
}
// LinterWrapper same fields but with struct tags.
// The field Name and Desc are added to have the information about the linter.
// The field Linter is removed (not serializable).
type LinterWrapper struct {
Name string `json:"name"` // From linter.
Desc string `json:"desc"` // From linter.
EnabledByDefault bool `json:"enabledByDefault,omitempty"`
LoadMode packages.LoadMode `json:"loadMode,omitempty"`
InPresets []string `json:"inPresets,omitempty"`
AlternativeNames []string `json:"alternativeNames,omitempty"`
OriginalURL string `json:"originalURL,omitempty"`
Internal bool `json:"internal"`
CanAutoFix bool `json:"canAutoFix,omitempty"`
IsSlow bool `json:"isSlow"`
DoesChangeTypes bool `json:"doesChangeTypes,omitempty"`
Since string `json:"since,omitempty"`
Deprecation *Deprecation `json:"deprecation,omitempty"`
}