阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

知乎 Android 客户端 CI/CD 方面的努力

  • 2020-03-26
  • 本文字数:3378 字

    阅读完需:约 11 分钟

知乎 Android 客户端 CI/CD 方面的努力

前言

伴随着知乎业务的飞速发展,近一年多时间,知乎的 Android 团队由十多人的小团队发展至五十多人的大团队,并且还在不断的壮大中。


虽然我们常常说人多力量大,但是有时候人多也未必是件好事,譬如经典计算机软件著作「人月神话」中就提到在某些情况下 1 + 1 也是有可能小于 2 的。(杜撰的,如有雷同…)


为了让 1+1 大于 2,移动平台团队做了一些工作,不断提升工程师的研发效率,降低各个团队相互干扰,减少重复无用功,支撑业务稳定前行。


下面就就其中 CI/CD 方向跟大家说一下。

组件化方面做的努力

Android 组件化方案 已经运转了近一年半的时间,令人欣喜的是其已经达到了我们当初的预期。即:不同 Android 团队之间,可以通过组件仓库制造代码壁垒,分而治之;


同时其来带的效果也是显著的,即:无论是研发效率还是编译速度都有了不少提升。


但是祸福相依,有得必有失。组件化也不例外,譬如:


  1. 先前代码全在一个仓库,组件化之后,代码跨了多个仓库,代码提交的 CodeReview 很不方便

  2. 一般修改某个组件的流程是去组件仓库提交代码,合入代码后,发布新的组件包,最后在主工程中使用这个新版组件包,打出测试包。也就是代码测试发生在组件代码在合入后

  3. 先前单个仓库的时候,主仓库有轮流的 merge 工程师,定期把 release 的代码 merge 到 develop 上面,多个组件后,仓库数量膨胀,如何不依赖组件管理人的细心程度,确保每个仓库的 release 代码能够合入到 develop 之中,也是一个问题。

跨组件的 CodeReview

知乎 Android 端的组件化,是使用如下的文件控制的:


// versions.gradlecomponent.answer.version = '1.2.3'
复制代码


主工程读取这些 version 信息,然后再依赖这些组件, 使用类似于这样的语句:


 compile com.zhihu.android:answer:${answer.version}
复制代码


一般而言,我们比如升级了某一个组件,在主工程上面看到的改动就只有类似于这样:


// versions.gradle+ component.answer.version = '1.2.5'- component.answer.version = '1.2.3'
复制代码


至于中间带来什么东西,只能靠工程师自己去翻仓库了,这个很不合理的。


其实由于每个版本都有一个 tag 对应,接着 gitlab 上面已经提供了一个方便浏览的页面


https://git.repo.guest.where.zhihu.com/Android/Ad/compare/<from-commit>...<to-commit> 
复制代码


我们直接输出出来即可。效果如下:



过去没有 tag 的时候,真的是一个个 commit 去搜,现在的孩子真幸福 =v=

联合打包

我们需要一个能够在多个组件提交代码之时,就能打出相应的测试包。


gradle 可以通过下面方法源码依赖某一个工程include 'moduleA'project('moduleA').rootDir = '/path/of/module/A'  gradle 版本依赖某一个工程dependencies { implementation 'com.well.zhihu:moduleJoinUs:2.3.3'}
复制代码


我们这里是使用一个配置文件,配置需要源码依赖或版本依赖的组件。


如下图:



可以看到这次打包是联合了 ModuleA 以及 ModuleB 组件提交的代码打包。


这里面有个细节,我们在每次开始编译的时候加了「begin to build」以及 job 版本号(图中的 3537),是为了跟最后生成的包的 job 版本号匹配的。


为了让测试的同学知道,这个包是在哪个代码状态下打出来的(打包所获取的组件代码是当前 MergeRequest 提交的代码,担心在打包的过程中,又提交新的代码,这样生成的包就不是当前代码状态的了,让测试同学误解)

分支合并的问题

世界上最冤的 bug 不是字符串的值为 “null”,而是我已经在 release 上修了,但是代码没有合入到 develop。如下图:



bug 是不存在的



纸还是包不住火 =_=


如果是 bug 不严重的话,可能就只是浪费测试以及开发资源。但是遇上什么 downtime,紧急修复,忘记合入,则会是新的 downtime,又一次紧急修复。而人总有可能犯错。


知乎这边的做法是定期自动提这样的 merge-request: 「次最新 release => 最新 release」以及「最新 release => develop」(也就是上文的 release-1.2 => release-1.3 release-1.3 => develop )我们会在需要合并的时候定期提醒工程师合并代码,尽量减少工程师的工作。

其他

还有一些细枝末节的,譬如:


有些业务组件的发布流程与主工程同步,在主工程拉分支的时候,也会拉出一个对应的 release 分支,一般自动拉分支的组件都会有自动合并分支的功能。


创建 lint 服务,组件工程只要配置一下,提交代码的时候,都会跑一次 lint,报告贴在 merge request 中,作为 CodeReview 的材料。

包大小监控

业务增长很快带来的另一个问题,是包大小也增长地很快。


包大小减少之前组内 Java script 工程师


@Peter Porker 做过一次,效果显著,但是无奈,包内增大席卷重来,所以除了直接减少包大小,一套可以无需人为地遏制包大小的增长,或者监控包大小的增长情况的方案,尤为重要。


我们这里做了两件事,一个是使用 gradle + githook 的方式,限制某些不规范的提交(譬如过大的资源文件等),二,实时监控代码提交的时候带来的包增长,生成易读的报告。

限制不规范的提交

不规范的提交包括:资源过大,提交的资源是 png 而不是优化过的 webp,一些低像素的资源也提交过去(-hdpi,-mdpi 现今的设备基本上不会用到这些资源)


githooks 中,可以往 commit-msg 中写一些脚本,检查当前提交的文件内,是否出现上述问题(可以用下列方式获取到当前提交文件: git diff --cached–name-only --diff-filter=d``)。


这里有一个问题,git hooks 一般不跟版本走,也就是说很难提交到仓库,然后让别人 down 下来,去覆盖本地的 hooks 文件。想要做到这一点,这就需要外界脚本的帮助。


知乎这边 Android 的开发流程很依赖 gradle,我们的做法是 先把 hooks 里面的所有文件存放在某个仓库里面,然后在 gradle 中植入这些代码:download 这些 hooks 文件,然后覆盖复制到本地的 .git/hooks/ 下。篇幅的问题,代码就不贴了 = =)download 的方法,我是用 git archive 。


最后把这些逻辑写入到 gradle plugin 中。由于所有组件工程都会依赖这个 plugin,这样所有组件工程都会装上 hook,所有的代码的提交都会被你限制到(我给他取名 Ozymandias =v=


安装 git hooks 的效果图:(其实 文字都是自己打印出来的,所以 「效果图」谈不上 = =)



美术有点差,见谅 - -


commit-msg 代码检查:



欲擒故纵 =v=

实时监控代码提交,生成相应报告

实际上,这些是不能 cover 住很多情况的,而且有些加入的资源,最后不一定会加入到 apk 包中(譬如 proguard 掉的部分)检查包增长,打个包出来,自然就知道了。


我们这边做的是:


  1. 每次合并代码之后,记录一下最新包的包大小以及包内信息,譬如 develop.detail release-1.2.3.detail

  2. 每次提 merge-request 往 develop/release 合的时候,打一个「假设已经」合入之后的包,获取它的包大小以及包内信息,跟历史纪录对比一下,即可以知道这次改动带来的变化


实现的效果如下:



确实细粒度到类或者包,可能会更好


包内信息我是 unzip 之后,逐一用 du 生成大小以及文件名的信息,交给 python 脚本进行比对的。大致的代码是:(由于文件过多,取最大的前 100 个)


// 统计包大小信息TOTAL_SIZE=`stat -c %s ${package}`SIZE_IN_MB=`echo "scale=2;${TOTAL_SIZE} / 1024 / 1024" | bc`    // 统计包内文件信息unzip "$package" -d "build/apk"find build/apk -type f | xargs du -k | sort -n | tail -n 100 \ | tee "$file_info"// 生成的包内信息如下:28  build/apk/res/raw/how.mp332  build/apk/res/drawable-night-xxhdpi-v8/are.webp32  build/apk/res/drawable-xxhdpi-v4/you.webp
复制代码


至于对比,只要写个 python 脚本读取该文件,以 name 为 key 的字典即可。

特殊团体的监控

移动平台团队维护的代码,由于调用方过多,稍有不慎,就出问题。所谓不受监督的权力容易滋生问题,所以平台组的成员需要出一套机制监督平台组的运转 ╮(╯_╰)╭


目前的是:


平台组内的 CodeReview 由另一个平台组成员 + 其他团队人员组成。CodeReviewer 是随机指派的,当然为了 CodeReview 效果更好(总不能把做想法的工程师 review 首页的代码 领域不同 CodeReview 效果可能不大好 (


  1. @李明亮等100万人: 诶 会有问题吗) ( = =)泥奏凯)这边就是通过看这次改动里面的文件的修改记录(git log), 查到最新的经办人是谁,交给他。

  2. 平台组的代码提交 MergeRequest Open - Merged - Close 事件都会通知到群里面的人

  3. 定期每一个迭代都会生成「在这个迭代内平台组的所有提交」的报告,供业务方查看。


就酱. Thanks for reading.


2020-03-26 19:00982

评论

发布
暂无评论
发现更多内容

spring boot整合rabbitmq

小鲍侃java

11月日更

成熟项目的Flutter快速引入以及Flutter、Native混合开发探究

android 程序员 移动开发

我懵了,面试大厂被熟悉的App启动流程和RecycleView连环三问坑了

android 程序员 移动开发

微信小程序之侧栏分类 —— 微信小程序实战商城系列(1

android 程序员 移动开发

开发者须知:女性玩家和手机游戏注意事项

android 程序员 移动开发

我是双非-三本-专科学校的Android开发,我有机会进入大厂吗?

android 程序员 移动开发

开发经验2年26岁的Android大龄程序员面试13家公司,逆袭薪资35K的!(1)

android 程序员 移动开发

微服务下的幂等设计

android 程序员 移动开发

微信小程序之商品属性分类 —— 微信小程序实战商城系列(4

android 程序员 移动开发

微信小程序之商品属性分类-——-微信小程序实战商城系列(4

android 程序员 移动开发

微信小程序使用canvas画布生成二维码海报分享图片(完整示例代码)

android 程序员 移动开发

想做直播的你,这些热门的音视频如何绝对同步的。你get了嘛?

android 程序员 移动开发

感觉中国程序员前景一片灰暗,是这样吗?

android 程序员 移动开发

成功获得字节跳动月薪20+的Android岗offer,看看面试都问了些什么?

android 程序员 移动开发

我们始终不能靠旧的技术来生活!

android 程序员 移动开发

当年他问我service,我是这样回答的

android 程序员 移动开发

微信小程序使用七牛云对象存储保存图片和文件

android 程序员 移动开发

总是听到有人说AndroidX,还有人不知道什么是AndroidX的吗?(1)

android 程序员 移动开发

底层学习---Android-IPC机制(二)序列化机制

android 程序员 移动开发

当事人:现在就是非常后悔,开工那天没去上班,老板不仅省了我的开工红包

android 程序员 移动开发

微信小程序之自定义轮播图实例 —— 微信小程序实战系列(3

android 程序员 移动开发

总是听到有人说AndroidX,还有人不知道什么是AndroidX的吗?

android 程序员 移动开发

想做直播的你,这些热门的音视频如何绝对同步的。你get了嘛?(1)

android 程序员 移动开发

我曾经用了 3 天面试 7 家公司,拿到了4个offer!记录一段搞Android开发黄金五年

android 程序员 移动开发

开发8年大牛教学Android动画:这是一篇全面-&-详细的-属性动画-总结&攻略

android 程序员 移动开发

开枝散叶的组件化技术,90%的开发者用后都说香

android 程序员 移动开发

开发经验2年26岁的Android大龄程序员面试13家公司,逆袭薪资35K的!

android 程序员 移动开发

强势分享!BAT大牛带你深度剖析《全网最系统Android 三大主流开源框架(附源码

android 程序员 移动开发

成为伟大程序员的-10-个要点

android 程序员 移动开发

我怎么感觉全世界都在劝退学Android的程序员?

android 程序员 移动开发

开发者,你是如何做到高效开发的

android 程序员 移动开发

知乎 Android 客户端 CI/CD 方面的努力_文化 & 方法_郑小则_InfoQ精选文章