在编写内部项目中,往往不同的项目因为历史遗留问题,会有存在多个版本的 golang 编译器共存的情况。
同时,由于 golang 的包管理的短板(虽然在 golang 1.11 以后推出了 go mod),因此不可避免的安装 govendor、go dep 等多个包管理工具,非常的混乱。
本地使用多个 golang 的编译器版本其实更容易造成混乱,同时 env 满天飞也非常不方面维护。解决这块的问题很容易就想到了使用 Docker 去编译和检查本地项目, Docker 官方维护了多个版本的 golang 镜像。
那么考虑的目标是:
- 使用同一个 Makefile 以及 Dockerfile 去维护项目的编译
- 因为 golang 1.11 以后官方推出了 go mod,所以尽量使用官方的包管理工具
- 接上条,老版本的依赖和本地编译使用 go mod vendor 去管理
- Makefile 不管 golang 编译器的版本问题,编译器版本让 Dockerfile 去管理
- 本地环境尽量使用最新版本的 golang 编译器,然后导入到 vendor 中
那么这样子,我们可以简单的使用 Dockerfile 去编译执行,例如下面使用编译器版本 golang 1.9.7 类似:
FROM golang:1.9.7 AS builder
# ...
RUN go build .
# ...
但需要注意的是,golang 1.11 之前必须代码在 $GOPATH 中,所以需要映射
ENV GOPATH /go
ENV GOROOT /usr/local/go
ENV PACKAGE ${YOUR_PACKAGE_NAME}
ENV BUILD_DIR ${GOPATH}/src/${PACKAGE}
# ...
COPY . ${BUILD_DIR}
然后在本地使用 go mod vendor
下载依赖包到项目的 /vendor
就可以在老版本中免去使用 dep、govendor 等依赖工具,统一使用官方的 go mod
去管理和下载依赖。
还有需要关注的是 Makefile 需要加个判断,为了增加通用性,建议根据路径判断 golang 的环境路径,而不是判断是否在 Docker 环境下:
# ...
ifneq ("$(wildcard /go)","")
GOPATH=/go
GOROOT=/usr/local/go
endif
GO=env $(GO_ENV) $(GOROOT)/bin/go
# ...
build:$(DIR_SRC)/main.go
@$(GO) build $(GO_FLAGS) -o $(BIN) $(DIR_SRC)
这样子 Makefile 文件就可以同时同于本地高版本的环境以及 Docker 编译环境了。
详细的示例代码可以参看 这个简单的项目(作用只是用于生成随机密码),其中的 Makefile 以及 Dockerfile 。
最后,有个讨论 vendor
目录到底要不要纳入到版本控制中?我个人的看法是看情况,大部分情况下不会将这些第三方代码纳入版本控制中。主要的理由有:
- 这些代码是第三方库的代码,不会直接更改以及维护;
- go.mod 以及 go.sum 文件已经保存了第三方依赖的库信息(版本、hash 等等);
- 库文件通常会很多,纳入这些的文件会「污染」本地的 git history;
- 不同于开源项目,我们本地的开发环境是可控的。
所以,类似的 Consul 等比较大型的项目还是会将 vendor
目录纳入到版本控制中,个人认为这也是因为兼容方面的考虑更多些,而如果个本地项目往往环境是可控的。
- eof -