前提
code review 是我们工作中可以说必不可少的一环,一方面能够提早的发现代码中不合理的部分、或者不稳定、架构不合理的部分,提高代码的质量;另外一方面能,更够让团队的知识共享,互相熟悉业务需求,以及团队写的好的部分。OK,作为code review最早最基础的一环,可以说是代码规范。如果人工进行代码规范的维护,难免会遗漏,而且会花费时间在这部分。
所以,针对这种情况,我们可以做的是,做一个lint帮助我们检测不规范的代码,OK,下面就lint,开始研究!
Lint 时机
我们熟知的Objective-C lint,有OClint、Infer; swift 有swiftLint,首先我们先不考虑这个几个lint的问题,我们先考虑一个重要的问题,lint应该放到什么阶段?
-
放到build时期?
这种方案被我们pass掉了,首先放到build时期会增加我们的build时间,第二事实上我们需要嵌入脚本到项目里,可能会影响到打包。
-
放到git push 之后gitlab pipeline时期?
这种方案不出意外也被pass了,首先如果知道pipeline job的人都知道,我们需要配置runner, 本身来说配置加集成,可能就不是一个容易的事情,而且对于我来说我觉得这个事情,太晚了,lint 有问题,还等再次push….
-
放到pre commit时期?
Ok, 对于比较合适的事情,我觉得就是在precommit时期,因为基于git 管理,我们代码每次改动之后都需要commit,那么commit事情去做lint这件事情,是非常合适的,如果存在问题,那么就修改之后,再次commit。时间上,时机上我觉得都很合适
ok, 对于 lint 放到哪个时机的问题,我们已经确定–pre commit时期
pre-commit
git 其实为我们提供了很多种的hook方式,随便打开一个.git文件夹就可以看到一堆的hooks example文件,如下:
OK,所以其实我们想要hook,就变得非常容易,只要我们修改pre-commit为可执行文件,并在里边按照example嵌入执行代码即可。
这一步,早就有人帮我们做了,那就是pre-commit 他不仅简单了我们的操作,并且起到了脚本lint代码分离的效果,他通过.pre-commit-config.yaml 文件中的配置,在执行git commit时候执行pre-commit 脚本,再而执行到安装的pre-commit代码里,进行clone .pre-commit-config.yaml里配置的repo,以及分析,执行对应的hook代码。
接下来简单的介绍一下pre-commit 相关的东西:
-
安装
Mac 推荐使用:
brew install pre-commit
其他安装:
pip install pre-commit
conda install -c conda-forge pre-commit
-
配置
安装完pre-commit之后,在.git 同级目录,添加.pre-commit-config.yaml 文件,则可支持pre-commit
.pre-commit-config.yaml的相关配置:
repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.3.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/psf/black rev: 21.12b0 hooks: - id: black
注意点:rev: 不再支持commit hash id, 只支持tag
具体参考:https://pre-commit.com/#plugins
-
创建一个自己的hook
一个hook必须要包含a .pre-commit-hooks.yaml 文件,这个文件是告诉pre-commit,这个hook库里包含的hook id等信息…
大概张这个样子…
- id: trailing-whitespace name: Trim Trailing Whitespace description: This hook trims trailing whitespace. entry: trailing-whitespace-fixer language: python types: [text]
hooks 仓库,支持很多种语言,每种语言包含的文件也不太一样,比如script (shell脚本)必须entry参 数给一个相对路径的shell脚本;比如python,必须要执行pip install .(及包括setup.py或者 pyproject.toml)以及entry一般在setup.py 里的console_scripts或者scripts配置的。
了解更多
Objective-C Lint
上面提到现存在两种lint,oc-lint/ infer。
oc-lint
安装: brew tap oclint/formulae
brew install oclint
*// 安装xcpretty* gem install xcpretty
先简单介绍一下OC lint:OC lint 是通过编译之后的产物 compile_commands.json 去分析的,所以必要的是我们执行oc-lint必须要build。然后在完成之后会以自动打开一个html分析结果。
我们先说说他的缺点:
- 必须要编译,首先这是一个漫长的过程,而且对于我们一个不支持模拟器的app(害!因为内部直播的framework没把x86打进去)这是比较致命的可以说
- 他不支持增量查,这样会带出来很多历史遗留问题,对于集成来说,又成了一个比较窘困的问题(你想想,以前多少可能有问题的代码,这集成都得改,不得改个三天三夜的😁)
接下来我们说说他的优点:
1. oc-lint 不仅查了不规范的代码,而且查了可能会导致问题的坏味道代码
1. oc-lint 对于代码严重程度做了等级分化,并且以html的方式打开,更加的直观
1. oc-lint支持自定义规则,和修改规则
当然,对于1.2的缺点,已经有了一些方案解决,了解更多
后面会尝试集成到pre-commit hook, 敬请期待…
infer
安装: brew install infer
可以选择下载安装包安装,下载地址
Infer 是facebook提供的一个lint 库
缺点:
1. 貌似infer 也需要编译工程
1. 不支持自定义规则
优点:
1. 支持多种检测,包括空指针、内存泄漏等等的检测
1. 支持增量检测
具体我也没有太多研究,需要深入研究的,请参考
Objective-CLint 自制hook
这个库是我基于SpaceCommand三方库,做的pre-commit hook,简单的介绍一下:
SpaceCommand是基于clang-format 制作的代码规范工具,而且写了一些自定义规则,用来检测clang-format忽略或者有歧义的部分。它支持了检测不规范代码,并给出终端提示;而且提供了一键格式化所有有问题的代码。
那么Objective-CLint 在它的基础上,1. 我显示了将它配置成一个可以pre-commit集成的hook,2. 增加了可视化diff 到html,自动打开 3. 去掉了一键格式化 5. 修改自定义规则,使得适用更加广泛。
具体内容介绍,请移步到个人博客Objective-CLint 或者移步到github
到此,我们对于OC代码已经有了好几种方案,我选择了容易集成且轻量级的自制hook。
Swift Lint
swiftLint
Swift 代码毫无疑问,我们就可以使用SwiftLint去集成。swiftLint 已经为我们提供了pre-commit hook,我 们可以直接使用,当然也可以fork,修改规则使用
具体请查看github:https://github.com/realm/SwiftLint
集成
到此为止,我们所有的lint 工具 以及hook 工具已经齐全,即 pre-commit + Objective-CLint + SwiftLint!
Ok, 当然我们还有一些工作量需要做,为了团队更好的集成,我们添加检测安装脚本位于pod pre_install, 目的在于自动检测是否安装了pre-commit,以及自动安装pre-commit。
下面是检测脚本:
#!/bin/bash
echo "正在检查安装必要工具..."
# 检测是否安装了homebrew
if [ "$(command -v brew -v)" ]; then
echo "✅homebrew 已经安装"
else
echo "正在安装brew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo '✅安装完成homebrew 🍺🍺🍺 /n'
fi
# 检测是否安装了pre-commit
if [ "$(command -v pre-commit)" ]; then
echo "✅pre-commit 已经安装"
else
echo "正在安装pre-commit..."
brew install pre-commit
echo '✅安装完成pre-commit 🍺🍺🍺/n'
fi
if [ ! -f ".pre-commit-config.yaml" ]; then
echo "\033[31m❎文件 \".pre-commit-config.yaml\" 不存在, 请添加.pre-commit-config.yaml\033[0m"
exit 1
fi
pre-commit install
echo "\033[36m ...................所有工具都安装成功😁😁😁 \033[0m"
Ok, 我们把它集成到pod中,当然没有使用pod管理的,直接执行这个脚本即可。把下面的脚本嵌入到podfile中
pre_install do |installer|
flag = `sh check_tools.sh`
Pod::UI.puts flag
end
添加.pre-commit-config.yaml
fail_fast: false
repos:
- repo: https://github.com/HaoXianSen/Objective-CLint.git
rev: v0.0.4
hooks:
- id: objc-lint
name: objc-format
entry: format-objc-hook
language: script
require_serial: true
verbose: true
- repo: https://github.com/realm/SwiftLint
rev: 0.49.1
hooks:
- id: swiftlint
OK,这样我们整个集成就完成了。
当然swiftLint的配置不一定是适用于我们的,比如我们想report为html、或者添加一些自定义的规则等等,就需要我们fork一份swiftLint,做一些自己的适配。上面的swiftLint就使用自己的repo以及自己tag就好了。
总结
到此为止,我们可以说整iOS 混合发开的代码规范lint就集成完事了。
当然我们还有优化的部分,首先自己的lint 只是规范检测,不想OCLint 那样面积大,并且输出也不是很哇塞。后续我们可以继续使用fast-oclint的方案,既能使用OCLint的强大,又能不用编译还支持增量编译。