Repository: zeromicro/go-zero
Pull request #3357 (2023-06-16 06:23:26)

fix In `goctl new api`, occur error `invalid character 'A' looking for beginning of value`
In windows10,I want to expenrience go-zero
In execute `goctl new api demo`,occur `occur error `invalid character 'A' looking for beginning of value`
The reason for this error is that my Windows computer is set to use UTF-8 character encoding, so when executing the cmd, there will be a prompt at the beginning saying 'Active code page: 65001', causing issues when validating the JSON format.

windows10下,我想体验go-zero
在执行goctl new api demo时,出现invalid character 'A' looking for beginning of value 的错误
这个错误的原因是我的Windows电脑设置为使用UTF-8字符编码,所以在执行cmd时,开头会有一个提示说'Active code page: 65001',导致验证JSON格式时出问题。

befor:
![image-20230616142034892](https://cdn.jsdelivr.net/gh/2822132073/image/202306161420998.png)

after
![image-20230616142159399](https://cdn.jsdelivr.net/gh/2822132073/image/202306161422465.png)

anqiansong: This is a great pr that solves a long-standing problem, and would be even better if it added some unit testing. @2822132073 
2822132073: ok,i add a unit testing to the pr,please look at it to see if it's correct
KesonAn: > ok,i add a unit testing to the pr,please look at it to see if it's correct

The case does not covered:

```go
{
	name: "utf-8",
	data: strings.NewReader(`Active code page: 65001 {
		"Path":"foo",
		"Dir":"/home/foo",
		"GoMod":"/home/foo/go.mod",
		"GoVersion":"go1.16"}`),
	want: []Module{
		{
			Path:      "foo",
			Dir:       "/home/foo",
			GoMod:     "/home/foo/go.mod",
			GoVersion: "go1.16",
		},
	},
},
```

@2822132073 
2822132073: sorry, i add the case to the pr just now, the test is pass 
![image-20230624221803292](https://cdn.jsdelivr.net/gh/2822132073/image/202306242218661.png)
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/zeromicro/go-zero/pull/3357?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#3357](https://app.codecov.io/gh/zeromicro/go-zero/pull/3357?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (4d4e4ea) into [master](https://app.codecov.io/gh/zeromicro/go-zero/commit/bcccfab82490ad70031621f4eddc184ccd5551a1?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (bcccfab) will **increase** coverage by `0.04%`.
> The diff coverage is `n/a`.

> :exclamation: Current head 4d4e4ea differs from pull request most recent head e4b9547. Consider uploading reports for the commit e4b9547 to get more accurate results


[see 2 files with indirect coverage changes](https://app.codecov.io/gh/zeromicro/go-zero/pull/3357/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)


Patch:
diff --git a/tools/goctl/util/ctx/gomod.go b/tools/goctl/util/ctx/gomod.go
index 96e3c235..108dbfdb 100644
--- a/tools/goctl/util/ctx/gomod.go
+++ b/tools/goctl/util/ctx/gomod.go
@@ -1,6 +1,7 @@
 package ctx
 
 import (
+	"bufio"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -94,8 +95,11 @@ func getRealModule(workDir string, execRun execx.RunFunc) (*Module, error) {
 }
 
 func decodePackages(rc io.Reader) ([]Module, error) {
+	r := bufio.NewReader(rc)
+	_, _ = r.ReadSlice('{')
+	_ = r.UnreadByte()
 	var modules []Module
-	decoder := json.NewDecoder(rc)
+	decoder := json.NewDecoder(r)
 	for decoder.More() {
 		var m Module
 		if err := decoder.Decode(&m); err != nil {


Test patch:
diff --git a/tools/goctl/util/ctx/gomod_test.go b/tools/goctl/util/ctx/gomod_test.go
index 11c419ca..b8bc4612 100644
--- a/tools/goctl/util/ctx/gomod_test.go
+++ b/tools/goctl/util/ctx/gomod_test.go
@@ -3,9 +3,11 @@ package ctx
 import (
 	"bytes"
 	"go/build"
+	"io"
 	"os"
 	"path/filepath"
 	"reflect"
+	"strings"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -110,3 +112,90 @@ func Test_getRealModule(t *testing.T) {
 		})
 	}
 }
+
+func TestDecodePackages(t *testing.T) {
+	tests := []struct {
+		name    string
+		data    io.Reader
+		want    []Module
+		wantErr bool
+	}{
+		{
+			name: "single module",
+			data: strings.NewReader(`{
+						"Path":"foo",
+						"Dir":"/home/foo",
+						"GoMod":"/home/foo/go.mod",
+						"GoVersion":"go1.16"}`),
+			want: []Module{
+				{
+					Path:      "foo",
+					Dir:       "/home/foo",
+					GoMod:     "/home/foo/go.mod",
+					GoVersion: "go1.16",
+				},
+			},
+		},
+		{
+			name: "go work multiple modules",
+			data: strings.NewReader(`
+					{
+						"Path":"foo",
+						"Dir":"/home/foo",
+						"GoMod":"/home/foo/go.mod",
+						"GoVersion":"go1.18"
+					}
+					{
+						"Path":"bar",
+						"Dir":"/home/bar",
+						"GoMod":"/home/bar/go.mod",
+						"GoVersion":"go1.18"
+					}`),
+			want: []Module{
+				{
+					Path:      "foo",
+					Dir:       "/home/foo",
+					GoMod:     "/home/foo/go.mod",
+					GoVersion: "go1.18",
+				},
+				{
+					Path:      "bar",
+					Dir:       "/home/bar",
+					GoMod:     "/home/bar/go.mod",
+					GoVersion: "go1.18",
+				},
+			},
+		},
+		{
+			name: "There are extra characters at the beginning",
+			data: strings.NewReader(`Active code page: 65001
+					{
+						"Path":"foo",
+						"Dir":"/home/foo",
+						"GoMod":"/home/foo/go.mod",
+						"GoVersion":"go1.18"
+					}`),
+			want: []Module{
+				{
+					Path:      "foo",
+					Dir:       "/home/foo",
+					GoMod:     "/home/foo/go.mod",
+					GoVersion: "go1.18",
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result, err := decodePackages(tt.data)
+			if err != nil {
+				t.Errorf("decodePackages() error %v,wantErr = %v", err, tt.wantErr)
+			}
+			if !reflect.DeepEqual(result, tt.want) {
+				t.Errorf("decodePackages() = %v,want  %v", result, tt.want)
+
+			}
+		})
+	}
+}