Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 28 additions & 8 deletions cmd/kratos/internal/base/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import (
"log"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/fatih/color"
)

var protobufRawDescBlockRE = regexp.MustCompile(`(?ms)^const file_.*?_rawDesc = "" \+\r?\n(?:\t"(?:[^"\\]|\\.)*" \+\r?\n)*\t"(?:[^"\\]|\\.)*"\r?\n`)

func kratosHome() string {
dir, err := os.UserHomeDir()
if err != nil {
Expand Down Expand Up @@ -44,17 +47,34 @@ func copyFile(src, dst string, replaces []string) error {
if err != nil {
return err
}
var old string
for i, next := range replaces {
if i%2 == 0 {
old = next
continue
}
buf = bytes.ReplaceAll(buf, []byte(old), []byte(next))
}
buf = replaceTemplateContent(buf, replaces)
return os.WriteFile(dst, buf, srcinfo.Mode())
}

func replaceTemplateContent(buf []byte, replaces []string) []byte {
matches := protobufRawDescBlockRE.FindAllIndex(buf, -1)
if len(matches) == 0 {
return applyReplacements(buf, replaces)
}

var out bytes.Buffer
last := 0
for _, match := range matches {
out.Write(applyReplacements(buf[last:match[0]], replaces))
out.Write(buf[match[0]:match[1]])
last = match[1]
}
out.Write(applyReplacements(buf[last:], replaces))
return out.Bytes()
}

func applyReplacements(buf []byte, replaces []string) []byte {
for i := 0; i+1 < len(replaces); i += 2 {
buf = bytes.ReplaceAll(buf, []byte(replaces[i]), []byte(replaces[i+1]))
}
return buf
}

func copyDir(src, dst string, replaces, ignores []string) error {
srcinfo, err := os.Stat(src)
if err != nil {
Expand Down
120 changes: 120 additions & 0 deletions cmd/kratos/internal/base/path_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package base

import (
"strings"
"testing"
)

func TestReplaceTemplateContentPreservesProtobufRawDesc(t *testing.T) {
const oldMod = "github.com/example/template"
const newMod = "github.com/example/service"

input := strings.Join([]string{
"package conf",
"",
"import dep \"" + oldMod + "/api/greeter/v1\"",
"",
"const file_conf_conf_proto_rawDesc = \"\" +",
"\t\"\\n\" +",
"\t\"\\x0fconf/conf.proto\\x12\\n\" +",
"\t\"kratos.apiB7Z5" + oldMod + "/internal/conf;confb\\x06proto3\"",
"",
"var wireImport = \"" + oldMod + "/internal/server\"",
"",
}, "\n")
Comment thread
NepetaLemon marked this conversation as resolved.

got := string(replaceTemplateContent([]byte(input), []string{oldMod, newMod}))

if !strings.Contains(got, `import dep "`+newMod+`/api/greeter/v1"`) {
t.Fatalf("expected import path to be replaced, got:\n%s", got)
}
if !strings.Contains(got, `var wireImport = "`+newMod+`/internal/server"`) {
t.Fatalf("expected regular string to be replaced, got:\n%s", got)
}
if !strings.Contains(got, `B7Z5`+oldMod+`/internal/conf;confb\x06proto3"`) {
t.Fatalf("expected protobuf raw descriptor to stay untouched, got:\n%s", got)
}
if strings.Contains(got, `B7Z5`+newMod+`/internal/conf;confb\x06proto3"`) {
t.Fatalf("protobuf raw descriptor was unexpectedly rewritten:\n%s", got)
}
}

func TestReplaceTemplateContentWithoutRawDescReplacesEverywhere(t *testing.T) {
const oldMod = "github.com/example/template"
const newMod = "github.com/example/service"

input := []byte(`package main

import _ "` + oldMod + `/internal/biz"

var modulePath = "` + oldMod + `"
`)

got := string(replaceTemplateContent(input, []string{oldMod, newMod}))

if strings.Contains(got, oldMod) {
t.Fatalf("expected all occurrences to be replaced, got:\n%s", got)
}
if !strings.Contains(got, newMod) {
t.Fatalf("expected replacement to be applied, got:\n%s", got)
}
}

func TestReplaceTemplateContentPreservesProtobufRawDescWithCRLF(t *testing.T) {
const oldMod = "github.com/example/template"
const newMod = "github.com/example/service"

input := strings.Join([]string{
"package conf",
"",
"import dep \"" + oldMod + "/api/greeter/v1\"",
"",
"const file_conf_conf_proto_rawDesc = \"\" +",
"\t\"\\n\" +",
"\t\"\\x0fconf/conf.proto\\x12\\n\" +",
"\t\"kratos.apiB7Z5" + oldMod + "/internal/conf;confb\\x06proto3\"",
"",
"var wireImport = \"" + oldMod + "/internal/server\"",
"",
}, "\r\n")

got := string(replaceTemplateContent([]byte(input), []string{oldMod, newMod}))

if !strings.Contains(got, "import dep \""+newMod+"/api/greeter/v1\"\r\n") {
t.Fatalf("expected CRLF import path to be replaced, got:\n%s", got)
}
if !strings.Contains(got, "var wireImport = \""+newMod+"/internal/server\"\r\n") {
t.Fatalf("expected CRLF regular string to be replaced, got:\n%s", got)
}
if !strings.Contains(got, `B7Z5`+oldMod+`/internal/conf;confb\x06proto3"`) {
t.Fatalf("expected CRLF protobuf raw descriptor to stay untouched, got:\n%s", got)
}
if strings.Contains(got, `B7Z5`+newMod+`/internal/conf;confb\x06proto3"`) {
t.Fatalf("CRLF protobuf raw descriptor was unexpectedly rewritten:\n%s", got)
}
}

func TestReplaceTemplateContentReplacesLegacyProtobufByteDesc(t *testing.T) {
const oldMod = "github.com/example/template"
const newMod = "github.com/example/service"

input := strings.Join([]string{
"package conf",
"",
"var file_conf_conf_proto_rawDesc = []byte{",
"\t0x42, 0x37, 0x5a, 0x35, // B7Z5",
"}",
"",
"const legacyPath = \"" + oldMod + "/internal/conf;conf\"",
"",
}, "\n")

got := string(replaceTemplateContent([]byte(input), []string{oldMod, newMod}))

if !strings.Contains(got, `const legacyPath = "`+newMod+`/internal/conf;conf"`) {
t.Fatalf("expected legacy protobuf file content to still be replaced, got:\n%s", got)
}
if strings.Contains(got, `const legacyPath = "`+oldMod+`/internal/conf;conf"`) {
t.Fatalf("legacy protobuf replacement did not occur, got:\n%s", got)
}
}