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 - name: Install golangci-lint
run: | 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 - name: Run golangci-lint
run: golangci-lint run --timeout=5m run: golangci-lint run --timeout=5m

1
.gitignore vendored
View file

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

View file

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

View file

@ -10,9 +10,9 @@ MCP Registry is a community-driven registry service for Model Context Protocol (
### Prerequisites ### Prerequisites
- **Go 1.23.x** - The project requires this specific version (check with `go version`) - **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 - 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 ```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) ### Git Hooks (Optional)
@ -72,7 +72,7 @@ gofmt -s -l .
gofmt -s -w . 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 ## 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) - Docker (optional, but recommended for development)
For development: For development:
- golangci-lint v1.61.0 - Install with: - golangci-lint v2.3.1 - Install with:
```bash ```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 ## Running

View file

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

View file

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

View file

@ -257,7 +257,7 @@ func TestServersHandler(t *testing.T) {
} }
// Verify mock expectations // 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) assert.Empty(t, paginatedResp.Metadata.NextCursor)
// Verify mock expectations // Verify mock expectations
mockRegistry.Mock.AssertExpectations(t) mockRegistry.AssertExpectations(t)
} }
// TestServersDetailHandlerIntegration tests the servers detail handler with actual HTTP requests // TestServersDetailHandlerIntegration tests the servers detail handler with actual HTTP requests
@ -387,5 +387,5 @@ func TestServersDetailHandlerIntegration(t *testing.T) {
assert.Equal(t, *serverDetail, serverDetailResp) assert.Equal(t, *serverDetail, serverDetailResp)
// Verify mock expectations // 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, ".") parts2 := strings.Split(version2, ".")
// Pad with zeros if needed // Pad with zeros if needed
maxLen := len(parts1) maxLen := max(len(parts2), len(parts1))
if len(parts2) > maxLen {
maxLen = len(parts2)
}
for len(parts1) < maxLen { for len(parts1) < maxLen {
parts1 = append(parts1, "0") parts1 = append(parts1, "0")
@ -162,10 +159,7 @@ func (db *MemoryDB) List(
}) })
// Apply pagination // Apply pagination
endIdx := startIdx + limit endIdx := min(startIdx+limit, len(filteredEntries))
if endIdx > len(filteredEntries) {
endIdx = len(filteredEntries)
}
var result []*model.Server var result []*model.Server
if startIdx < len(filteredEntries) { if startIdx < len(filteredEntries) {