Repository: go-kratos/kratos
Pull request #3643 (2025-04-23 05:53:16)

github: Remove submodule dependencies in Dependabot.
1. Only include main module / dependency auto-updates.
2. Change to monthly check frequency.
3. Update golangci-lint and migrates configuration.


Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index c5d51d5e..64284b90 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -4,84 +4,4 @@ updates:
   - package-ecosystem: "github-actions"
     directory: "/"
     schedule:
-      interval: "daily"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/config/apollo"
-    schedule:
-      interval: "weekly"    
-  - package-ecosystem: "gomod"
-    directory: "/contrib/config/consul"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/config/etcd"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/config/kubernetes"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/config/nacos"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/encoding/msgpack"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/log/aliyun"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/log/zap"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/log/fluent"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/metrics/datadog"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/metrics/prometheus"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/opensergo"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/registry/apollo"
-    schedule:
-      interval: "weekly"    
-  - package-ecosystem: "gomod"
-    directory: "/contrib/registry/consul"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/registry/etcd"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/registry/kubernetes"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/registry/nacos"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/registry/zookeeper"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/registry/eureka"
-    schedule:
-      interval: "weekly"
-  - package-ecosystem: "gomod"
-    directory: "/contrib/registry/polaris"
-    schedule:
-      interval: "weekly"
+      interval: "monthly"
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 977e4ea3..7979eb96 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -13,7 +13,7 @@ jobs:
   build:
     strategy:
       matrix:
-        go: [ 1.21.x, 1.22.x, 1.23.x ]
+        go: [1.22.x, 1.23.x, 1.24.x]
     name: build & test
     runs-on: ubuntu-latest
     services:
@@ -44,7 +44,7 @@ jobs:
     steps:
       - uses: actions/checkout@v4
       - name: Set up Go
-        uses: actions/setup-go@v5.4.0
+        uses: actions/setup-go@v5
         with:
           go-version: ${{ matrix.go }}
 
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index e6cb8848..b2fdab47 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -29,7 +29,8 @@ jobs:
     steps:
       - uses: actions/checkout@v4
       - name: Lint
-        uses: golangci/golangci-lint-action@v6
+        uses: golangci/golangci-lint-action@v7
         with:
-          version: v1.61.0
-          working-directory: ${{ matrix.workdir }}
\ No newline at end of file
+          version: v2.0
+          working-directory: ${{ matrix.workdir }}
+          skip-pkg-cache: true
diff --git a/.golangci.yml b/.golangci.yml
index 6d021062..145559c5 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,73 +1,71 @@
+version: "2"
 run:
-  timeout: 5m
   modules-download-mode: readonly
-
 linters:
-  disable-all: true
-  fast: false
+  default: none
   enable:
     - bodyclose
     - dogsled
     - durationcheck
     - errcheck
-    # - copyloopvar # need upgrade to Go 1.22
-    - govet
-    - staticcheck
-    - gosimple
-    - gofmt
-    - gofumpt
     - goconst
-    - goimports
-    - mnd
     - gocyclo
+    - govet
     - ineffassign
     - lll
+    - misspell
+    - mnd
     - prealloc
     - revive
     - staticcheck
-    - typecheck
+    - unconvert
     - unused
-    - whitespace
     - wastedassign
-    - unconvert
-    - misspell
-
-  # don't enable:
-  # - asciicheck
-  # - scopelint
-  # - gochecknoglobals
-  # - gocognit
-  # - godot
-  # - godox
-  # - goerr113
-  # - interfacer
-  # - maligned
-  # - nestif
-  # - prealloc
-  # - testpackage
-  # - stylecheck
-  # - wsl
-
-linters-settings:
-  govet:
-    enable:
-      - shadow
-  whitespace:
-    multi-func: true
-  lll:
-    line-length: 160
-  mnd:
-    # don't include the "operation", "argument" and "assign"
-    checks:
-      - case
-      - condition
-      - return
-  goconst:
-    ignore-tests: true
-  gocyclo:
-    # recommend 10-20
-    min-complexity: 50
-  goimports:
-    local-prefixes: github.com/go-kratos # Put imports beginning with prefix after 3rd-party packages
-  misspell: # Finds commonly misspelled English words in comments
-    locale: US
+    - whitespace
+  settings:
+    gocyclo:
+      min-complexity: 50
+    govet:
+      enable:
+        - shadow
+    lll:
+      line-length: 160
+    misspell:
+      locale: US
+    mnd:
+      checks:
+        - case
+        - condition
+        - return
+    whitespace:
+      multi-func: true
+  exclusions:
+    generated: lax
+    presets:
+      - comments
+      - common-false-positives
+      - legacy
+      - std-error-handling
+    rules:
+      - linters:
+          - goconst
+        path: (.+)_test\.go
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
+formatters:
+  enable:
+    - gofmt
+    - gofumpt
+    - goimports
+  settings:
+    goimports:
+      local-prefixes:
+        - github.com/go-kratos
+  exclusions:
+    generated: lax
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
diff --git a/cmd/kratos/internal/base/path.go b/cmd/kratos/internal/base/path.go
index d24647fd..9a6d8654 100644
--- a/cmd/kratos/internal/base/path.go
+++ b/cmd/kratos/internal/base/path.go
@@ -101,7 +101,7 @@ func hasSets(name string, sets []string) bool {
 func Tree(path string, dir string) {
 	_ = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
 		if err == nil && info != nil && !info.IsDir() {
-			fmt.Printf("%s %s (%v bytes)\n", color.GreenString("CREATED"), strings.Replace(path, dir+"/", "", -1), info.Size())
+			fmt.Printf("%s %s (%v bytes)\n", color.GreenString("CREATED"), strings.ReplaceAll(path, dir+"/", ""), info.Size())
 		}
 		return nil
 	})
diff --git a/contrib/polaris/router.go b/contrib/polaris/router.go
index 93202eb5..c60fc464 100644
--- a/contrib/polaris/router.go
+++ b/contrib/polaris/router.go
@@ -54,7 +54,7 @@ func (p *Polaris) NodeFilter(opts ...RouterOption) selector.NodeFilter {
 			req.SourceService.Service = appInfo.Name()
 		}
 
-		req.AddArguments(model.BuildCallerServiceArgument(p.namespace, req.ProcessRoutersRequest.SourceService.Service))
+		req.AddArguments(model.BuildCallerServiceArgument(p.namespace, req.SourceService.Service))
 
 		// process transport
 		if tr, ok := transport.FromClientContext(ctx); ok {
diff --git a/contrib/registry/discovery/impl_discover.go b/contrib/registry/discovery/impl_discover.go
index 38b1fc5c..3be0865e 100644
--- a/contrib/registry/discovery/impl_discover.go
+++ b/contrib/registry/discovery/impl_discover.go
@@ -44,21 +44,21 @@ func (d *Discovery) GetService(ctx context.Context, serviceName string) ([]*regi
 
 func (d *Discovery) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) {
 	return &watcher{
-		Resolve:     d.resolveBuild(serviceName),
+		resolve:     d.resolveBuild(serviceName),
 		serviceName: serviceName,
 		cancelCtx:   ctx,
 	}, nil
 }
 
 type watcher struct {
-	*Resolve
+	resolve *Resolve
 
 	cancelCtx   context.Context
 	serviceName string
 }
 
 func (w *watcher) Next() ([]*registry.ServiceInstance, error) {
-	event := w.Resolve.Watch()
+	event := w.resolve.Watch()
 
 	select {
 	case <-event:
@@ -70,12 +70,12 @@ func (w *watcher) Next() ([]*registry.ServiceInstance, error) {
 	ctx, cancel := context.WithTimeout(context.TODO(), 15*time.Second)
 	defer cancel()
 
-	ins, ok := w.Resolve.fetch(ctx)
+	ins, ok := w.resolve.fetch(ctx)
 	if !ok {
 		return nil, errors.New("Discovery.GetService fetch failed")
 	}
 
-	out := filterInstancesByZone(ins, w.Resolve.d.config.Zone)
+	out := filterInstancesByZone(ins, w.resolve.d.config.Zone)
 	if len(out) == 0 {
 		return nil, fmt.Errorf("Discovery.GetService(%s) not found", w.serviceName)
 	}
@@ -84,5 +84,5 @@ func (w *watcher) Next() ([]*registry.ServiceInstance, error) {
 }
 
 func (w *watcher) Stop() error {
-	return w.Resolve.Close()
+	return w.resolve.Close()
 }
diff --git a/internal/group/group.go b/internal/group/group.go
index 04340e37..23fae95a 100644
--- a/internal/group/group.go
+++ b/internal/group/group.go
@@ -5,26 +5,29 @@ package group
 
 import "sync"
 
+// Factory is a function that creates an object of type T.
+type Factory[T any] func() T
+
 // Group is a lazy load container.
-type Group struct {
-	new  func() any
-	vals map[string]any
+type Group[T any] struct {
+	factory func() T
+	vals    map[string]T
 	sync.RWMutex
 }
 
 // NewGroup news a group container.
-func NewGroup(new func() any) *Group {
-	if new == nil {
+func NewGroup[T any](factory Factory[T]) *Group[T] {
+	if factory == nil {
 		panic("container.group: can't assign a nil to the new function")
 	}
-	return &Group{
-		new:  new,
-		vals: make(map[string]any),
+	return &Group[T]{
+		factory: factory,
+		vals:    make(map[string]T),
 	}
 }
 
 // Get gets the object by the given key.
-func (g *Group) Get(key string) any {
+func (g *Group[T]) Get(key string) T {
 	g.RLock()
 	v, ok := g.vals[key]
 	if ok {
@@ -40,25 +43,25 @@ func (g *Group) Get(key string) any {
 	if ok {
 		return v
 	}
-	v = g.new()
+	v = g.factory()
 	g.vals[key] = v
 	return v
 }
 
 // Reset resets the new function and deletes all existing objects.
-func (g *Group) Reset(new func() any) {
-	if new == nil {
+func (g *Group[T]) Reset(factory Factory[T]) {
+	if factory == nil {
 		panic("container.group: can't assign a nil to the new function")
 	}
 	g.Lock()
-	g.new = new
+	g.factory = factory
 	g.Unlock()
 	g.Clear()
 }
 
 // Clear deletes all objects.
-func (g *Group) Clear() {
+func (g *Group[T]) Clear() {
 	g.Lock()
-	g.vals = make(map[string]any)
+	g.vals = make(map[string]T)
 	g.Unlock()
 }
diff --git a/middleware/circuitbreaker/circuitbreaker.go b/middleware/circuitbreaker/circuitbreaker.go
index 33fc1fd0..bcf2d479 100644
--- a/middleware/circuitbreaker/circuitbreaker.go
+++ b/middleware/circuitbreaker/circuitbreaker.go
@@ -20,7 +20,7 @@ type Option func(*options)
 
 // WithGroup with circuit breaker group.
 // NOTE: implements generics circuitbreaker.CircuitBreaker
-func WithGroup(g *group.Group) Option {
+func WithGroup(g *group.Group[circuitbreaker.CircuitBreaker]) Option {
 	return func(o *options) {
 		o.group = g
 	}
@@ -29,21 +29,21 @@ func WithGroup(g *group.Group) Option {
 // WithCircuitBreaker with circuit breaker genFunc.
 func WithCircuitBreaker(genBreakerFunc func() circuitbreaker.CircuitBreaker) Option {
 	return func(o *options) {
-		o.group = group.NewGroup(func() any {
+		o.group = group.NewGroup(func() circuitbreaker.CircuitBreaker {
 			return genBreakerFunc()
 		})
 	}
 }
 
 type options struct {
-	group *group.Group
+	group *group.Group[circuitbreaker.CircuitBreaker]
 }
 
 // Client circuitbreaker middleware will return errBreakerTriggered when the circuit
 // breaker is triggered and the request is rejected directly.
 func Client(opts ...Option) middleware.Middleware {
 	opt := &options{
-		group: group.NewGroup(func() any {
+		group: group.NewGroup(func() circuitbreaker.CircuitBreaker {
 			return sre.NewBreaker()
 		}),
 	}
@@ -53,7 +53,7 @@ func Client(opts ...Option) middleware.Middleware {
 	return func(handler middleware.Handler) middleware.Handler {
 		return func(ctx context.Context, req any) (any, error) {
 			info, _ := transport.FromClientContext(ctx)
-			breaker := opt.group.Get(info.Operation()).(circuitbreaker.CircuitBreaker)
+			breaker := opt.group.Get(info.Operation())
 			if err := breaker.Allow(); err != nil {
 				// rejected
 				// NOTE: when client reject requests locally,
diff --git a/transport/grpc/server.go b/transport/grpc/server.go
index 02bdb706..6d2f8969 100644
--- a/transport/grpc/server.go
+++ b/transport/grpc/server.go
@@ -244,7 +244,7 @@ func (s *Server) Stop(ctx context.Context) error {
 	go func() {
 		defer close(done)
 		log.Info("[gRPC] server stopping")
-		s.Server.GracefulStop()
+		s.GracefulStop()
 	}()
 
 	select {
diff --git a/transport/http/server.go b/transport/http/server.go
index dc92e789..19c019c0 100644
--- a/transport/http/server.go
+++ b/transport/http/server.go
@@ -347,7 +347,7 @@ func (s *Server) Stop(ctx context.Context) error {
 	if err != nil {
 		if ctx.Err() != nil {
 			log.Warn("[HTTP] server couldn't stop gracefully in time, doing force stop")
-			err = s.Server.Close()
+			err = s.Close()
 		}
 	}
 	return err


Test patch:
diff --git a/encoding/form/form_test.go b/encoding/form/form_test.go
index de13a65b..819f8e9f 100644
--- a/encoding/form/form_test.go
+++ b/encoding/form/form_test.go
@@ -148,6 +148,7 @@ func TestFormCodecUnmarshal(t *testing.T) {
 	}
 }
 
+//nolint:staticcheck
 func TestProtoEncodeDecode(t *testing.T) {
 	in := &complex.Complex{
 		Id:      2233,
@@ -227,6 +228,7 @@ func TestProtoEncodeDecode(t *testing.T) {
 	}
 }
 
+//nolint:staticcheck
 func TestDecodeStructPb(t *testing.T) {
 	req := new(ectest.StructPb)
 	query := `data={"name":"kratos"}&data_list={"name1": "kratos"}&data_list={"name2": "go-kratos"}`
diff --git a/internal/group/group_test.go b/internal/group/group_test.go
index 53a973da..9de4ffba 100644
--- a/internal/group/group_test.go
+++ b/internal/group/group_test.go
@@ -7,22 +7,22 @@ import (
 
 func TestGroupGet(t *testing.T) {
 	count := 0
-	g := NewGroup(func() any {
+	g := NewGroup[int](func() int {
 		count++
 		return count
 	})
 	v := g.Get("key_0")
-	if !reflect.DeepEqual(v.(int), 1) {
+	if !reflect.DeepEqual(v, 1) {
 		t.Errorf("expect 1, actual %v", v)
 	}
 
 	v = g.Get("key_1")
-	if !reflect.DeepEqual(v.(int), 2) {
+	if !reflect.DeepEqual(v, 2) {
 		t.Errorf("expect 2, actual %v", v)
 	}
 
 	v = g.Get("key_0")
-	if !reflect.DeepEqual(v.(int), 1) {
+	if !reflect.DeepEqual(v, 1) {
 		t.Errorf("expect 1, actual %v", v)
 	}
 	if !reflect.DeepEqual(count, 2) {
@@ -31,12 +31,12 @@ func TestGroupGet(t *testing.T) {
 }
 
 func TestGroupReset(t *testing.T) {
-	g := NewGroup(func() any {
+	g := NewGroup(func() int {
 		return 1
 	})
 	g.Get("key")
 	call := false
-	g.Reset(func() any {
+	g.Reset(func() int {
 		call = true
 		return 1
 	})
@@ -56,7 +56,7 @@ func TestGroupReset(t *testing.T) {
 }
 
 func TestGroupClear(t *testing.T) {
-	g := NewGroup(func() any {
+	g := NewGroup(func() int {
 		return 1
 	})
 	g.Get("key")
diff --git a/middleware/circuitbreaker/circuitbreaker_test.go b/middleware/circuitbreaker/circuitbreaker_test.go
index 01ce43b9..6981d656 100644
--- a/middleware/circuitbreaker/circuitbreaker_test.go
+++ b/middleware/circuitbreaker/circuitbreaker_test.go
@@ -5,6 +5,7 @@ import (
 	"errors"
 	"testing"
 
+	"github.com/go-kratos/aegis/circuitbreaker"
 	kratoserrors "github.com/go-kratos/kratos/v2/errors"
 	"github.com/go-kratos/kratos/v2/internal/group"
 	"github.com/go-kratos/kratos/v2/transport"
@@ -46,8 +47,8 @@ func (c *circuitBreakerMock) MarkFailed()  {}
 
 func Test_WithGroup(t *testing.T) {
 	o := options{
-		group: group.NewGroup(func() any {
-			return ""
+		group: group.NewGroup(func() circuitbreaker.CircuitBreaker {
+			return &circuitBreakerMock{}
 		}),
 	}
 
@@ -68,7 +69,7 @@ func TestServer(_ *testing.T) {
 	ctx := transport.NewClientContext(context.Background(), &transportMock{})
 
 	_, _ = Client(func(o *options) {
-		o.group = group.NewGroup(func() any {
+		o.group = group.NewGroup(func() circuitbreaker.CircuitBreaker {
 			return &circuitBreakerMock{err: errors.New("circuitbreaker error")}
 		})
 	})(nextValid)(ctx, nil)