🐱‍👓PowerProto: gRPC工具链(protoc, protoc-gen-go)的一键安装与版本控制

goreport.png

lastcommit.png

godoc.png

中文 | English

exmaple.gif

项目地址::tada: An awesome version control tool for protoc and its related plugins. (github.com)

PowerProto主要用于解决下面三个问题:

  1. 降低gRPC的使用门槛与使用成本。

  2. 解决protoc以及其相关插件(比如protoc-gen-go、protoc-gen-grpc-gateway)的版本控制问题。

  3. 高效管理proto的编译,实现多平台兼容、一键安装与编译。

:tada: 功能

  1. 实现protoc的一键安装与多版本管理。

  2. 实现protoc相关插件(比如protoc-gen-go)的一键安装与多版本管理。

  3. 通过配置文件管理proto的编译,而非shell脚本,提高可读性与兼容性。

  4. 引导式生成配置文件,跨平台兼容,一份配置在多个平台均可以实现一键编译。

  5. 支持批量、递归编译proto文件,提高效率。

  6. 跨平台支持PostAction,可以在编译完成之后执行一些常规操作(比如替换掉所有生成文件中的"omitempty")。

  7. 支持PostShell,在编译完成之后执行特定的shell脚本。

  8. 支持 google api, gogo protobuf 等的一键安装与版本控制。

安装与依赖

  1. 目前版本的 PowerProto 依赖 go 以及 git(未来可能会直接使用CDN拉取构建好的二进制),请确保运行环境中包含这两个命令。

  2. protoc的下载源是Github,PowerProto在下载protoc时尊重 HTTP_PROXYHTTPS_PROXY环境变量,如果遇到网络问题,请自行配置代理。

  3. 在查询protoc的版本列表时,会对github.com使用git ls-remote,如果遇到网络问题,请自行为git配置代理。

  4. 在当前版本,下载和查询插件版本均依赖go命令,所以如果遇到网络问题,请自行配置 GOPROXY环境变量。

  5. 默认会使用 用户目录/.powerproto作为安装目录,用于放置下载好的各种插件以及全局配置,可以通过 POWERPROTO_HOME环境变量来修改安装目录。

  6. 如果认为powerproto名字太长,可以通过alias成一个更简单的名字来提高输入效率,比如没有人会介意你叫它pp

一、通过Go进行安装

直接执行下面的命令即可进行安装:


go install github.com/storyicon/powerproto/cmd/powerproto@latest

二、开箱即用版本

可以通过 Github Release Page 下载开箱即用版本。

命令介绍

你可以通过 powerproto -h 来查看帮助,比如:


powerproto -h

powerproto init -h

powerproto tidy -h

powerproto build -h

powerproto env -h

它的好处是命令行中的文档永远和你的二进制版本保持一致。而Github上的文档可能会一直是对应最新的二进制。

一、初始化配置

可以通过下面的命令进行配置的初始化:


powerproto init

二、整理配置

可以通过下面的命令整理配置:


powerproto tidy

它将会从当前目录开始向父级目录搜索名为 powerproto.yaml 的配置文件,并对配置进行读取和整理。

你也可以指定整理哪个配置文件:


powerproto tidy [the path of proto file]

整理配置主要包含两个操作:

  1. 通过查询,将版本中的latest替换为真实的最新版本号。

  2. 安装配置文件中定义的所有依赖。

支持通过 -d 参数来进入到debug模式,查看更详细的日志。

三、编译Proto文件

可以通过下面的命令进行Proto文件的编译:


// 编译指定的proto文件

powerproto build xxxx.proto

// 编译当前目录下的所有proto文件

powerproto build .

// 递归编译当前目录下的所有proto文件,包括子文件夹。

powerproto build -r .

其执行逻辑是,对于每一个proto文件,从其文件所在目录开始向父级目录寻找 powerproto.yaml 配置文件:

  1. 对于找到的配置文件,与其中的scope进行匹配,如果匹配则采用。

  2. 检查并安装配置文件中声明的依赖。

  3. 根据配置文件中的pluginsprotocoptionsimportPaths等配置对proto文件进行编译。 当所有的proto文件都编译完成之后,如果你指定了 -p 参数,还会进行PostActionPostShell的执行。

注意:protoc执行的工作目录默认是proto文件匹配到的配置文件所在的目录,它相当于你在配置文件所在目录执行protoc命令。你可以通过配置文件中的 protocWorkDir 来进行修改。

支持通过 -d 参数来进入到debug模式,查看更详细的日志。

支持通过 -y 参数来进入到dryRun模式,只打印命令而不真正执行,这对于调试非常有用。

四、查看环境变量

如果你的命令一直卡在某个状态,大概率是出现网络问题了。

你可以通过下面的命令来查看环境变量是否配置成功:


powerproto env

示例

比如你在 /mnt/data/hello 目录下拥有下面这样的文件结构:


$ pwd

/mnt/data/hello

$ tree

./apis

└── hello.proto

powerproto.yaml

powerproto.yaml 的文件内容是(你可以通过 powerproto init 命令很方便的生成配置文件):


scopes:

    - ./

protoc: latest

protocWorkDir: ""

plugins:

    protoc-gen-go: google.golang.org/protobuf/cmd/protoc-gen-go@latest

    protoc-gen-go-grpc: google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

repositories:

    GOOGLE_APIS: https://github.com/googleapis/googleapis@75e9812478607db997376ccea247dd6928f70f45

options:

    - --go_out=.

    - --go_opt=paths=source_relative

    - --go-grpc_out=.

    - --go-grpc_opt=paths=source_relative

importPaths:

    - .

    - $GOPATH

    - $POWERPROTO_INCLUDE

    - $GOOGLE_APIS/github.com/googleapis/googleapis

postActions: []

postShell: ""

在任意目录执行:


powerproto build -r /mnt/data/hello/apis

你都可以得到编译后的文件


$ pwd

/mnt/data/hello

$ tree

./apis

├── hello.pb.go

├── hello.proto

└── hello_grpc.pb.go

powerproto.yaml

它相当于你在 powerproto.yaml 所在目录,执行:


$POWERPROTO_HOME/protoc/3.17.3/protoc --go_out=. \

--go_opt=paths=source_relative \

--go-grpc_out=. \

--go-grpc_opt=paths=source_relative \

--proto_path=/mnt/data/hello \

--proto_path=$GOPATH \

--proto_path=$POWERPROTO_HOME/include \

--proto_path=$POWERPROTO_HOME/gits/75e9812478607db997376ccea247dd6928f70f45/github.com/googleapis/googleapis \

--plugin=protoc-gen-go=$POWERPROTO_HOME/plugins/google.golang.org/protobuf/cmd/protoc-gen-go@v1.27.1/protoc-gen-go \

--plugin=protoc-gen-go-grpc=$POWERPROTO_HOME/plugins/google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1.0/protoc-gen-go-grpc

/mnt/data/hello/apis/hello.proto

配置文件

配置文件用于描述编译proto文件时,各种依赖的版本以及参数等。

可以方便的通过 powerproto init进行配置文件的初始化。

解释

以下面这份配置文件为例:


# 必填,scopes 用于定义作用域,即当前配置项对项目中的哪些目录生效

scopes:

    - ./

# 必填,protoc的版本,可以填 latest,会自动转换成最新的版本

protoc: 3.17.3

# 选填,执行protoc命令的工作目录,默认是配置文件所在目录

# 支持路径中混用环境变量,比如$GOPATH

protocWorkDir: ""

# 选填,定义依赖的Git存储库

# 一般用于公共的protobuf库的依赖控制

repositories:

    # 定义依赖 27156597fdf4fb77004434d4409154a230dc9a32 版本的 https://github.com/googleapis/googleapis

    # 并且定义其名字为 GOOGLE_APIS

    # 在 importPaths 中可以通过 $GOOGLE_APIS 来引用它

    GOOGLE_APIS: https://github.com/googleapis/googleapis@27156597fdf4fb77004434d4409154a230dc9a32

    # 定义依赖 226206f39bd7276e88ec684ea0028c18ec2c91ae 版本的 https://github.com/gogo/protobuf

    # 并且定义其名字为 GOGO_PROTOBUF

    # 在 importPaths 中可以通过 $GOGO_PROTOBUF 来引用它

    GOGO_PROTOBUF: https://github.com/gogo/protobuf@226206f39bd7276e88ec684ea0028c18ec2c91ae

# 必填,代表scope匹配的目录中的proto文件,在编译时需要用到哪些插件

plugins:

    # 插件的名字、路径以及版本号。

    # 插件的地址必须是 path@version 的格式,version可以填latest,会自动转换成最新的版本。

    protoc-gen-deepcopy: istio.io/tools/cmd/protoc-gen-deepcopy@latest

    protoc-gen-go: google.golang.org/protobuf/cmd/protoc-gen-go@latest

    protoc-gen-go-json: github.com/mitchellh/protoc-gen-go-json@v1.0.0

    protoc-gen-grpc-gateway: github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@v2.5.0

# 必填,定义了编译proto文件时 protoc 的参数

options:

    - --go_out=paths=source_relative:.

    - --go-json_out=.

    - --deepcopy_out=source_relative:.

    - --grpc-gateway_out=.

    - --go-grpc_out=paths=source_relative:.

# 必填,定义了构建时 protoc 的引用路径,会被转换为 --proto_path (-I) 参数。

importPaths:

    # 特殊变量。代表当前配置文件所在文件夹

    - .

    # 环境变量。可以使用环境变量

    # 也支持 $GOPATH/include 这样的混合写法

    - $GOPATH

    - $POWERPROTO_INCLUDE

    # 特殊变量。引用待编译的proto文件所在的目录

    # 比如将要编译 /a/b/data.proto,那么 /a/b 目录将会被自动引用

    - $SOURCE_RELATIVE

    # 引用 repositories 中的 GOOGLE_APIS

    - $GOOGLE_APIS/github.com/googleapis/googleapis

    # 引用 repositories 中的 GOGO_PROTOBUF

    - $GOGO_PROTOBUF

# 选填,构建完成之后执行的操作,工作目录是配置文件所在目录

# postActions是跨平台兼容的

# 注意,必须在 powerproto build 时附加 -p 参数,才会执行配置文件中的postActions

postActions: []

# 选填,构建完成之后执行的shell脚本,工作目录是配置文件所在目录

# postShell不是跨平台兼容的。

# 注意,必须在 powerproto build 时附加 -p 参数,才会执行配置文件中的postShell

postShell: |

    // do something

匹配模式与工作目录

在构建proto文件时,将会从proto文件所在目录开始,向父级目录搜索 powerproto.yaml 配置文件,并与其中的 scope进行匹配,第一个匹配到的配置,将会被用于此proto文件的编译。

在执行protoc时(执行postActions、postShell时也是如此),是以配置文件所在目录作为工作目录的,即相当于你在这个目录执行protoc命令。

多配置组合

一个配置文件中支持填写多份配置,多份配置之间以 “—” 进行分割。

在下面的示例中,apis1目录使用的是v1.25.0的protoc-gen-go,而apis2目录使用的则是v1.27.0的protoc-gen-go。


scopes:

    - ./apis1

protoc: v3.17.3

protocWorkDir: ""

plugins:

    protoc-gen-go: google.golang.org/protobuf/cmd/protoc-gen-go@v1.25.0

    protoc-gen-go-grpc: google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1.0

repositories:

    GOOGLE_APIS: https://github.com/googleapis/googleapis@75e9812478607db997376ccea247dd6928f70f45

options:

    - --go_out=.

    - --go_opt=paths=source_relative

    - --go-grpc_out=.

    - --go-grpc_opt=paths=source_relative

importPaths:

    - .

    - $GOPATH

    - $POWERPROTO_INCLUDE

    - $GOOGLE_APIS/github.com/googleapis/googleapis

postActions: []

postShell: ""

---

scopes:

    - ./apis2

protoc: v3.17.3

protocWorkDir: ""

plugins:

    protoc-gen-go: google.golang.org/protobuf/cmd/protoc-gen-go@v1.27.0

    protoc-gen-go-grpc: google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1.0

repositories:

    GOOGLE_APIS: https://github.com/googleapis/googleapis@75e9812478607db997376ccea247dd6928f70f45

options:

    - --go_out=.

    - --go_opt=paths=source_relative

    - --go-grpc_out=.

    - --go-grpc_opt=paths=source_relative

importPaths:

    - .

    - $GOPATH

    - $POWERPROTO_INCLUDE

    - $GOOGLE_APIS/github.com/googleapis/googleapis

postActions: []

postShell: ""

PostAction

PostAction允许在所有的proto文件都编译完成之后,执行特定的操作。与PostShell相比,它是跨平台支持的。

为了安全起见,只有在执行 powerproto build时附加上 -p 参数,才会执行配置文件中定义的PostActionPostShell

目前,PostAction支持下面这些命令:

| 命令 | 描述 | 函数原型 |

| ------- | ---------------------- | ----------------------------------------------------- |

| copy | 复制文件或文件夹 | copy(src string, dest string) error |

| move | 移动文件或文件夹 | move(src string, dest string) error |

| remove | 删除文件或文件夹 | remove(path …string) error |

| replace | 批量替换文件中的字符串 | replace(pattern string, from string, to string) error |

1. copy

用于复制文件或文件夹,其函数原型为:


copy(src string, dest string) error

为了安全以及配置的兼容性,参数中只允许填写相对路径。

如果目标文件夹已经存在,将会合并。

下面的例子将会把配置文件所在目录下的a复制到b:


postActions:

    - name: copy

      args:

        - ./a

        - ./b

2. move

用于移动文件或文件夹,其函数原型为:


move(src string, dest string) error

为了安全以及配置的兼容性,参数中只允许填写相对路径。

如果目标文件夹已经存在,将会合并。

下面的例子将会把配置文件所在目录下的a移动到b:


postActions:

    - name: move

      args:

        - ./a

        - ./b

3. remove

用于删除文件或文件夹,其函数原型为:


remove(path ...string) error

为了安全以及配置的兼容性,参数中只允许填写相对路径。

下面的例子将会删除配置文件所在目录下的a、b、c:


postActions:

    - name: remove

      args:

        - ./a

        - ./b

        - ./c

4. replace

用于批量替换文件中的字符串,其函数原型为:


replace(pattern string, from string, to string) error

其中:

  • pattern是支持通配符的相对路径。

  • from是要被替换的字符串。

  • to是替换为的字符串。

下面的例子将会把apis目录以及其子目录下的所有go文件中的 ,omitempty 替换为空字符串:


postActions:

    - name: replace

      args:

        - ./apis/**/*.go

        - ',omitempty'

        - ""