演进维度:研发模式(瀑布模式,迭代模式,敏捷模式) -> CI/CD -> DevOps。
软件生命周期:
角色分工:
# 需求阶段
参与产品讨论,详细了解需求,为后续开发打下基础。
通过需求评审后,输出详细的需求文档。
# 设计阶段
包括产品设计、交互设计、视觉设计、技术设计、技术评审、需求排期。
开发人员需要做好调研、设计领先的技术架构,技术方案获得团队一致通过。
# 开发阶段
根据项目规模和协作模式,选择采用 Git Flow 工作流。
代码开发阶段基本流程包括:创建 feature 分支 -> 完成开发工作 -> 生成代码 -> 版权检查 -> 代码格式化 -> 静态检查 -> 单元测试 -> 构建二进制文件(可执行 make
命令一键完成)。
首先在 master 分支上创建常驻的 develop 分支,并基于 develop 分支 新建 feature 分支:
git checkout -b feature/helloworld develop
分支名称需符合 Git Flow 命名规范,否则 githooks 会导致 commit 失败(参考 iam/pre-commit (opens new window)):
master
develop
feature + "/" + x.y.z-a
release
hotfix
2
3
4
5
git 不会提交 .git/hooks 下的 githooks 脚本,需要通过以下手段确保开发者 clone 仓库后,仍然能安装指定的 githooks 脚本到 .git/hooks 目录(参考 iam/common.mk (opens new window)):
# Copy githook scripts when execute makefile
COPY_GITHOOK:=$(shell cp -f githooks/* .git/hooks/)
2
在 feature 分支 完成开发工作:
# 通过 iamctl new helloworld 命令创建 helloworld 命令模板
iamctl new helloworld -d internal/iamctl/cmd/helloworld
# Command file generated: internal/iamctl/cmd/helloworld/helloworld.go
# 编辑 internal/iamctl/cmd/cmd.go 文件,在源码文件中添加helloworld.NewCmdHelloworld(f, ioStreams),,加载 helloworld 命令。
# import (
# "github.com/marmotedu/iam/internal/iamctl/cmd/helloworld"
# )
# ...
# {
# Message: "Troubleshooting and Debugging Commands:",
# Commands: []*cobra.Command{
# validate.NewCmdValidate(f, ioStreams),
# helloworld.NewCmdHelloworld(f, ioStreams),
# },
# },
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
使用 make 生成代码(比如清理先前的文件再重新生成),要求操作具有幂等性:
make gen # gen.run: gen.clean gen.errcode gen.docgo.doc
针对开源软件需进行 版权检查,检查新文件是否添加版权头信息:
make verify-copyright
# 如果检查失败,可只需 make add-copyright 自动添加。
2
如果 Makefile 的 command 需要某个命令,可以使该目标依赖类似 tools.verify.addlicense 的目标,可检查该工具是否已安装,没有就先安装。
.PHONY: copyright.verify
copyright.verify: tools.verify.addlicense
...
tools.verify.%:
@if ! which $* &>/dev/null; then $(MAKE) tools.install.$*; fi
.PHONY: install.addlicense
install.addlicense:
@$(GO) get -u github.com/marmotedu/addlicense
2
3
4
5
6
7
8
代码格式化:依次执行 gofmt、goimports(自动增删依赖的包,并将依赖包按字母序排序并分类)、golines(超过 120 行的代码按规则折行处理)等,并使用 go mod edit -fmt
格式化 go.mod 文件。
make format
静态检查:
make lint
单元测试:注意检查测试覆盖率,达不到预期阈值就需要补充测试用例,否则禁止合并到 develop 和 master 分支(也可由 GitHub Actions CI 等自动化流水线工具检查)。
make test
# 排除不需要单元测试的包。
# go test `go list ./...|egrep -v $(subst $(SPACE),'|',$(sort $(EXCLUDE_TESTS)))`
# 把 mock_.* .go 文件中的函数单元测试信息从 coverage.out 中删除。
# sed -i '/mock_.*.go/d' $(OUTPUT_DIR)/coverage.out
make cover COVERAGE=90
# test coverage is 62.1%
# test coverage does not meet expectations: 90%, please add test cases!
# make[1]: *** [go.test.cover] Error 1
# make: *** [cover] Error 2
2
3
4
5
6
7
8
9
10
11
构建二进制文件:自动构建 cmd/ 目录下的所有组件,也可以构建其中一或多个组件。
make build
# make build BINS="iam-apiserver iamctl"
2
以上步骤可随时根据需要执行,发现问题及时改正。
Makefile 技巧:
随着项目的扩展 Makefile 会加入大量管理功能,其中 help 命令可实现自动解析:
## help: Show this help info.
.PHONY: help
help: Makefile
@echo -e "\nUsage: make <TARGETS> <OPTIONS> ...\n\nTargets:"
# 自动解析 Makefile 中 ## 开头的注释行,从而自动生成 make help 输出。
@sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /'
@echo "$$USAGE_OPTIONS"
2
3
4
5
6
7
变量可以在 Makefile options 中被指定:
ifeq ($(origin COVERAGE),undefined)
COVERAGE := 60
endif
# make COVERAGE=90
2
3
4
其中版本号可使用 gsemver
生成,参考 iam/ensure_tag.sh (opens new window)。
以下步骤则为提交阶段,将代码 提交到 feature 分支(建议只添加与 feature/helloworld 相关的改动,明确一个 commit 所做的变更,方便以后追溯),并 push 到远端仓库。
git add internal/iamctl/cmd/helloworld internal/iamctl/cmd/cmd.go
git commit -m "feat: add new iamctl command 'helloworld'"
git push origin feature/helloworld
2
3
提交时 githooks 会检查 commit message 是否符合规范(利用 go-gitlint,参考 iam/commit-msg (opens new window),也可以自行执行命令检查 go-gitlint
)。
配置 GitHub Actions,当有代码被 Push 时会 触发 CI 流水线。由于保证了线上 CI 流程和本地 CI 流程完全一致。如果 CI 不通过,则需要修改代码直到通过为止。
name: IamCI
on:
push:
branchs:
- '*'
pull_request:
types: [opened, reopened]
jobs:
build:
runs-on: ubuntu-latest
steps:
# 拉取代码
- uses: actions/checkout@v2
# 设置 Go 编译环境
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
# 执行 make 命令
- name: all
run: make
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
提交 pull request:在 GitHub 上基于 feature 创建 PR,并指定 reviewers 进行 CR,此时也会触发 CI 流水线。
code viewer:可选择 Comment、Approve、Request Changes。
- 不通过:直接在 feature/helloworld 分支修正代码,并 push 到远端的 feature/helloworld 分支,再通知 reviewers 再次 review。push 事件发生会再触发 GitHub Actions CI 流水线。
- 通过:maintainer 将新的代码合并到 develop 分支。如选择 Create a merge commit 方式,将 PR 合并到 develop 分支(
git merge --no-ff
,好处是所有的 commit 都会追加到 develop 分支,并生成 merge commit,便于回溯历史)。
合并到 develop 分支后触发 CI 流程。
使用 CHANGELOG 可展示每个版本之间的变更内容,作为 Release Note 的一部分。可以借助 git-chglog 工具来自动生成,配置文件参考:iam/commit-msg (opens new window)
git-chglog v1.0.0 CHANGELOG/CHANGELOG-1.0.0.md
# 测试阶段
对于开发人员,需要基于 develop 分支 创建 release 分支:
git checkout -b release/1.0.0 develop
make
2
将 release/1.0.0 分支代码 提交测试,如测试失败可直接在 release/1.0.0 修复、本地构建并提交:
make
git add internal/iamctl/cmd/helloworld/
git commit -m "fix: fix helloworld print bug"
git push origin release/1.0.0
2
3
4
push 到 release/1.0.0 后,GitHub Actions 会执行 CI 流水线,要求修改直到流水线执行成功为止。由测试人员完成功能测试、性能测试、集成测试、系统测试等。
测试人员根据需求文档创建测试计划、编写测试用例,并与开发人员一起评审测试计划和用例。通过后测试人员根据测试计划和测试用例对服务进行测试(测试计划的创建和测试用例的编写可与开发阶段并行)。
在此阶段为了不阻塞测试,确保项目按时发布,开发人员应优先解决测试同学的 Bug(至少是阻塞类的 Bug)。为减少不必要的沟通和排障,安装部署文档要尽可能详尽和准确,并及时地跟进测试。
测试通过后将 release/1.0.0 分支 合并到 master 分支和 develop 分支:
git checkout develop
git merge --no-ff release/1.0.0
git checkout master
git merge --no-ff release/1.0.0
git tag -a v1.0.0 -m "add print hello world" # master分支打tag
2
3
4
5
删除 feature 和 release 分支:
git branch -d feature/helloworld
git branch -d release/1.0.0
2
# 发布阶段
遵循发布规范,谨慎发布到现网,并做好现网验证。
# 运营阶段
协助运维快速定位并恢复系统,协助运营开发运营类功能接口。