feat: migrate golangci-lint from v1 to v2 (#144)

Migrate golangci-lint from v1 to v2.

## Motivation and Context
The golangci-lint has been upgraded to v2 since March 2025.
The v2 of golangci-lint has simplified the linters management.

## How Has This Been Tested?
Tested locally with golangci-lint@v2.1.6

## Breaking Changes
No.

## Types of changes
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [ ] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->

---------

Co-authored-by: haokunt <haokunt@opera.com>
Co-authored-by: Avinash Sridhar <sridharavinash@users.noreply.github.com>
Co-authored-by: Adam Jones <adamj+git@anthropic.com>
This commit is contained in:
Haokun Tang 2025-08-06 19:07:16 +08:00 • committed by GitHub
commit 6ba612942e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 94 additions and 101 deletions

View file

@ -25,7 +25,7 @@ jobs:
- name: Install golangci-lint
run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.61.0
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.3.1
- name: Run golangci-lint
run: golangci-lint run --timeout=5m

1
.gitignore vendored
View file

@ -4,6 +4,7 @@ build/*
.mcpregistry*
**/bin
cmd/registry/registry
.DS_Store
validate-examples
validate-schemas
.idea/

View file

@ -1,19 +1,11 @@
# GolangCI-Lint configuration
# See: https://golangci-lint.run/usage/configuration/
version: "2"
run:
timeout: 5m
modules-download-mode: readonly
linters:
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- typecheck
- unused
- asasalint
- asciicheck
- bidichk
@ -28,14 +20,11 @@ linters:
- exhaustive
- forbidigo
- funlen
- gci
- gocognit
- goconst
- gocritic
- gocyclo
- godox
- gofmt
- goimports
- gomoddirectives
- gomodguard
- goprintffuncname
@ -59,8 +48,7 @@ linters:
- revive
- rowserrcheck
- sqlclosecheck
- stylecheck
- tenv
- staticcheck
- testpackage
- thelper
- tparallel
@ -69,55 +57,69 @@ linters:
- usestdlibvars
- wastedassign
- whitespace
linters-settings:
revive:
settings:
cyclop:
max-complexity: 50
funlen:
lines: 150
statements: 150
gocognit:
min-complexity: 50
goconst:
min-len: 3
min-occurrences: 3
gocyclo:
min-complexity: 25
lll:
line-length: 150
misspell:
locale: US
mnd:
checks:
- argument
- case
- condition
- operation
- return
nestif:
min-complexity: 8
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- name: use-any
disabled: false
severity: error
cyclop:
max-complexity: 50
funlen:
lines: 150
statements: 150
gocognit:
min-complexity: 50
gocyclo:
min-complexity: 25
goconst:
min-len: 3
min-occurrences: 3
mnd:
checks:
- argument
- case
- condition
- operation
- return
lll:
line-length: 150
misspell:
locale: US
nestif:
min-complexity: 8
issues:
exclude-rules:
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- mnd
- funlen
- gocyclo
- errcheck
- dupl
- gosec
# Ignore long lines in generated code
- path: docs/
linters:
- lll
# Allow local replacement directives in go.mod
- path: go\.mod
linters:
- gomoddirectives
- linters:
- dupl
- errcheck
- funlen
- gocyclo
- gosec
- mnd
path: _test\.go
- linters:
- lll
path: docs/
- linters:
- mnd
path: integrationtests/
- linters:
- gomoddirectives
path: go\.mod
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gci
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View file

@ -10,9 +10,9 @@ MCP Registry is a community-driven registry service for Model Context Protocol (
### Prerequisites
- **Go 1.23.x** - The project requires this specific version (check with `go version`)
- Consider using a Go version manager like `g` or `gvm` if you work on multiple projects
- **golangci-lint v1.61.0** - Install with:
- **golangci-lint v2.3.1** - Install with:
```bash
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.61.0
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.3.1
```
### Git Hooks (Optional)
@ -72,7 +72,7 @@ gofmt -s -l .
gofmt -s -w .
```
**Note**: The project uses golangci-lint v1.61.0 with 62 enabled linters. Always run linting locally before pushing to avoid CI failures.
**Note**: The project uses golangci-lint v2.3.1 with 62 enabled linters. Always run linting locally before pushing to avoid CI failures.
## Architecture Overview

View file

@ -29,9 +29,9 @@ The MCP Registry service provides a centralized repository for MCP server entrie
- Docker (optional, but recommended for development)
For development:
- golangci-lint v1.61.0 - Install with:
- golangci-lint v2.3.1 - Install with:
```bash
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.61.0
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.3.1
```
## Running

View file

@ -23,17 +23,17 @@ type MockRegistryService struct {
}
func (m *MockRegistryService) List(cursor string, limit int) ([]model.Server, string, error) {
args := m.Mock.Called(cursor, limit)
args := m.Called(cursor, limit)
return args.Get(0).([]model.Server), args.String(1), args.Error(2)
}
func (m *MockRegistryService) GetByID(id string) (*model.ServerDetail, error) {
args := m.Mock.Called(id)
args := m.Called(id)
return args.Get(0).(*model.ServerDetail), args.Error(1)
}
func (m *MockRegistryService) Publish(serverDetail *model.ServerDetail) error {
args := m.Mock.Called(serverDetail)
args := m.Called(serverDetail)
return args.Error(0)
}
@ -45,17 +45,17 @@ type MockAuthService struct {
func (m *MockAuthService) StartAuthFlow(
ctx context.Context, method model.AuthMethod, repoRef string,
) (map[string]string, string, error) {
args := m.Mock.Called(ctx, method, repoRef)
args := m.Called(ctx, method, repoRef)
return args.Get(0).(map[string]string), args.String(1), args.Error(2)
}
func (m *MockAuthService) CheckAuthStatus(ctx context.Context, statusToken string) (string, error) {
args := m.Mock.Called(ctx, statusToken)
args := m.Called(ctx, statusToken)
return args.String(0), args.Error(1)
}
func (m *MockAuthService) ValidateAuth(ctx context.Context, authentication model.Authentication) (bool, error) {
args := m.Mock.Called(ctx, authentication)
args := m.Called(ctx, authentication)
return args.Bool(0), args.Error(1)
}
@ -416,8 +416,8 @@ func TestPublishHandler(t *testing.T) {
}
// Assert that all expectations were met
mockRegistry.Mock.AssertExpectations(t)
mockAuthService.Mock.AssertExpectations(t)
mockRegistry.AssertExpectations(t)
mockAuthService.AssertExpectations(t)
})
}
}
@ -487,7 +487,7 @@ func TestPublishHandlerBearerTokenParsing(t *testing.T) {
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusCreated, rr.Code)
mockAuthService.Mock.AssertExpectations(t)
mockAuthService.AssertExpectations(t)
})
}
}
@ -552,7 +552,7 @@ func TestPublishHandlerAuthMethodSelection(t *testing.T) {
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusCreated, rr.Code)
mockAuthService.Mock.AssertExpectations(t)
mockAuthService.AssertExpectations(t)
})
}
}
@ -639,9 +639,9 @@ func TestPublishIntegration(t *testing.T) {
// Verify the server was actually published by retrieving it
publishedServer, err := registryService.GetByID(response["id"])
require.NoError(t, err)
assert.Equal(t, publishReq.ServerDetail.Name, publishedServer.Name)
assert.Equal(t, publishReq.ServerDetail.Description, publishedServer.Description)
assert.Equal(t, publishReq.ServerDetail.VersionDetail.Version, publishedServer.VersionDetail.Version)
assert.Equal(t, publishReq.Name, publishedServer.Name)
assert.Equal(t, publishReq.Description, publishedServer.Description)
assert.Equal(t, publishReq.VersionDetail.Version, publishedServer.VersionDetail.Version)
assert.Len(t, publishedServer.Packages, 1)
assert.Len(t, publishedServer.Remotes, 1)
})

View file

@ -60,12 +60,8 @@ func ServersHandler(registry service.RegistryService) http.HandlerFunc {
return
}
if parsedLimit > 100 {
// Cap maximum limit to prevent excessive queries
limit = 100
} else {
limit = parsedLimit
}
// Cap maximum limit to prevent excessive queries
limit = min(parsedLimit, 100)
}
// Use the GetAll method to get paginated results

View file

@ -257,7 +257,7 @@ func TestServersHandler(t *testing.T) {
}
// Verify mock expectations
mockRegistry.Mock.AssertExpectations(t)
mockRegistry.AssertExpectations(t)
})
}
}
@ -321,7 +321,7 @@ func TestServersHandlerIntegration(t *testing.T) {
assert.Empty(t, paginatedResp.Metadata.NextCursor)
// Verify mock expectations
mockRegistry.Mock.AssertExpectations(t)
mockRegistry.AssertExpectations(t)
}
// TestServersDetailHandlerIntegration tests the servers detail handler with actual HTTP requests
@ -387,5 +387,5 @@ func TestServersDetailHandlerIntegration(t *testing.T) {
assert.Equal(t, *serverDetail, serverDetailResp)
// Verify mock expectations
mockRegistry.Mock.AssertExpectations(t)
mockRegistry.AssertExpectations(t)
}

View file

@ -48,10 +48,7 @@ func compareSemanticVersions(version1, version2 string) int {
parts2 := strings.Split(version2, ".")
// Pad with zeros if needed
maxLen := len(parts1)
if len(parts2) > maxLen {
maxLen = len(parts2)
}
maxLen := max(len(parts2), len(parts1))
for len(parts1) < maxLen {
parts1 = append(parts1, "0")
@ -162,10 +159,7 @@ func (db *MemoryDB) List(
})
// Apply pagination
endIdx := startIdx + limit
if endIdx > len(filteredEntries) {
endIdx = len(filteredEntries)
}
endIdx := min(startIdx+limit, len(filteredEntries))
var result []*model.Server
if startIdx < len(filteredEntries) {