1 Workflow Overview

Workflow based on git-flow

Workflow based on git-flow

发布一个版本:

  1. Developer 创建新的 feature 分支,在上面实现功能,每次 commit 前进行本地 lint 和 test(可选)
  2. Push feature 分支到 central repo
  3. Server hook 触发 feature 分支上的 lint 和 test
  4. Reviewer 在 gitlab web 界面上查看代码变更和自动测试的运行结果
  5. 通过代码审核和测试后 (reviewer 或者 developer) merge feature 到 develop 分支
  6. Push develop 分支后自动运行 lint, build, test 以及 deploy on staging server
  7. Feature 分支满足发布版本要求后,发布到 master 分支,自动执行 build, test 和 deploy on beta server
  8. beta 问题修正,重复前面的过程直到符合正式发布标准
  9. 发布到 production server

2 Introduction

2.1 Why We Need VCS

  • 安全网:版本保存与回退
  • 协作工具:并行开发,分支与合并
  • 质量保证:代码评审和职责确定
  • 持续集成/持续发布

2.2 Basic Concepts & Commands

  • Repository
  • Local vs remote
  • Working directory, index(stage), local repository
  • Codes as documents

参考 git simple guide

2.3 git ignore

2.3.1 Rationale

  • 保持 git diff 的有效性
  • 保持代码自洽

实践中,所有非手工编写文件都尽量不进入版本控制系统: * 二进制文件 * 程序生成的文件

2.3.2 Examples

  • .DS_Store for macOS
  • .Rhistory, .Rproj.user/, .RData, .nb.html for RStudio & Rmd
  • **/__pycache__/ for Python project
  • .ipynb, .ipynb_checkpoints for Jupyter notebook
  • .idea/workspace.xml, .idea/usage.statistics.xml, .idea/*.iml for JetBrains IDE family, such as PyCharm, WebStorm, etc

辅助工具:gitignore.io

2.3.3 For Binary Files

常见处理方法:

  • 提交到 VCS 中。优点:实施难度低;缺点:代码库质量低
  • 构建过程中由 CI?CD 系统提供。优点:保持代码库质量、可移植;缺点:代码库部署完整性不足
  • 使用 git-lfs 处理非手工生成文件。优点:兼顾代码库质量和部署完整性;缺点:需要安装 git-lfs

3 Commit Message

Commit message 的作用:

让每一行代码讲述历史。最重要的产品文档。

git log, git diffgit blame 充分发挥作用。

3.1 Gommit Message Template

<type>(<scope>): <subject>

<body>

<footer>

3.1.1 Keyword: type

What kind of action is performed in this commit?

常用标签:

  • feat: 新功能
  • fix: 修复 bug
  • docs: 添加/更新文档
  • style: 更新代码格式
  • refactor: 重构代码
  • perf: 改善性能
  • test: 添加/更新测试代码

3.1.2 Keyword: scope

What’s the object of the action?

改动的目标是什么?

3.1.3 Keyword: subject

What action is performed in this commit?

对本次提交的一句话说明,句尾不加句号

3.1.4 Keyword: body

Why and how of <subject>?

3.1.6 Examples

feat(backend): 实现了用户购物车基本功能

通过将购买服务加入购物车中,方便用户使用优惠券等提升购物体验

Fix #233

4 Toolchain

4.1 Local VCS hook

4.1.1 提交前检查

在 hook 文件 pre-commit 中定义,常用来进行本地代码的格式检查、单元测试等。 示例代码:

#!/usr/bin/env python

import sys
from subprocess import call


def runsh(cmd: str):
    ret = call(cmd, shell=True)
    if ret != 0:
        sys.exit(f'Encounter error when running "{cmd}", return value: {ret}')


print('Linting codes ...')
# runsh('flake8 .')
runsh('flake8 --ignore=F401,E501 .')
print('Pass!')

4.1.2 提交消息模板

由 hook 文件 prepare-commit-msg 实现,例如:

#!/usr/bin/env python

import sys

template = """
# This is a custom Template
# <type>(<scope>): <subject>
# <blank line>
# <body>
# <blank line>
# <footer>

# See [Git Commit Message Guidance]() for details

# Example:

# feat(backend): 实现了用户购物车基本功能

# 通过将购买服务加入购物车中,方便用户使用优惠券等提升购物体验

# Fix #233
"""

with open(sys.argv[1], 'w') as f:
    f.write(template)

4.1.3 验证提交消息

由 hook 文件 commit-msg 实现,对提交的信息进行格式检查,例如:

#!/usr/bin/env python

import sys
import re

with open(sys.argv[1], 'r') as f:
    lines = f.read().splitlines()

valid_lines = [line for line in lines if (not line.startswith('#'))
               and len(line) > 0]
assert len(valid_lines) >= 2, '有效行数小于2'
subject_format = (r'((feat)|(fix)|(docs)|(style)|(refactor)|(perf)|(test))'
                  r'\(\S+\):\s.*\S')
assert re.fullmatch(subject_format, lines[0]), '标题格式不符合要求'
assert len(lines[1]) == 0, '标题行下面没有空行'
assert len(lines[2].strip()) > 5, 'body 长度不足 5 个字符'

4.2 Server CI/CD Tools

4.2.1 Gitlab CI/CD

在指定的运行环境(gitlab runner)中执行 CI/CD 动作,一般由 build, test, deploy 三步组成。 常用的运行环境是 Docker,通过 image 关键字指定,例如 image: python:3.8.9

编写 .gitlab-ci.yml 文件,自动运行

优点:功能强大,可以对触发条件做精细控制,是代码库的一部分,方便管理

缺点:绑定 gitlab 平台

4.2.1.1 整体架构

  • Simple: build > test > deploy 三段模式
  • DAG: 有向无环图,
  • Double Tier: 支持局部构建

4.2.1.2 Simple Build

stages 间顺序执行,stage 内各个 job 并行执行。 前面 stage 中所有 job 运行完成后,开始下一个 stage。示例:

stages:
  - build
  - test
  - deploy

image: python:3.8.9

job1:
  stage: build
  script:
    - pip install -r requirements.txt
    - echo "step 2"

job2:
  stage: test
  script:
    - echo "run test 1"
    - echo "run test 2"

job3:
  stage: deploy
  script:
    - echo "run job 1"
    - echo "run job 2"

顶层标签是 or stages or workflow or image.

workflow 一般包含 rules 实现对执行场景的精确控制,例如:

workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "schedule"'
      when: never
    - if: '$CI_PIPELINE_SOURCE == "push"'
      when: never
    - when: always

4.2.2 Git Server hooks

与 local hook 一样保存在 .git/hooks 文件夹中,包括:

  • pre-receive: 接收 push 前执行,常用于格式检查等
  • post-receive: 接收 push 后执行,常用于部署服务
  • update: 针对某个 branch 做相应的动作

优点:任何 git 仓库都可用

缺点:需要有服务器文件系统读写权限,不是代码库的一部分,需要单独维护

4.2.3 Gitlab webhook

需要单独编写 web 服务执行相关动作,比其他方式略重。

5 Collabration Pattern

5.1 Central Repository

git add
git commit
git push
git pull --rebase
git push

5.2 Feature branches

每个特征使用一个分支,将特征分支 push 到 central repo。

开发过程中的 commit push 到 remote repo 对应的 feature branch 上。

开发完成后发起 PR 请求 merge 到 master 上,相关开发者接到通知,可以评论代码, 也可以 clone 此 branch 修改代码。

5.3 git-flow

5.3.1 Concepts

  • master: 发布分支(只包含可发布的代码),commit tag 作为版本号
  • develop: 特征收集分支
  • release: 短期分支,专为某次发布服务,合并回 master 和 develop
  • hotfix: 短期分支,专为某次发布服务,基于 master,合并回 master 和 develop
  • feature-xxx: 短期分支,专为某个功能开发,功能完成后合并回 develop

与 feature branches 模式的区别:特征分支基于 develop 分支而非 master 分支: 从 develop 创建,合并回 develop。

当 develop 满足发布版本的特征后,创建 release 分支,此分支只接收发布相关的 commit, 例如 bug fix,文档完善等。发布到 master 分支,并合并回 develop 分支。

优点:发布版本和特征开发并行。

5.3.2 Installation

brew install git-flow  # on Mac
apt install git-flow   # on Linux

5.3.3 Working Demo

全局级别:clone hook repo,设置 git hook 全局路径(可选);

项目级别:初始化 git flow 项目,设置项目环境:

$ cd ~/Documents
$ git clone git@123.56.15.24:datascience/git-template.git
$ git config --global core.hooksPath '~/Documents/git-template/hooks'

$ python -m venv .env
$ . .env/bin/activate
$ pip install Django pytest-django ipython ptpython pipdeptree flake8 pep8-naming yapf pynvim  # config Python virtualenv
$ django-admin startproject messenger
$ cd messenger

$ pipdeptree -f > requirements.txt
$ git flow init -d
# or use interactive mode:
$ git flow init
Initialized empty Git repository in /home/leo/Documents/messenger/.git/
No branches exist yet. Base branches must be created now.
Branch name for production releases: [master]
Branch name for "next release" development: [develop]

How to name your supporting branch prefixes?
Feature branches? [feature/]
Bugfix branches? [bugfix/]
Release branches? [release/]
Hotfix branches? [hotfix/]
Support branches? [support/]
Version tag prefix? []
Hooks and filters directory? [/home/leo/Documents/messenger/.git/hooks]
# on develop branch

# here run `git config core.hooksPath ~/Documents/git-template/hooks` to set template for only this repo

$ git flow feature start naive-func
# on feature/naive-func branch

这时自动进入 feature 分支。

5.4 Folk based workflow

去中心化,每个开发者都有两个 repo:本地的私有库和远端的公开库,多见于开源项目。 与其他协作方式相比,不需要开发者有中心库写权限,maintainer 负责审核所有开发者的 PR, 并写入到中心库中。

开发者的第一个动作不是 clone,而是 fork。