diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 57857e5..bd8adc4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,16 +19,24 @@ permissions: jobs: testlint: runs-on: ubuntu-latest + strategy: + matrix: + go-version: [1.21.x, 1.22.x, 1.23.x] steps: - name: checkout uses: actions/checkout@v4 - name: setup-go uses: actions/setup-go@v5 with: - go-version: 1.21.x + go-version: ${{ matrix.go-version }} - name: make-test run: make test && make checkgenerate - name: make-lint + # Often, lint & gofmt guidelines depend on the Go version. To prevent + # conflicting guidance, run only on the most recent supported version. + # For the same reason, only check generated code on the most recent + # supported version. + if: matrix.go-version == '1.23.x' run: make lint docker-build-push: if: github.ref == 'refs/heads/main' diff --git a/.golangci.yml b/.golangci.yml index 3a98685..9b15a94 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,3 @@ -run: - skip-dirs-use-default: false linters-settings: errcheck: check-type-assertions: true @@ -32,37 +30,33 @@ linters-settings: linters: enable-all: true disable: + - copyloopvar # only valid for go v1.22 and above - cyclop # covered by gocyclo - - deadcode # abandoned - depguard # in golangci-lint v1.53.0+ default requires only stdlib deps - - exhaustivestruct # replaced by exhaustruct + - exportloopref # deprecated in golangci v1.60.2 + - execinquery # deprecated in golangci v1.58 - funlen # rely on code review to limit function length - gocognit # dubious "cognitive overhead" quantification - gofumpt # prefer standard gofmt - goimports # rely on gci instead - - golint # deprecated by Go team - - gomnd # some unnamed constants are okay - - ifshort # deprecated by author - - interfacer # deprecated by author + - gomnd # deprecated in golangci v1.58 in favor of mnd + - mnd # some unnamed constants are okay + - intrange # only valid for go v1.22 and above - ireturn # "accept interfaces, return structs" isn't ironclad - lll # don't want hard limits for line length - maintidx # covered by gocyclo - - maligned # readability trumps efficient struct packing - nlreturn # generous whitespace violates house style - - nosnakecase # deprecated in https://github.com/golangci/golangci-lint/pull/3065 - paralleltest # in this project, it's not worth making all tests parallel - - scopelint # deprecated by author - - structcheck # abandoned - testpackage # internal tests are fine - tparallel # in this project, it's not worth making all tests parallel - - varcheck # abandoned - wrapcheck # don't _always_ need to wrap errors - wsl # generous whitespace violates house style issues: + exclude-dirs-use-default: false exclude: # Don't ban use of fmt.Errorf to create new errors, but the remaining # checks from err113 are useful. - - "err113: do not define dynamic errors.*" + - "do not define dynamic errors.*" exclude-rules: # It's much more convenient to keep eliza's matching and response data as # globals rather than config. diff --git a/Makefile b/Makefile index b6a9f2d..cb3ea7a 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,7 @@ $(BIN)/license-header: Makefile $(BIN)/golangci-lint: Makefile @mkdir -p $(@D) - $(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54.1 + $(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.3 $(BIN)/protoc-gen-go: Makefile @mkdir -p $(@D) diff --git a/cmd/demoserver/main.go b/cmd/demoserver/main.go index abcb1c3..7784177 100644 --- a/cmd/demoserver/main.go +++ b/cmd/demoserver/main.go @@ -53,7 +53,7 @@ func (e *elizaServer) Say( _ context.Context, req *connect.Request[elizav1.SayRequest], ) (*connect.Response[elizav1.SayResponse], error) { - reply, _ := eliza.Reply(req.Msg.Sentence) // ignore end-of-conversation detection + reply, _ := eliza.Reply(req.Msg.GetSentence()) // ignore end-of-conversation detection return connect.NewResponse(&elizav1.SayResponse{ Sentence: reply, }), nil @@ -73,7 +73,7 @@ func (e *elizaServer) Converse( } else if err != nil { return fmt.Errorf("receive request: %w", err) } - reply, endSession := eliza.Reply(request.Sentence) + reply, endSession := eliza.Reply(request.GetSentence()) if err := stream.Send(&elizav1.ConverseResponse{Sentence: reply}); err != nil { return fmt.Errorf("send response: %w", err) } @@ -88,7 +88,7 @@ func (e *elizaServer) Introduce( req *connect.Request[elizav1.IntroduceRequest], stream *connect.ServerStream[elizav1.IntroduceResponse], ) error { - name := req.Msg.Name + name := req.Msg.GetName() if name == "" { name = "Anonymous User" } @@ -125,7 +125,7 @@ func newCORS() *cors.Cors { http.MethodPatch, http.MethodDelete, }, - AllowOriginFunc: func(origin string) bool { + AllowOriginFunc: func(_ /* origin */ string) bool { // Allow all origins, which effectively disables CORS. return true }, diff --git a/cmd/demoserver/main_test.go b/cmd/demoserver/main_test.go index 5f8d38b..cd04264 100644 --- a/cmd/demoserver/main_test.go +++ b/cmd/demoserver/main_test.go @@ -17,16 +17,15 @@ package main import ( "context" "errors" - "fmt" "io" "net/http" "net/http/httptest" - "sync" "testing" "connectrpc.com/connect" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/sync/errgroup" elizav1 "connect-examples-go/internal/gen/connectrpc/eliza/v1" "connect-examples-go/internal/gen/connectrpc/eliza/v1/elizav1connect" @@ -59,39 +58,37 @@ func TestElizaServer(t *testing.T) { result, err := client.Say(context.Background(), connect.NewRequest(&elizav1.SayRequest{ Sentence: "Hello", })) - require.Nil(t, err) - assert.True(t, len(result.Msg.Sentence) > 0) + require.NoError(t, err) + assert.NotEmpty(t, result.Msg.GetSentence()) } }) t.Run("converse", func(t *testing.T) { for _, client := range clients { sendValues := []string{"Hello!", "How are you doing?", "I have an issue with my bike", "bye"} var receivedValues []string - stream := client.Converse(context.Background()) - var wg sync.WaitGroup - wg.Add(2) - go func() { - defer wg.Done() + grp, ctx := errgroup.WithContext(context.Background()) + stream := client.Converse(ctx) + grp.Go(func() error { for _, sentence := range sendValues { err := stream.Send(&elizav1.ConverseRequest{Sentence: sentence}) - require.Nil(t, err, fmt.Sprintf(`failed for string sentence: "%s"`, sentence)) + if err != nil { + return err + } } - require.Nil(t, stream.CloseRequest()) - }() - go func() { - defer wg.Done() + return stream.CloseRequest() + }) + grp.Go(func() error { for { msg, err := stream.Receive() if errors.Is(err, io.EOF) { break } - require.Nil(t, err) - assert.True(t, len(msg.Sentence) > 0) - receivedValues = append(receivedValues, msg.Sentence) + assert.NotEmpty(t, msg.GetSentence()) + receivedValues = append(receivedValues, msg.GetSentence()) } - require.Nil(t, stream.CloseResponse()) - }() - wg.Wait() + return stream.CloseResponse() + }) + require.NoError(t, grp.Wait()) assert.Equal(t, len(receivedValues), len(sendValues)) } }) @@ -102,13 +99,13 @@ func TestElizaServer(t *testing.T) { Name: "Ringo", }) stream, err := client.Introduce(context.Background(), request) - assert.Nil(t, err) + require.NoError(t, err) for stream.Receive() { total++ } - assert.Nil(t, stream.Err()) - assert.Nil(t, stream.Close()) - assert.True(t, total > 0) + assert.NoError(t, stream.Err()) + assert.NoError(t, stream.Close()) + assert.Positive(t, total) } }) } diff --git a/go.mod b/go.mod index c931aa4..1e24637 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ // Users should clone the repo to explore the examples. module connect-examples-go -go 1.18 +go 1.21 require ( connectrpc.com/connect v1.14.0 @@ -18,6 +18,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/sync v0.8.0 // indirect golang.org/x/text v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 7016f85..b409c3e 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=