From 40dcdeaa37a674a7275bd4bd320e2d77799da2e3 Mon Sep 17 00:00:00 2001 From: Ulysses Souza Date: Tue, 19 Mar 2024 16:20:44 +0100 Subject: [PATCH 1/2] Add tests Signed-off-by: Ulysses Souza --- facade_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/facade_test.go b/facade_test.go index 2ed49a3..a1924cb 100644 --- a/facade_test.go +++ b/facade_test.go @@ -172,6 +172,16 @@ A = "aaa ${B} ccc " "B": strPtr("bbb "), }, }, + { + "SpecialWithVariable", + `SPECIAL1 = "{{{ ${A} }}}"`, + map[string]*string{ + "SPECIAL1": strPtr("{{{ aaa }}}"), + }, + map[string]*string{ + "A": strPtr("aaa"), + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From fb324d0ee57132c51861e13f3c76ca255f196555 Mon Sep 17 00:00:00 2001 From: Ulysses Souza Date: Tue, 19 Mar 2024 17:48:59 +0100 Subject: [PATCH 2/2] Add linter Signed-off-by: Ulysses Souza --- .github/workflows/ci.yml | 3 + .golangci.yaml | 231 +++++++++++++++++++++++++++++++++++++++ Makefile | 5 +- dao/default_dao.go | 11 +- facade_test.go | 2 +- handlers/filehandler.go | 4 +- handlers/valuehandler.go | 12 +- 7 files changed, 256 insertions(+), 12 deletions(-) create mode 100644 .golangci.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5884ddf..48bec25 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,3 +30,6 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} slug: ulyssessouza/envlang + + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v4.0.0 diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..48d84c5 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,231 @@ +# This code is licensed under the terms of the MIT license https://opensource.org/license/mit +# Copyright (c) 2021 Marat Reymers + +## Golden config for golangci-lint v1.56.2 +# +# This is the best config for golangci-lint based on my experience and opinion. +# It is very strict, but not extremely strict. +# Feel free to adapt and change it for your needs. + +run: + # Timeout for analysis, e.g. 30s, 5m. + # Default: 1m + timeout: 3m + + +# This file contains only configs which differ from defaults. +# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml +linters-settings: + cyclop: + # The maximal code complexity to report. + # Default: 10 + max-complexity: 30 + # The maximal average package complexity. + # If it's higher than 0.0 (float) the check is enabled + # Default: 0.0 + package-average: 10.0 + + errcheck: + # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. + # Such cases aren't reported by default. + # Default: false + check-type-assertions: true + + exhaustive: + # Program elements to check for exhaustiveness. + # Default: [ switch ] + check: + - switch + - map + + funlen: + # Checks the number of lines in a function. + # If lower than 0, disable the check. + # Default: 60 + lines: 100 + # Checks the number of statements in a function. + # If lower than 0, disable the check. + # Default: 40 + statements: 50 + # Ignore comments when counting lines. + # Default false + ignore-comments: true + + gocognit: + # Minimal code complexity to report. + # Default: 30 (but we recommend 10-20) + min-complexity: 20 + + gocritic: + # Settings passed to gocritic. + # The settings key is the name of a supported gocritic checker. + # The list of supported checkers can be find in https://go-critic.github.io/overview. + settings: + captLocal: + # Whether to restrict checker to params only. + # Default: true + paramsOnly: false + underef: + # Whether to skip (*x).method() calls where x is a pointer receiver. + # Default: true + skipRecvDeref: false + + gomnd: + # List of function patterns to exclude from analysis. + # Values always ignored: `time.Date`, + # `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`, + # `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`. + # Default: [] + ignored-functions: + - flag.Arg + - flag.Duration.* + - flag.Float.* + - flag.Int.* + - flag.Uint.* + - os.Chmod + - os.Mkdir.* + - os.OpenFile + - os.WriteFile + - prometheus.ExponentialBuckets.* + - prometheus.LinearBuckets + + gomodguard: + blocked: + # List of blocked modules. + # Default: [] + modules: + - github.com/golang/protobuf: + recommendations: + - google.golang.org/protobuf + reason: "see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules" + - github.com/satori/go.uuid: + recommendations: + - github.com/google/uuid + reason: "satori's package is not maintained" + - github.com/gofrs/uuid: + recommendations: + - github.com/gofrs/uuid/v5 + reason: "gofrs' package was not go module before v5" + + govet: + # Enable all analyzers. + # Default: false + enable-all: true + # Disable analyzers by name. + # Run `go tool vet help` to see all analyzers. + # Default: [] + disable: + - fieldalignment # too strict + # Settings per analyzer. + settings: + shadow: + # Whether to be strict about shadowing; can be noisy. + # Default: false + strict: true + + inamedparam: + # Skips check for interface methods with only a single parameter. + # Default: false + skip-single-param: true + + nakedret: + # Make an issue if func has more lines of code than this setting, and it has naked returns. + # Default: 30 + max-func-lines: 0 + + nolintlint: + # Exclude following linters from requiring an explanation. + # Default: [] + allow-no-explanation: [ funlen, gocognit, lll ] + # Enable to require an explanation of nonzero length after each nolint directive. + # Default: false + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. + # Default: false + require-specific: true + + rowserrcheck: + # database/sql is always checked + # Default: [] + packages: + - github.com/jmoiron/sqlx + + tenv: + # The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. + # Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked. + # Default: false + all: true + + +linters: + disable-all: true + enable: + ## enabled by default + - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases + - gosimple # specializes in simplifying a code + - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - ineffassign # detects when assignments to existing variables are not used + - staticcheck # is a go vet on steroids, applying a ton of static analysis checks + - typecheck # like the front-end of a Go compiler, parses and type-checks Go code + - unused # checks for unused constants, variables, functions and types + ## disabled by default + - asasalint # checks for pass []any as any in variadic func(...any) + - asciicheck # checks that your code does not contain non-ASCII identifiers + - bidichk # checks for dangerous unicode character sequences + - bodyclose # checks whether HTTP response body is closed successfully + - cyclop # checks function and package cyclomatic complexity + - dupl # tool for code clone detection + - durationcheck # checks for two durations multiplied together + - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error + - errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13 + - execinquery # checks query string in Query function which reads your Go src files and warning it finds + - exhaustive # checks exhaustiveness of enum switch statements + - exportloopref # checks for pointers to enclosing loop variables + - forbidigo # forbids identifiers + - funlen # tool for detection of long functions + - gocheckcompilerdirectives # validates go compiler directive comments (//go:) + - gochecknoglobals # checks that no global variables exist + - gochecksumtype # checks exhaustiveness on Go "sum types" + - gocognit # computes and checks the cognitive complexity of functions + - goconst # finds repeated strings that could be replaced by a constant + - gocritic # provides diagnostics that check for bugs, performance and style issues + - gocyclo # computes and checks the cyclomatic complexity of functions + - godot # checks if comments end in a period + - goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt + - gomnd # detects magic numbers + - gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod + - gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations + - goprintffuncname # checks that printf-like functions are named with f at the end + - gosec # inspects source code for security problems + - lll # reports long lines + - loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap) + - makezero # finds slice declarations with non-zero initial length + - mirror # reports wrong mirror patterns of bytes/strings usage + - musttag # enforces field tags in (un)marshaled structs + - nakedret # finds naked returns in functions greater than a specified function length + - nestif # reports deeply nested if statements + - nilerr # finds the code that returns nil even if it checks that the error is not nil + - nilnil # checks that there is no simultaneous return of nil error and an invalid value + - noctx # finds sending http request without context.Context + - nolintlint # reports ill-formed or insufficient nolint directives + - nonamedreturns # reports all named returns + - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL + - predeclared # finds code that shadows one of Go's predeclared identifiers + - promlinter # checks Prometheus metrics naming via promlint + - protogetter # reports direct reads from proto message fields when getters should be used + - reassign # checks that package variables are not reassigned + - revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint + - rowserrcheck # checks whether Err of rows is checked successfully + - sloglint # ensure consistent code style when using log/slog + - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed + - stylecheck # is a replacement for golint + - tenv # detects using os.Setenv instead of t.Setenv since Go1.17 + - testableexamples # checks if examples are testable (have an expected output) + - testifylint # checks usage of github.com/stretchr/testify + - testpackage # makes you use a separate _test package + - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes + - unconvert # removes unnecessary type conversions + - unparam # reports unused function parameters + - usestdlibvars # detects the possibility to use variables/constants from the Go standard library + - wastedassign # finds wasted assignment statements + - whitespace # detects leading and trailing whitespace diff --git a/Makefile b/Makefile index 13696f4..120b11b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -all: clean gen test +all: clean gen test lint @echo "All done!" test: @@ -18,3 +18,6 @@ grun: antlr4 EnvLangValue.g4 javac -cp "/usr/share/java/antlr-4.13.1-complete.jar:$CLASSPATH" EnvLangValue*.java java -Xmx500M -cp "/usr/share/java/antlr-4.13.1-complete.jar:$CLASSPATH" org.antlr.v4.gui.TestRig EnvLangValue dqstrings -tokens + +lint: + golangci-lint run ./... --timeout 2m diff --git a/dao/default_dao.go b/dao/default_dao.go index 7ffafce..761eec2 100644 --- a/dao/default_dao.go +++ b/dao/default_dao.go @@ -5,6 +5,11 @@ import ( "sync" ) +const ( + pairSeparator = "=" + pairNumber = 2 +) + var _ EnvLangDao = &DefaultDao{} type DefaultDao struct { @@ -26,8 +31,9 @@ func NewDefaultDaoFromEnv(env []string) EnvLangDao { m: make(map[string]*string), env: make(map[string]*string), } + for _, e := range env { - splitEnv := strings.SplitN(e, "=", 2) + splitEnv := strings.SplitN(e, pairSeparator, pairNumber) v := splitEnv[1] d.env[splitEnv[0]] = &v } @@ -49,7 +55,7 @@ func (d *DefaultDao) ImportList(env []string) { defer d.Unlock() for _, e := range env { - splitEnv := strings.SplitN(e, "=", 2) + splitEnv := strings.SplitN(e, pairSeparator, pairNumber) v := splitEnv[1] d.m[splitEnv[0]] = &v } @@ -60,6 +66,7 @@ func (d *DefaultDao) ImportMap(m map[string]string) { defer d.Unlock() for k, v := range m { + v := v d.m[k] = &v } } diff --git a/facade_test.go b/facade_test.go index a1924cb..2b6dee7 100644 --- a/facade_test.go +++ b/facade_test.go @@ -13,6 +13,7 @@ func init() { log.SetLevel(log.DebugLevel) } +//nolint:funlen func TestGetValue(t *testing.T) { tests := []struct { name string @@ -188,7 +189,6 @@ A = "aaa ${B} ccc " d := dao.NewDefaultDaoFromMap(tt.envState) assert.DeepEqual(t, tt.expected, GetVariables(d, tt.input)) }) - } } diff --git a/handlers/filehandler.go b/handlers/filehandler.go index a5b028a..28e4d77 100644 --- a/handlers/filehandler.go +++ b/handlers/filehandler.go @@ -27,7 +27,7 @@ func (l *EnvLangFileListener) GetVariables() map[string]*string { } func (l *EnvLangFileListener) ExitEntry(c *fileparser.EntryContext) { - var valuePtr *string = nil + var valuePtr *string id := strings.TrimSpace(c.Identifier().GetText()) if id == "" { @@ -52,7 +52,7 @@ func (l *EnvLangFileListener) ExitEntry(c *fileparser.EntryContext) { v = v[1 : len(v)-1] // Removing quotes fallthrough case fileparser.EnvLangFileParserTEXT: - v := GetValue(l.d, v) + v = GetValue(l.d, v) valuePtr = &v default: panic(fmt.Sprintf("unexpected string: %s", c.Value().GetStr().GetText())) diff --git a/handlers/valuehandler.go b/handlers/valuehandler.go index 89e9331..8228ec2 100644 --- a/handlers/valuehandler.go +++ b/handlers/valuehandler.go @@ -29,15 +29,15 @@ func (l *envLangValueListener) ExitVariable(c *valueparser.VariableContext) { fullText := c.GetText() log.Debugf("ExitVariable: %s", fullText) - vName := "" - var_ := c.GetVar_() - switch var_.GetTokenType() { + var vName string + varr := c.GetVar_() + switch varr.GetTokenType() { case valueparser.EnvLangValueParserSIMPLE_VAR: - vName = var_.GetText()[1:] + vName = varr.GetText()[1:] case valueparser.EnvLangValueParserSTRICT_VAR: - vName = strings.TrimSpace(var_.GetText()[2 : len(var_.GetText())-1]) + vName = strings.TrimSpace(varr.GetText()[2 : len(varr.GetText())-1]) default: - panic("unexpected token: " + var_.GetText()) + panic("unexpected token: " + varr.GetText()) } value, ok := l.d.Get(vName)