前言

Github是全球最大的开源代码托管平台,有很多的著名开源项目都将代码托管到Github上,例如 Linux, React 以及 Redis 等具有极高知名度的项目。

Go 是由Google主导开发的一门编程语言,其源代码同样托管在Github上,并且使用Go语言开发的 kubernetes, etcd 以及 istio 等项目也将代码托管在Github。所以作为Go开发者,不可避免的要和Github产生交互,本文谨试谈作者在Github上使用Go的一些体会。

本文只是作者个人的经验,欢迎各位大佬指正。

Github与GOPATH

Golang使用GOPATH作为工作区的指定目录,当我们的项目在Github上托管时,我们经常将项目代码clone到GOPATH的子目录下,例如对于 autoscaler 这个项目:

>> echo $GOPATH
/Users/xxx/workspace/golang/lib:/Users/xxx/workspace/golang

>> cd /Users/xxx/workspace/golang/src/k8s.io
>> git clone https://github.com/kubernetes/autoscaler.git

至于为什么是使用k8s.io而不是github.com,是因为代码中使用的import路径为 k8s.io。基本的原则是: 需要将代码 clone 到import指定的路径下。

当使用Fork时

如果我们要参与autoscaler这个项目的开发,采取的步骤如下:

  1. 在github上fork该项目
  2. 使用前述的方式将代码clone到本地

>> git clone https://github.com/${YOUR_REPO_NAME}/autoscaler.git

3. 为项目添加upstream

>> git remote -v

origin git@github:comLxxx/autoscaler.git (fetch)
origin [email protected]:xxx/autoscaler.git (push)
upstream [email protected]:kubernetes/autoscaler.git (fetch)
upstream [email protected]:kubernetes/autoscaler.git (push)

之后继续采用原有的开发模式即可。在这种方式下不用对import的路径进行任何更改。

目录结构

在使用Go语言进行开发过程中,并没有强制规定的代码结构。但是为了更清晰的表名各目录的作用,一种推荐的代码结构如下:

>> tree -L 1

.
├── Gopkg.lock
├── Gopkg.toml
├── LICENSE
├── Makefile
├── README.md
├── build
├── cmd
├── codecov.yml
├── docs
├── hack
├── pkg
└── vendor

各个目录及文件的简要介绍如下:

  1. Gopkg*: 使用 dep 进行依赖管理时的重要文件,需要提交到 github 进行管理
  2. Makefile: 构建脚本,下一节会详细描述
  3. build: 存放一些构建相关的配置脚本
  4. cmd: 命令文件存放目录, 一般为程序的入口, 下面可能有多个子目录
  5. docs: 文档
  6. hack: 一些脚本, 一般是升级工具/测试工具一类
  7. pkg: 项目使用的代码包目录
  8. vendor: 各依赖包,可托管到github
  9. bin: 生成的可执行文件的目录

Makefile

make 是一个控制从程序的源文件生成可执行文件和其他非源文件的工具,可以从一个名为Makefile的文件中获得如何构建程序的知识,并管理编译过程中的各种依赖关系和规则。

我们可以使用 make 对Go程序的构建,测试和发布等进行管理,一个示例脚本如下:

# Current version of the project
VERSION ?= 1.0.0
GIT_SHA=$(shell git rev-parse --short HEAD)
TAGS=$(GIT_SHA)

# This repos root import path (under GOPATH)
ROOT := github.com/lsytj0413/xxx

# Target binaries. You can build multiple binaries for a single project
TARGETS := xxx

# A list of all packages
PKGS := $(shell go list ./... | grep -v /vendor | grep -v /test)

# Project main package location (can be multiple ones)
CMD_DIR := ./cmd

# Project output directory
OUTPUT_DIR := ./bin

# Build directory
BUILD_DIR := ./build

# Git commit sha
COMMIT := $(shell git rev-parse --short HEAD)

# Golang standard bin directory
BIN_DIR := $(firstword $(subst :, ,$(GOPATH)))/bin
GOMETALINTER := $(BIN_DIR)/gometalinter
GODEP := $(BIN_DIR)/dep

#
# all targets
#

.PHONY: clean lint test build dep

all: test build

# TODO: if vendor exists skip ensure?
dep: $(GODEP)
@if [ ! -d ./vendor ]; then
dep ensure;
else
echo "vendor exists, skip dep ensure";
fi
$(GODEP):
go get -u -v github.com/golang/dep/cmd/dep

test: dep
# go test $(PKGS)
@for pkg in $(PKGS); do
go test $${pkg};
done

build: build-local

build-local: dep
@for target in $(TARGETS); do
go build -i -v -o $(OUTPUT_DIR)/$${target}
-ldflags "-s -w -X $(ROOT)/pkg/version.Version=$(VERSION)
-X $(ROOT)/pkg/version.Commit=$(COMMIT)"
$(CMD_DIR)/$${target};
done

build-docker:
@for target in $(TARGETS); do
docker build --force-rm -t $${target}:$(VERSION)
-f $(BUILD_DIR)/$${target}/Dockerfile .;
done
# docker rmi -f $(shell docker images -q --filter label=stage=intermediate)
docker image prune -f

lint: $(GOMETALINTER)
gometalinter ./... --vendor
$(GOMETALINTER):
go get -u github.com/alecthomas/gometalinter
gometalinter --install &> /dev/null

clean:
-rm -vrf ${OUTPUT_DIR}

常见的构建目标如下:

  1. build: 构建可执行程序
  2. test: 执行测试
  3. build-docker: 构建docker镜像, 其中的dockerfile存放在 build 目录下
  4. push: 将镜像推送到registry
  5. clean: 清理构建过程中的临时文件

推荐阅读:

相关文章