Skip to content
Merged
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
65 changes: 64 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,66 @@ GRPC_BatchGetPlayletInfo
# 如果你的代码让更多的服务受到了影响,veronica会将它输出到这里
```

## 配置文件
veronica在运行时,会在项目跟目录下寻找`veronica.yaml`配置文件, 当前版本的配置文件主要由以下部分组成:

`version`: 指定veronica配置文件的版本,当前项目还处于早期阶段,变动较大,未来更新可能导致配置文件的语法产生变化,因此使用版本号来进行区分。

```yaml
version: 1.0.0
```

`services`: services下可以定义一系列的service item,通常来说,每个service item都是一个服务,比如CronJob进程、消费者进程、对外提供HTTP或RPC的进程。
> 但services并不局限于服务,它可以是任何你想要让veronica关注的内容(你将会在下面的entrypoint部分来了解它)

```yaml
services:
# refresh_playlet_info 是一个CronJob进程
refresh_playlet_info:
entrypoint: ...
# update_playlet 是一个消费者进程
update_playlet:
entrypoint: ...
# playlet_server是一个对外提供RPC服务的进程
playlet_server:
entrypoint: ...
```

`entrypoint`: 每个service都需要有一个entrypoint,entrypoint一般是该服务的入口,它可能是个函数,可能是个方法,可能是个变量。
但entrypoint并不局限于此,它甚至可以是常量/类型声明,只要是你想让veronica关注的内容,都可以写进services里:

```yaml
services:
refresh_playlet_info:
# NewRefreshPlayletInfoCronjob 这个函数是CronJob进程的入口
entrypoint: 'cmd/cron/refresh_playlet_info.go:NewRefreshPlayletInfoCronjob'

update_playlet:
# UpdatePlaylet 是个变量(&cobra.Command),是消费者进程的入口,通过cobra.AddCommand绑定到root上进行执行
entrypoint: 'github.com/bootun/some-project/cmd/consumer/update_playlet.go:UpdatePlaylet'

# or gRPC interface
# GetPlayletInf是一个gRPC接口实现
GRPC_GetPlayletInfo:
# GetPlayletInfo 是一个方法,它的签名如下:
# func (svc *PlayletServer) GetPlayletInfo(ctx context.Context, req *pb.GetPlayletInfoReq) (*pb.PlayletBaseInfo, error)
# 如果你也想关注本次改动对RPC的影响,你可以像这样将RPC接口也纳入veronica的管控
entrypoint: "internal/server/grpc.go:(*PlayletServer).GetPlayletInfo"

# 除此之外,service还支持类型声明(结构体)、常量等顶级字段的依赖分析
# ...
```
entrypoint的值为你想要关注对象的包路径,比如你的Go module name是`github.com/bootun/some-project`,
那么你的entrypoint可以是: `github.com/bootun/some-project/cmd/consumer/update_playlet.go:UpdatePlaylet`。
你也可以使用简略版的相对包名, 例如`cmd/consumer/update_playlet.go:UpdatePlaylet`来表示,veronica会自动为你添加前缀。

如果你关注的对象是个方法(method), 你需要写出他的receiver,就像上面示例中`GRPC_GetPlayletInfo`那样:
```yaml
GRPC_GetPlayletInfo:
# func (svc *PlayletServer) GetPlayletInfo(ctx context.Context, req *pb.GetPlayletInfoReq) (*pb.PlayletBaseInfo, error)
entrypoint: "internal/server/grpc.go:(*PlayletServer).GetPlayletInfo"
```

## 可配置项

**输出源代码变更可能会产生的全部影响**
Expand Down Expand Up @@ -58,7 +118,10 @@ remove (*playletService).setPlayletCacheInfo in github.com/bootun/some-project/i
```
该命令会详细告诉你对那些内容做了哪些操作(add/modify/remove), 并报告该修改产生的影响。


## 未来规划
1. 当前GRPC这种方式对超多接口的项目来说,需要配置非常多的service,veronica计划改进这一点。
2. 当前veronica只能分析go文件带来的影响,接下来我计划实现service的`hooks`和`ignores`字段,使任意文件的改动都能与service进行关联。
3. veronica输出变更产生的影响时,计划增加对Go模版语法的支持。

## 命名背景
`Veronica`取自钢铁侠的同名外太空支援系统,在你需要升级战甲时,只需要通知维罗妮卡,它就会将战甲的模块从外太空发送给你,重新组合后完成升级。
Expand Down
4 changes: 3 additions & 1 deletion astdiff/astdiff.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,9 @@ func astNodesEqual(a, b ast.Node) bool {
}
}
return true

case *ast.TypeSwitchStmt:
y := b.(*ast.TypeSwitchStmt)
return astNodesEqual(x.Init, y.Init) && astNodesEqual(x.Assign, y.Assign) && astNodesEqual(x.Body, y.Body)
default:
panic(fmt.Sprintf("未处理的节点类型: %T, a: %v, b: %v\n", x, a, b))
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/spf13/cobra"
)

const Version = "1.0.1"
const Version = "1.0.3"

var versionCmd = &cobra.Command{
Use: "version",
Expand Down
19 changes: 16 additions & 3 deletions parser/project.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package parser

import (
"strings"

"github.com/pkg/errors"

"github.com/bootun/veronica/config"
Expand Down Expand Up @@ -40,19 +42,30 @@ func NewProject(root string) (*project, error) {
if err != nil {
return nil, errors.WithMessage(err, "failed to parse go.mod")
}
if module == nil {
return nil, errors.New("invalid go.mod file")
}
moduleName := module.Name
if moduleName == "" {
return nil, errors.New("invalid go.mod file, module name is empty")
}
services := make(map[string]Service)
// initialize entrypoint
ignores := make(map[string][]string)
hooks := make(map[string][]string)
for _, v := range cfg.Services {
fullRelPath := rootPath.Join(v.Entrypoint)
entrypoint := v.Entrypoint
if !strings.HasPrefix(entrypoint, moduleName) {
entrypoint = moduleName + "/" + entrypoint
}
fullRelPath := rootPath.Join(entrypoint)
relPath, err := fullRelPath.Rel(root)
if err != nil {
return nil, errors.WithMessage(err, "failed to get relative path")
}
services[v.Entrypoint] = Service{
services[entrypoint] = Service{
Name: v.Name,
Entrypoint: v.Entrypoint,
Entrypoint: entrypoint,
Ignores: v.Ignores,
Hooks: v.Hooks,
}
Expand Down
7 changes: 4 additions & 3 deletions veronica_example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 1.0.0-alpha
services:
# every item is a service
refresh_playlet_info:
entrypoint: 'github.com/bootun/some-project/cmd/cron/refresh_playlet_info.go:NewRefreshPlayletInfoCronjob'
entrypoint: 'cmd/cron/refresh_playlet_info.go:NewRefreshPlayletInfoCronjob'

# the current version does not currently support it
#ignores:
Expand All @@ -12,13 +12,14 @@ services:
# - 'go.mod'

update_playlet:
# or use the full package path
entrypoint: 'github.com/bootun/some-project/cmd/consumer/update_playlet.go:UpdatePlaylet'

# or gRPC interface
GRPC_GetPlayletInfo:
entrypoint: "github.com/bootun/some-project/internal/server/grpc.go:(*PlayletServer).GetPlayletInfo"
entrypoint: "internal/server/grpc.go:(*PlayletServer).GetPlayletInfo"
GRPC_BatchGetPlayletInfo:
entrypoint: "github.com/bootun/some-project/internal/server/grpc.go:(*PlayletServer).BatchGetPlayletInfo"
entrypoint: "internal/server/grpc.go:(*PlayletServer).BatchGetPlayletInfo"

# or variable declare / type declare ...
# ...