Skip to content

importas linter is non-deterministic #5218

@stephanos

Description

@stephanos

Welcome

  • Yes, I'm using a binary release within 2 latest releases. Only such installations are supported.
  • Yes, I've searched similar issues on GitHub and didn't find any.
  • Yes, I've read the typecheck section of the FAQ.
  • Yes, I've tried with the standalone linter if available (e.g., gocritic, go vet, etc.).
  • I agree to follow this project's Code of Conduct

Description of the problem

Running the importas linter does not seem to be deterministic.

I've run importas separately, with the same config, and it produces the expected results, consistently.

With golangci-lint it doesn't.

For example, in the output I pasted, you'll find both

common/enums/defaults.go:28:2: import "go.temporal.io/api/enums/v1" imported as "enumspb" but must be "enumspb1" according to config (importas)
        enumspb "go.temporal.io/api/enums/v1"

ie the first rule, enumspb1.

As well as

service/history/hsm/events.go:28:2: import "go.temporal.io/api/enums/v1" imported as "enumspb" but must be "enumspb2" according to config (importas)
        enumspb "go.temporal.io/api/enums/v1"
        ^

ie the second rule, enumspb2.

But they match the same import!

Version of golangci-lint

$ golangci-lint --version
golangci-lint has version v1.62.2 built with go1.23.3 from (unknown, modified: ?, mod sum: "h1:b8K5K9PN+rZN1+mKLtsZHz2XXS9aYKzQ9i25x3Qnxxw=") on (unknown)

Configuration

linters-settings:
  importas:
    alias:
      - pkg: go.temporal.io/api/(\w+)/v1
        alias: enumspb1
      - pkg: go.temporal.io/api/enums/v1
        alias: enumspb2

Go environment

$ go version && go env
go version go1.23.3 darwin/arm64
GO111MODULE='on'
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/stephan/Library/Caches/go-build'
GOENV='/Users/stephan/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/stephan/.asdf/installs/golang/1.23.2/packages/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/stephan/.asdf/installs/golang/1.23.2/packages'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/Users/stephan/.asdf/installs/golang/1.23.2/packages/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.3.darwin-arm64'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/Users/stephan/.asdf/installs/golang/1.23.2/packages/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.3.darwin-arm64/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.23.3'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/stephan/Library/Application Support/go/telemetry'
GCCGO='gccgo'
GOARM64='v8.0'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
GOMOD='/Users/stephan/Workspace/default/temporal/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/4w/5qdjw8sd6417nldg5pvhs_rr0000gn/T/go-build3778052966=/tmp/go-build -gno-record-gcc-switches -fno-common'

Verbose output of running

$ golangci-lint cache clean
$ golangci-lint run -v
INFO File cache stats: 38 entries of total size 432.7KiB 
INFO Memory: 50 samples, avg is 218.8MB, max is 863.7MB 
INFO Execution took 4.887243625s                  
stephan@Stephans-MacBook-Pro temporal % .bin/golangci-lint-v1.62.2 cache clean && .bin/golangci-lint-v1.62.2 run --verbose --config=.golangci.yml
INFO golangci-lint has version v1.62.2 built with go1.23.3 from (unknown, modified: ?, mod sum: "h1:b8K5K9PN+rZN1+mKLtsZHz2XXS9aYKzQ9i25x3Qnxxw=") on (unknown) 
INFO [config_reader] Used config file .golangci.yml 
INFO [lintersdb] Active 1 linters: [importas]     
INFO [loader] Go packages loading at mode 8767 (name|types_sizes|exports_file|imports|files|compiled_files|deps) took 2.217652584s 
INFO [runner/filename_unadjuster] Pre-built 0 adjustments in 165.738833ms 
INFO [linters_context/goanalysis] analyzers took 686.34634ms with top 10 stages: importas: 539.681616ms, inspect: 141.834324ms, typecheck: 4.8304ms 
INFO [runner/skip_dirs] Skipped 1 issues from dir api/workflow/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 4 issues from dir api/replication/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 7 issues from dir api/adminservice/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 1 issues from dir api/history/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 3 issues from dir api/deployment/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 3 issues from dir api/taskqueue/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 12 issues from dir api/historyservice/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 9 issues from dir api/matchingservice/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 1 issues from dir api/common/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 16 issues from dir api/persistence/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 3 issues from dir api/archiver/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 1 issues from dir api/checksum/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 3 issues from dir api/cli/v1 by pattern ^api 
INFO [runner/skip_dirs] Skipped 5 issues from dir api/schedule/v1 by pattern ^api 
INFO [runner/max_same_issues] 350/353 issues with text "import \"go.temporal.io/api/enums/v1\" imported as \"enumspb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 299/302 issues with text "import \"go.temporal.io/api/common/v1\" imported as \"commonpb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 164/167 issues with text "import \"go.temporal.io/api/history/v1\" imported as \"historypb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 112/115 issues with text "import \"go.temporal.io/api/taskqueue/v1\" imported as \"taskqueuepb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 63/66 issues with text "import \"go.temporal.io/api/command/v1\" imported as \"commandpb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 46/49 issues with text "import \"go.temporal.io/api/workflow/v1\" imported as \"workflowpb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 36/39 issues with text "import \"go.temporal.io/api/enums/v1\" imported as \"enumspb\" but must be \"enumspb2\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 34/37 issues with text "import \"go.temporal.io/api/failure/v1\" imported as \"failurepb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 31/34 issues with text "import \"go.temporal.io/api/update/v1\" imported as \"updatepb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 19/22 issues with text "import \"go.temporal.io/api/namespace/v1\" imported as \"namespacepb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 16/19 issues with text "import \"go.temporal.io/api/replication/v1\" imported as \"replicationpb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 15/18 issues with text "import \"go.temporal.io/api/deployment/v1\" imported as \"deploymentpb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 14/17 issues with text "import \"go.temporal.io/api/query/v1\" imported as \"querypb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 14/17 issues with text "import \"go.temporal.io/api/protocol/v1\" imported as \"protocolpb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 10/13 issues with text "import \"go.temporal.io/api/filter/v1\" imported as \"filterpb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 5/8 issues with text "import \"go.temporal.io/api/nexus/v1\" imported as \"nexuspb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 5/8 issues with text "import \"go.temporal.io/api/sdk/v1\" imported as \"sdkpb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 4/7 issues with text "import \"go.temporal.io/api/schedule/v1\" imported as \"schedpb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_same_issues] 1/4 issues with text "import \"go.temporal.io/api/batch/v1\" imported as \"batchpb\" but must be \"enumspb1\" according to config" were hidden, use --max-same-issues 
INFO [runner/max_from_linter] 18/68 issues from linter importas were hidden, use --max-issues-per-linter 
INFO [runner] Issues before processing: 1440, after processing: 50 
INFO [runner] Processors filtering stat (in/out): filename_unadjuster: 1440/1440, skip_dirs: 1440/1371, exclude: 1306/1306, uniq_by_line: 1306/1306, diff: 1306/1306, max_from_linter: 68/50, path_prefixer: 50/50, cgo: 1440/1440, max_per_file_from_linter: 1306/1306, path_shortener: 50/50, fixer: 50/50, path_prettifier: 1440/1440, identifier_marker: 1306/1306, exclude-rules: 1306/1306, nolint: 1306/1306, source_code: 50/50, skip_files: 1440/1440, autogenerated_exclude: 1371/1306, max_same_issues: 1306/68, severity-rules: 50/50, sort_results: 50/50, invalid_issue: 1440/1440 
INFO [runner] processing took 316.580623ms with stages: nolint: 229.031166ms, exclude-rules: 43.087376ms, autogenerated_exclude: 20.728583ms, identifier_marker: 13.736834ms, path_prettifier: 6.6605ms, source_code: 1.200833ms, skip_dirs: 1.090041ms, max_same_issues: 721.501µs, uniq_by_line: 148.583µs, invalid_issue: 71.167µs, cgo: 46.958µs, filename_unadjuster: 28.75µs, max_per_file_from_linter: 16.207µs, path_shortener: 6.458µs, max_from_linter: 4.375µs, sort_results: 292ns, fixer: 249ns, exclude: 209ns, diff: 208ns, skip_files: 167ns, path_prefixer: 83ns, severity-rules: 83ns 
INFO [runner] linters took 2.8375895s with stages: goanalysis_metalinter: 2.520802917s 
common/enums/defaults.go:28:2: import "go.temporal.io/api/enums/v1" imported as "enumspb" but must be "enumspb1" according to config (importas)
        enumspb "go.temporal.io/api/enums/v1"
        ^
common/persistence/visibility/manager/visibility_manager.go:34:2: import "go.temporal.io/api/common/v1" imported as "commonpb" but must be "enumspb1" according to config (importas)
        commonpb "go.temporal.io/api/common/v1"
        ^
common/persistence/visibility/manager/visibility_manager.go:35:2: import "go.temporal.io/api/enums/v1" imported as "enumspb" but must be "enumspb1" according to config (importas)
        enumspb "go.temporal.io/api/enums/v1"
        ^
common/persistence/visibility/manager/visibility_manager.go:36:2: import "go.temporal.io/api/workflow/v1" imported as "workflowpb" but must be "enumspb1" according to config (importas)
        workflowpb "go.temporal.io/api/workflow/v1"
        ^
common/persistence/sql/errors.go:28:2: import "go.temporal.io/api/enums/v1" imported as "enumspb" but must be "enumspb1" according to config (importas)
        enumspb "go.temporal.io/api/enums/v1"
        ^
common/persistence/sql/execution_state_map.go:32:2: import "go.temporal.io/api/common/v1" imported as "commonpb" but must be "enumspb1" according to config (importas)
        commonpb "go.temporal.io/api/common/v1"
        ^
common/persistence/sql/execution_state_non_map.go:32:2: import "go.temporal.io/api/common/v1" imported as "commonpb" but must be "enumspb1" according to config (importas)
        commonpb "go.temporal.io/api/common/v1"
        ^
common/worker_versioning/worker_versioning.go:35:2: import "go.temporal.io/api/deployment/v1" imported as "deploymentpb" but must be "enumspb1" according to config (importas)
        deploymentpb "go.temporal.io/api/deployment/v1"
        ^
common/worker_versioning/worker_versioning.go:38:2: import "go.temporal.io/api/workflow/v1" imported as "workflowpb" but must be "enumspb1" according to config (importas)
        workflowpb "go.temporal.io/api/workflow/v1"
        ^
service/history/hsm/events.go:28:2: import "go.temporal.io/api/enums/v1" imported as "enumspb" but must be "enumspb2" according to config (importas)
        enumspb "go.temporal.io/api/enums/v1"
        ^
service/history/hsm/events.go:29:2: import "go.temporal.io/api/history/v1" imported as "historypb" but must be "enumspb1" according to config (importas)
        historypb "go.temporal.io/api/history/v1"
        ^
service/history/hsm/registry.go:31:2: import "go.temporal.io/api/enums/v1" imported as "enumspb" but must be "enumspb2" according to config (importas)
        enumspb "go.temporal.io/api/enums/v1"
        ^
service/history/hsm/tree.go:34:2: import "go.temporal.io/api/history/v1" imported as "historypb" but must be "enumspb1" according to config (importas)
        historypb "go.temporal.io/api/history/v1"
        ^
components/callbacks/statemachine.go:31:2: import "go.temporal.io/api/failure/v1" imported as "failurepb" but must be "enumspb1" according to config (importas)
        failurepb "go.temporal.io/api/failure/v1"
        ^
components/nexusoperations/completion.go:33:2: import "go.temporal.io/api/history/v1" imported as "historypb" but must be "enumspb1" according to config (importas)
        historypb "go.temporal.io/api/history/v1"
        ^
components/nexusoperations/executors.go:36:2: import "go.temporal.io/api/failure/v1" imported as "failurepb" but must be "enumspb1" according to config (importas)
        failurepb "go.temporal.io/api/failure/v1"
        ^
components/nexusoperations/statemachine.go:31:2: import "go.temporal.io/api/failure/v1" imported as "failurepb" but must be "enumspb1" according to config (importas)
        failurepb "go.temporal.io/api/failure/v1"
        ^
service/history/tests/vars.go:31:2: import "go.temporal.io/api/namespace/v1" imported as "namespacepb" but must be "enumspb1" according to config (importas)
        namespacepb "go.temporal.io/api/namespace/v1"
        ^
internal/protocol/naming.go:31:2: import "go.temporal.io/api/protocol/v1" imported as "protocolpb" but must be "enumspb1" according to config (importas)
        protocolpb "go.temporal.io/api/protocol/v1"
        ^
components/nexusoperations/workflow/commands.go:34:2: import "go.temporal.io/api/command/v1" imported as "commandpb" but must be "enumspb1" according to config (importas)
        commandpb "go.temporal.io/api/command/v1"
        ^
service/history/api/describeworkflow/api.go:37:2: import "go.temporal.io/api/taskqueue/v1" imported as "taskqueuepb" but must be "enumspb1" according to config (importas)
        taskqueuepb "go.temporal.io/api/taskqueue/v1"
        ^
service/history/api/describeworkflow/api.go:38:2: import "go.temporal.io/api/workflow/v1" imported as "workflowpb" but must be "enumspb1" according to config (importas)
        workflowpb "go.temporal.io/api/workflow/v1"
        ^
service/history/api/updateworkflow/api.go:34:2: import "go.temporal.io/api/taskqueue/v1" imported as "taskqueuepb" but must be "enumspb1" according to config (importas)
        taskqueuepb "go.temporal.io/api/taskqueue/v1"
        ^
service/history/api/updateworkflow/api.go:35:2: import "go.temporal.io/api/update/v1" imported as "updatepb" but must be "enumspb1" according to config (importas)
        updatepb "go.temporal.io/api/update/v1"
        ^
service/history/api/pollupdate/api.go:33:2: import "go.temporal.io/api/update/v1" imported as "updatepb" but must be "enumspb1" according to config (importas)
        updatepb "go.temporal.io/api/update/v1"
        ^
service/history/api/queryworkflow/api.go:32:2: import "go.temporal.io/api/query/v1" imported as "querypb" but must be "enumspb1" according to config (importas)
        querypb "go.temporal.io/api/query/v1"
        ^
service/history/api/recordactivitytaskstarted/api.go:32:2: import "go.temporal.io/api/deployment/v1" imported as "deploymentpb" but must be "enumspb1" according to config (importas)
        deploymentpb "go.temporal.io/api/deployment/v1"
        ^
service/history/api/recordworkflowtaskstarted/api.go:33:2: import "go.temporal.io/api/query/v1" imported as "querypb" but must be "enumspb1" according to config (importas)
        querypb "go.temporal.io/api/query/v1"
        ^
service/history/api/recordworkflowtaskstarted/api.go:35:2: import "go.temporal.io/api/taskqueue/v1" imported as "taskqueuepb" but must be "enumspb1" according to config (importas)
        taskqueuepb "go.temporal.io/api/taskqueue/v1"
        ^
common/testing/testvars/test_vars.go:36:2: import "go.temporal.io/api/namespace/v1" imported as "namespacepb" but must be "enumspb1" according to config (importas)
        namespacepb "go.temporal.io/api/namespace/v1"
        ^
common/testing/testvars/test_vars.go:38:2: import "go.temporal.io/api/update/v1" imported as "updatepb" but must be "enumspb1" according to config (importas)
        updatepb "go.temporal.io/api/update/v1"
        ^
common/testing/updateutils/update.go:29:2: import "go.temporal.io/api/command/v1" imported as "commandpb" but must be "enumspb1" according to config (importas)
        commandpb "go.temporal.io/api/command/v1"
        ^
common/testing/updateutils/update.go:32:2: import "go.temporal.io/api/protocol/v1" imported as "protocolpb" but must be "enumspb1" according to config (importas)
        protocolpb "go.temporal.io/api/protocol/v1"
        ^
service/history/hsm/hsmtest/backend.go:30:2: import "go.temporal.io/api/enums/v1" imported as "enumspb" but must be "enumspb2" according to config (importas)
        enumspb "go.temporal.io/api/enums/v1"
        ^
common/log/tag/tags.go:31:2: import "go.temporal.io/api/deployment/v1" imported as "deploymentpb" but must be "enumspb1" according to config (importas)
        deploymentpb "go.temporal.io/api/deployment/v1"
        ^
common/namespace/mutate.go:28:2: import "go.temporal.io/api/namespace/v1" imported as "namespacepb" but must be "enumspb1" according to config (importas)
        namespacepb "go.temporal.io/api/namespace/v1"
        ^
common/namespace/replication_task_executor.go:33:2: import "go.temporal.io/api/replication/v1" imported as "replicationpb" but must be "enumspb1" according to config (importas)
        replicationpb "go.temporal.io/api/replication/v1"
        ^
common/namespace/transmission_task_handler.go:32:2: import "go.temporal.io/api/replication/v1" imported as "replicationpb" but must be "enumspb1" according to config (importas)
        replicationpb "go.temporal.io/api/replication/v1"
        ^
common/namespace/replication_task_executor_test.go:37:2: import "go.temporal.io/api/replication/v1" imported as "replicationpb" but must be "enumspb1" according to config (importas)
        replicationpb "go.temporal.io/api/replication/v1"
        ^
common/nexus/failure.go:32:2: import "go.temporal.io/api/nexus/v1" imported as "nexuspb" but must be "enumspb1" according to config (importas)
        nexuspb "go.temporal.io/api/nexus/v1"
        ^
common/persistence/persistence-tests/cluster_metadata_manager.go:35:2: import "go.temporal.io/api/version/v1" imported as "versionpb" but must be "enumspb1" according to config (importas)
        versionpb "go.temporal.io/api/version/v1"
        ^
common/rpc/interceptor/telemetry_test.go:31:2: import "go.temporal.io/api/command/v1" imported as "commandpb" but must be "enumspb1" according to config (importas)
        commandpb "go.temporal.io/api/command/v1"
        ^
common/rpc/interceptor/telemetry_test.go:34:2: import "go.temporal.io/api/protocol/v1" imported as "protocolpb" but must be "enumspb1" according to config (importas)
        protocolpb "go.temporal.io/api/protocol/v1"
        ^
components/nexusoperations/workflow/commands_test.go:36:2: import "go.temporal.io/api/sdk/v1" imported as "sdkpb" but must be "enumspb1" according to config (importas)
        sdkpb "go.temporal.io/api/sdk/v1"
        ^
service/frontend/nexus_endpoint_client.go:33:2: import "go.temporal.io/api/nexus/v1" imported as "nexuspb" but must be "enumspb1" according to config (importas)
        nexuspb "go.temporal.io/api/nexus/v1"
        ^
service/frontend/nexus_handler.go:39:2: import "go.temporal.io/api/nexus/v1" imported as "nexuspb" but must be "enumspb1" according to config (importas)
        nexuspb "go.temporal.io/api/nexus/v1"
        ^
service/frontend/version_checker.go:35:2: import "go.temporal.io/api/version/v1" imported as "versionpb" but must be "enumspb1" according to config (importas)
        versionpb "go.temporal.io/api/version/v1"
        ^
service/frontend/workflow_handler.go:39:2: import "go.temporal.io/api/batch/v1" imported as "batchpb" but must be "enumspb1" according to config (importas)
        batchpb "go.temporal.io/api/batch/v1"
        ^
service/frontend/workflow_handler.go:42:2: import "go.temporal.io/api/filter/v1" imported as "filterpb" but must be "enumspb1" according to config (importas)
        filterpb "go.temporal.io/api/filter/v1"
        ^
service/frontend/workflow_handler.go:44:2: import "go.temporal.io/api/query/v1" imported as "querypb" but must be "enumspb1" according to config (importas)
        querypb "go.temporal.io/api/query/v1"
        ^
INFO File cache stats: 38 entries of total size 653.5KiB 
INFO Memory: 54 samples, avg is 203.7MB, max is 872.5MB 
INFO Execution took 5.235075333s

A minimal reproducible example or link to a public repository

temporalio/temporal#6968

Validation

  • Yes, I've included all information above (version, config, etc.).

Supporter

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions