使用 go modules 将 retool 替换为 tools.go 用于多开发人员和 CI 环境

replace retool with tools.go for multi developer and CI environments using go modules

我想替换 retool with go modules tools.go "tools as dependencies"。但是,当我的开发人员和 CI 环境都使用不同的操作系统时,我很难理解这是如何工作的。

我想确保每个环境都使用完全相同版本的工具。

举个具体的例子,我的应用需要protoc编译器通过github.com/golang/protobuf/protoc-gen-go生成go代码。我有 3 个 OS,都需要用 protoc-gen-go plugin/generator:

执行协议
  1. Ryan:使用 MacOS
  2. 乔:Linux (Ubuntu)
  3. CI: Linux (分OS)

我目前使用 retool 来确保所有环境都锁定在同一版本的工具上(本示例中的 protoc-gen-go):

retool do build/bin/protoc -Ibuild/protoc/include -I. rpc/platform/platform.proto --go_out=.

我的新模块/"tools as dependencies"设置

tools.go:

// +build tools

package tools

import (
    _ "github.com/golang/protobuf/protoc-gen-go"
)

设置go install将使用的路径:

export GOBIN=$PWD/bin

安装:

go install github.com/golang/protobuf/protoc-gen-go

如果 Ryan 运行 go install ..,则会创建一个 bin/protoc-gen-go MacOS 可执行文件。

问题:

  1. 此时,为什么 protoc-gen-go 工具版本(或 git 哈希值)未列在 go.mod 中?
  2. 当 Joe 克隆应用程序存储库时,他如何获取并编译 Ryan 使用的相同版本的 protoc-gen-go?
  3. protoc 怎么知道在我的 ./bin 目录中使用 protoc-gen-go 可执行生成器?

Go 模块与 .go 文件的导入一起使用。如果他们找到导入,他们将自动下载满足您要求的最新版本。你必须阅读 https://github.com/golang/go/wiki/Modules 并了解自 Go 1.11 及更高版本以来模块的工作方式。

At this point, why is protoc-gen-go tool version (or git hash) NOT listed in go.mod?

这是因为 protoc-gen-go 就 Go 模块而言只是一个外部工具。您不导入 golang/protobuf/tree/master/protoc-gen-go 但它生成的代码。

When Joe clones the app repo, how does he get and compile the same version of protoc-gen-go that Ryan used?

使用:

GIT_TAG="v1.2.0" # change as needed
go get -d -u github.com/golang/protobuf/protoc-gen-go
git -C "$(go env GOPATH)"/src/github.com/golang/protobuf checkout $GIT_TAG
go install github.com/golang/protobuf/protoc-gen-go

在用户的每台机器上安装特定版本。可能会编写一个自动化该过程的构建脚本。

How does protoc know to use the protoc-gen-go executable generator in my ./bin dir?

来自 github 文档: 除非设置了 $GOBIN,否则编译器插件 protoc-gen-go 将安装在 $GOPATH/bin 中。它必须在您的 $PATH 中,协议编译器 protoc 才能找到它。

我能够完成 protoc 的供应商工具构建(以及像 Twirp) following the Go Modules tools guidelines, plus a little Makefile-Fu for the protoc 二进制文件这样的插件。

可以在 Aspiration Labs pyggpot repo 中找到完整的工作示例。以下是基本细节。值得注意的是:为某些工具设置正确的导入路径非常繁琐,但最终还是成功了。

对于 protoc 本身,我在 Makefile 中提供二进制版本并将其设置到 tools/bin 目录中:

TOOLS_DIR := ./tools
TOOLS_BIN := $(TOOLS_DIR)/bin

# protoc
PROTOC_VERSION := 3.7.1
PROTOC_PLATFORM := osx-x86_64
PROTOC_RELEASES_PATH := https://github.com/protocolbuffers/protobuf/releases/download
PROTOC_ZIP := protoc-$(PROTOC_VERSION)-$(PROTOC_PLATFORM).zip
PROTOC_DOWNLOAD := $(PROTOC_RELEASES_PATH)/v$(PROTOC_VERSION)/$(PROTOC_ZIP)
PROTOC := $(TOOLS_BIN)/protoc

# protoc
$(PROTOC): $(TOOLS_DIR)/$(PROTOC_ZIP)
    unzip -o -d "$(TOOLS_DIR)" $< && touch $@  # avoid Prerequisite is newer than target `tools/bin/protoc'.

$(TOOLS_DIR)/$(PROTOC_ZIP):
    curl --location $(PROTOC_DOWNLOAD) --output $@

PROTOC_PLATFORM 字符串可以用 OS detecting makefile. The version of that we use is at https://github.com/aspiration-labs/pyggpot/blob/master/build/makefiles/osvars.mk.

之类的东西自动化

开始构建 go 工具。创建一个 tools.go 类似

的东西
// +build tools

package tools

import (
    // protocol buffer compiler plugins
    _ "github.com/golang/protobuf/protoc-gen-go"
    _ "github.com/twitchtv/twirp/protoc-gen-twirp"
    _ "github.com/twitchtv/twirp/protoc-gen-twirp_python"
    _ "github.com/thechriswalker/protoc-gen-twirp_js"
)

注意:// +build tools 标签将保留 go build 从 over-building 工具导入到您的最终版本中。

最后,一些 make 代码来构建你的 go 工具:

# go installed tools.go
GO_TOOLS := github.com/golang/protobuf/protoc-gen-go \
            github.com/twitchtv/twirp/protoc-gen-twirp \
            github.com/twitchtv/twirp/protoc-gen-twirp_python \
            github.com/thechriswalker/protoc-gen-twirp_js \

# tools
GO_TOOLS_BIN := $(addprefix $(TOOLS_BIN), $(notdir $(GO_TOOLS)))
GO_TOOLS_VENDOR := $(addprefix vendor/, $(GO_TOOLS))

setup_tools: $(GO_TOOLS_BIN)

$(GO_TOOLS_BIN): $(GO_TOOLS_VENDOR)
    GOBIN="$(PWD)/$(TOOLS_BIN)" go install -mod=vendor $(GO_TOOLS)

最后,make setup 目标到 运行 go mod vendor 并处理上面的目标。

setup: setup_vendor $(TOOLS_DIR) $(PROTOC) setup_tools

# vendor
setup_vendor:
    go mod vendor

$(TOOLS_DIR):
    mkdir -v -p $@