写点什么

知乎 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:001117

评论

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

3DCAT荣获2021金陀螺“年度XR行业技术创新奖”“年度优秀VR行业应用奖”两项大奖

3DCAT实时渲染

云计算 教育 VR/AR 渲染 渲染器

【高并发】导致并发编程频繁出问题的“幕后黑手”

冰河

并发编程 多线程 高并发 协程 异步编程

(1-14/14) 首位销售人员

mtfelix

300天创作 2022Y300P

聚类算法有哪些?又是如何分类?

郑州埃文科技

数据分析 聚类算法

CPython 性能将提升 5 倍?faster-python 项目 PEP 659 源码级解读

阿里巴巴终端技术

Python 源码 源码分析 CPython

Android 64位架构适配

百瓶技术

andiod 客户端

Go 语言快速入门指南:Go 并发初识

宇宙之一粟

golang 并发 Go 语言 1月月更

腾讯自选股如何实现单位小时内完成千万级数据运算

ninetyhe

腾讯 海量数据 分布式,

Kafka 为什么这么快?多的是你不知道的事

码哥字节

kafka 消息队列 1月日更 1月月更

使用无参数函数进行命令执行

网络安全学海

黑客 网络安全 信息安全 渗透测试 安全漏洞

表单数据高级搜索功能设计

全象云低代码

搜索引擎 前端 低代码 搜索 表单

深入浅出Apache Pulsar(1):Pulsar vs Kafka

云智慧AIOps社区

kafka 云原生 消息队列 kafka运维 Apache Pulsar 消息系统

人效将是快消品企业未来发展的最大瓶颈

百度大脑

人工智能

政法委跨单位重点人员联防联控平台建设,治安防控系统开发

a13823115807

如何基于知识图谱实体解析技术进行数据优化?

索信达控股

人工智能 AI 知识图谱 数据优化 索信达控股

架构实战营第 4 期 -- 模块七作业

烈火干柴烛灭田边残月

架构实战营

前额皮质如何影响我们的工作效率?

LigaAI

工作效率 脑科学

redis未授权访问漏洞复现

喀拉峻

redis 黑客 网络安全 安全 信息安全

如何处理消息丢失问题?

JavaEdge

1月月更

恒源云(GPUSHARE)_实例关机后如何操作迁移?

恒源云

gpu 运维 实例

打造手淘极简包的轻量化框架

阿里巴巴终端技术

ios android 框架设计 移动开发 包大小

APICloud 原生模块、H5模块、多端组件使用教程

YonBuilder低代码开发平台

前端开发 APP开发 APICloud 模块 跨端开发

混沌工程之 Linux 网络故障模拟工具TC

zuozewei

Linux 混沌工程 1月月更

使用Rainbond打包业务模块,实现业务积木式拼装

北京好雨科技有限公司

十大视频场景化应用工具+五大视频领域冠军/顶会算法重磅开源!

百度大脑

Scrum Master如何参与每日Scrum(Daily Scrum)

Bruce Talk

Scrum 敏捷 Agile Coach/Facilitate

实战 MongoDB Aggregate

PingCode研发中心

mongo pipeline Expression

网络安全kali渗透学习 web渗透入门 Kali系统的国内源配置

学神来啦

Hoo虎符研究院|区块链简报 20220117期

区块链前沿News

Hoo虎符 Hoo 虎符研究院 区块链资讯

ThinkPHP6和GatewayWorker简单的示例

CRMEB

Python 为什么不设计 do-while 循环结构?

Python猫

Python

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