融入开源仓库工作流 - 代码风格格式化和规范性检查篇【Python/CPP/Markdown】

在开始之前,我必须感谢 SigureMo

说来惭愧,我写了这么久 Python,都不知道什么是 Formatter 和 Linter,也从来没有对代码进行格式化过。

https://github.com/SigureMo/nyakku.moe/pull/217

第一次格式化是用 Prettier =-=,因为 CI 没通过,SigureMo 提醒我用 Prettier 格式化代码。


学习优雅的工作流,让代码变得更可爱

参考:让 Paddle 更可爱——开发者体验提升计划

超纲,太超纲了!!我之前连一个格式化工具都没有用过,结果一上来,black,isort,Poetry,pytest,git hook.

但是,为了能够比较快地融入大型开源仓库的开发,我有必要把这些学下去,哪怕只是囫囵吞枣。

在项目中配置和应用工作流

流程:

分成 IDE 配置和 git hook 配置,以及 CI 配置。

IDE 配置让我们可以在保存代码的时候自动格式化代码,git hook 配置让我们在提交代码的时候自动格式化代码和检查代码规范性,CI 一般是 PR 的工作流里检查代码,我们能做的通常是让 git hook 能和 CI 保持一致,这样会省掉很多反复 CI 的功夫。

我们专注于前两个,CI 一般是仓库维护者配置的,我们只会稍微学习做一个简单的 CI 工作流。

而且一般仓库也会提供.pre-commit-config.yaml,这是 git hook 的配置文件,一般直接套用即可,就能保证代码风格的一致性。

代码规范性弄完了,还有环境统一性的问题,这个我们应该会用 docker 解决。

IDE 配置:

这一次是 CPP 和 Python 以及 Markdown 的工作流。对于 CPP 格式化和编写,我都使用 vscode。但 vscode 似乎不支持类跳转,所以可能还会用到 Clion。

Formatter:

Python 我们日常使用 isort,black,pylint

CPP 我们使用 clang-format,clang-tidy

Markdown 我们使用 prettier

这里测试了一下,发现 vscode 对 isort 和 black 的 formatter 似乎无法正常识别

“There is no formatter for ‘python’ files installed.”

暂时无解,所以现在 Python 和 CPP 在 Git Hook 中格式化,写脚本批量格式化所有代码可能动到自己没有修改的文件,而 Git Hook 的格式化添加到 git add 或者 git commit 之前,不会有这个问题,而且很适配我们的问题。【当然可以的话尽量在我们的 IDE 里面也都配置一份,不然 commit 的时候会有很多很多待 solve 的问题。】

那么 IDE 就只需要配置 Markdown 的格式化了。

项目根目录创建:./.vscode/settings.json:

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode"
}

并且再 vscode 的插件中安装 Prettier - Code formatter 插件。

Linter:

这里我用 Pyright 作为 Python 的 linters,clang-tidy 作为 CPP 的 linters。

./.vscode/settings.json:

{
  "C_Cpp.clang_format_style": "file",
  "C_Cpp.codeAnalysis.clangTidy.enabled": true
}

Pyright 在 vscode 插件中安装后即可,似乎不需要额外配置。

clang-tidy 一般还需要.clang-tidy文件来作为配置文件,这个不用自己写,白嫖别人的就好,参考后面 git hook 中我从 paddle 那里白嫖了一个 codestyle。

在终端中安装 clang-fortmat,clang-tidy:

sudo apt install clang-format clang-tidy
clang-format --version
clang-tidy --version

Git Hook 配置:

git hook 的配置主要是编写.pre-commit-config.yaml文件,这个文件是 pre-commit 的配置文件,里面会说明我们要使用哪些 formatter 和 linters,顺序怎么样。一般开源仓库会提供这个文件来作为代码风格约束。

这里我们日常使用的就是 isort ,black,clang-format,clang-tidy,prettier。

我们需要把它们引入工作流中,当然也得先学会阅读仓库提供的.pre-commit-config.yaml文件。

诶嘿,这个现在不用学了,丢给 gpt 嘎嘎两下就出来了。

因为我 markdown 平时写完及时保存,保存时会自动格式化,所以我不需要在 git hook 里配置 Prettier。而且无伤大雅。

小练习 1:为 python 小仓库配置 git hook

找一个合适的仓库来做,不宜太大,那搞得累死,也不宜太小,不然整个撸下来都没几个问题。

这里我打算用我之前写的一个 chromedriver 爬虫 template 来做,说实话我垃圾代码巨多,你要是有兴趣随便拉一点。

https://github.com/MrXnneHang

你需要先创建.pre-commit-config.yaml文件:

# 这是根级配置项
exclude: |
  (?x)^(
      patches/.+|
      third_party/.+
  )$

repos:
  # 配置仓库及钩子
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: check-added-large-files
      - id: check-merge-conflict

  - repo: https://github.com/psf/black
    rev: 24.8.0
    hooks:
      - id: black

我们都是依赖于 pip:

pip install pre-commit
pip install isort black
cd . # 进入项目根目录,.git文件夹所在的目录
pre-commit install
pre-commit run --all-files # 对所有文件进行检查

这里即使不写 install isort black 也可以,因为已经指定了镜像仓库,会自己拉下来。

但是我们有时候会需要自己本地运行一些格式化,所以还是建议本地也有一个格式化的工具,你大概也不希望一个一个给注释前面加上一个空格吧。

这么搞完之后,你的.git/文件夹下会多一个hooks/文件夹,里面会有一些文件,这些文件是 pre-commit 的脚本。

这意味着之后每次提交代码,即使没有使用先前 pre-commit 的虚拟环境,也会自动运行 pre-commit 的脚本。

如果仓库提供的.pre-commit-config.yaml 文件里要求使用不同的格式化工具,那么你要做的也类似。

小练习 2:为 CPP 小仓库配置 git hook:

不同于 Python,CPP 的格式化工具是 clang-format,clang-tidy。

这俩并不能似乎通过 pip 安装,而是通过 apt 安装,而且不小。

这里我也有一个 CPP 仓库,你可以试一试。【我放弃了,真的太烂了,在我把所有的格式化问题解决后才发现规范性问题才要命】

https://github.com/MrXnneHang/CPP-Primer-Note

clang-format,clang-tidy:

sudo apt install clang-format clang-tidy
clang-format --version
clang-tidy --version

值得注意的是,clang-format 和 clang-tidy 现在并不能通过 pre-commit 安装(如果 repo 写的是远程镜像仓库),因为它的镜像仓库有个奇怪的现象:如果通过 https 来访问,会出现鉴权的情况(输入用户名和密码,但是现在 github 不支持直接用密码登陆,2FA 了。),而通过 ssh 连接,似乎要对仓库具有写权限,这样也不行。

所以,那个仓库就那么挂着,我也只能干巴巴看着,但是总有办法,这是 PaddlePaddle 的.pre-commit-config.yaml

它里面写的是 Local 方法,写了几个 bash 脚本,可能稍微绕了点路,但是总归让我两三行代码就能把东西都配置好,版本兼容也不用考虑。

https://github.com/MrXnneHang/daily-code-style-template/tree/cpp-python-paddle-rule

这是我提取出来的 PaddlePaddle 的代码规范。

每次 Python+CPP 空仓库可以直接套用,非常方便。

主要碰到一个问题,对于先前完全没有进行格式化过的大仓库,不宜引入 pre-commit。

因为虽然格式化大量代码似乎会有一种看别人挤黑头的快感,但是实测,很难通过 pre-commit 的检查,而且需要修改大量的文件,可能毁了仓库。

学会 pre-commit,只能说有一个好的开始,下次好好来个从零开始的可爱仓库。

不过这也说明,code-style 这东西应该最初就定好,而且不会太经常改变,不然对维护代码而言是一种灾难。

CI 配置:

CI 是一个脚本,准确来说是一个 github 的 workflow,它会检查代码的规范性,如果代码不符合规范,那么 CI 就会报错,不允许合并 PR。

Sigmore 的仓库就有 CI,在发起 PR 后会先检查我代码是不是符合 Prettier 的规范,一下子就很有 b 格。它其实和本地的 git hook 的工作方式是一样的,只不过是在 github 上自动运行的。

在 Paddle 里,它的 CI 就用来做很多事情了,包括各种平台化测试,显卡兼容性测试,以及代码规范性检查。

我记得上次我提交了一个改了三个单词的 PR,然后 CI 跑了一个早上,我突然就觉得挺对不起 github 的服务器的。

我们可以通过配置.github/workflows/文件夹下的 yaml 文件来配置 CI。

以 prettier-check 为例:

name: Prettier Check

on:
  pull_request:
    branches:
      - main # 或你希望监控的其他分支

jobs:
  prettier:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: "16" # 选择你需要的 Node.js 版本

      - name: Install dependencies
        run: npm install

      - name: Run Prettier
        run: npm run prettier -- --check .

如果你在提交过程中碰到 git hook 的阻截,比如每行末尾多余空格之类的,你可以考虑先用 Prettier 对 yaml 文件进行格式化,然后再提交就没问题了。

名词解释:

git hook:

它是 Git 的一个重要特性,它让你可以在 Git 仓库中定义一些自动化的脚本,这些脚本可以在特定的 Git 事件(如提交代码、接收代码等)发生时被触发执行。

pre-commit:

它是一个 python 的库,通过配置.pre-commit-config.yml(通常开源仓库会提供作为它的代码规范)可以配置 git hook.它通常说明了在提交代码时要进行那些规范性检查和格式化。

Formatter(代码风格格式化):

python 常用的 isort 和 black,isort 用于格式化 import 语句,black 用于格式化代码。

CPP 常用的有 clang-format.

Linter(代码规范性检查):

python 常用的有 flake8,pylint,其中 pylint 是 vscode 内置的。

CPP 常用的有 clang-tidy.

CI:

在 github PR 的工作流里面,一般会定义一个 CI 的工作流,用于检查代码的规范性,通过 CI 的检查才能合并 PR。

17 个赞

感谢你的分享!

4 个赞

欢迎佬友指正。

有没有Typora 大纲的形式,按照标题来把目录列出来,不然看起来好长

3 个赞

感谢大佬分享。

4 个赞

干货,感谢佬友分享

4 个赞

感谢老友分享!

2 个赞

佬,你字写错了

2 个赞