前言

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: 清理構建過程中的臨時文件

推薦閱讀:

相关文章