找回密码
 会员注册
查看: 9|回复: 0

AirMax-Android组件修改信息设计落地

[复制链接]

2万

主题

0

回帖

7万

积分

超级版主

积分
72548
发表于 2024-10-5 17:56:10 | 显示全部楼层 |阅读模式
背景?在 Android 当前整个 CI / CD 流程中,组件或者 App 产物产出时缺少核心修改信息。由于修改信息的缺失,这会让开发困惑于该产物是否包含我最新的代码提交。这也给开发/测试流程带来了诸多不便,例如测试不清楚当前需要使用哪个App验证回归相关问题,开发在修完Bug后,也只能手动通知到测试需要使用某个时间节点后的App包,增加了不必要的沟通环节,降低了开发测试验收的效率。技术规划为了解决这个问题,AirMax 项目成立,一个核心,记录每一个组件的修改 commit 信息,两个展示,TestFilght 和 得物App 均要支持修改信息的展示,每个App和组件都可追溯修改记录。为了完成这个目标,涉及到的工作有 CI 卡口 commit 检查优化,deploy 发布后增加 log 拉取存储服务,App 打包流程找那个增加apk包信息存入,便于在 App 中能拉取到自己的修改信息。最后是 TestFlight app 改版,使用新的接口完成列表和详情页的网络请求。整体流程deploy 服务App 打包CI ?卡口优化主要是针对 commit 信息检查的优化,之前只是简单校验 commit 信息是否是 feat fix 等开头,但是发现很多同学,因为卡口造成了冗余的提交,并且没有对此进行commit精简或者合并。为了优化这个情况,保证后续展示的 commit 信息是精准的,做了以下规则调整:格式化冗余信息(包含「代码格式化」等信息的 commit)commit 修改文件数量小于 commit 数量包含重复的 commit 信息# 检查是否有重复的提交信息commitInfoIsRepeatCheck() { ?commitInfoArr=$1 ?for ((i = 1; i lastGitCommitLogs = gitCommitLogMapper.list(new HashMap() {{ put("cvId", lastPublishComponentVersion.getId()); }}); lastGitCommitLogs.forEach(item -> { item.setId(null); item.setCvId(componentVersion.getId()); }); gitCommitLogs = lastGitCommitLogs;}since 异常组件第一次构建时,肯定没有上个打包时间,这个时候,就选择开始时间的24小时前,然后取三条信息存入即可。如果直接使用上一次的 commit date 信息,本次信息拉取会包含上次的最后一条信息,所以,使用 since 请求时,要手动加上 1001 ms 作为偏移(并发合入的情况几乎不存在)。if (lastPublishComponentVersion == null) { ? ?since = ISO8601Utils.format(new Date(componentVersion.getCommitTime() - 24 * 60 * 60 * 1000), true); ? ?count = paramConfigService.getIntValue(ParamConfigKeys.CV_GITLOG_FETCH_COUNT, 3);} else { ? ?since = ISO8601Utils.format(new Date(lastPublishComponentVersion.getCommitTime() + 1001), true);}commit 信息丢失我们提交 MR 时,由于是多人并行开发,总会出现,时间合入落后的情况。这个时候就会存在一种情况。一次 MR 中,真实的 commit 时间落在了上次发布时间外,这个时候,通过当前生成的 Merge into 新 commit 信息作为 until 去查询的话,since 时间大于了 正式 commit 时间。所以就查不出此条 commit 信息。这个时候怎么处理呢?不要忘记上面讲到的 parent_ids 参数,它其实就记录了自己父节点,那这正好就是我们需要的呢。这是一条标准的 Merge commit 信息,可以看到 parent id 对应两条,第一条永远对应主干分支的上一个 commit ,第二个,就是合入分支的 上一个 commit ,这个就是我们想要的 commit id。通过这个 commit_id ,请求一个单独的log信息即可。通过这次优化,可以处理时间异常的 commit 信息抓取。当然,这样只抓取了一条信息,可能存在丢失信息的问题,不过由于我们有CI前置卡口,MR 目前要求一个 MR 就做一件事,最好只有一个 commit 。所以这种极端情况,有一条信息,也能确定相关代码被打包到 App 中。fetchUrl = String.format("https://gitlab.poizon.com/api/v4/projects/%d/repository/commits?since=%s&until=%s&ref_name=%s", repo.getProjectId(), since, until, componentVersion.getDeployBranch());gitCommitLogs = fixWhenOnlyOneMerge(repo, fetchGitCommitLogs(fetchUrl, count));if (gitCommitLogs == null || gitCommitLogs.size() == 0) { ? ?fetchUrl = String.format("https://gitlab.poizon.com/api/v4/projects/%d/repository/commits?until=%s&ref_name=%s", repo.getProjectId(), until, componentVersion.getDeployBranch()); ? ?gitCommitLogs = fetchGitCommitLogs(fetchUrl, count); ? ?if (gitCommitLogs != null & !gitCommitLogs.isEmpty()) { ? ? ? ?final GitCommitLog firstCommitLog = gitCommitLogs.get(0); ? ? ? ?gitCommitLogs = fixWhenOnlyOneMerge(repo, new ArrayList(1) {{ ? ? ? ? ? ?add(firstCommitLog); ? ? ? ?}}); ? ?}}private List fixWhenOnlyOneMerge(Repo repo, List gitCommitLogs) { ? ?if (gitCommitLogs == null || gitCommitLogs.size() != 1) { ? ? ? ?return gitCommitLogs; ? ?} ? ?GitCommitLog firstGitCommitLog = gitCommitLogs.get(0); ? ?if (firstGitCommitLog.getParent_ids() == null || firstGitCommitLog.getParent_ids().size() != 2) { ? ? ? ?return gitCommitLogs; ? ?} ? ?String parentId = firstGitCommitLog.getParent_ids().get(firstGitCommitLog.getParent_ids().size() - 1); ? ?String fetchUrl = String.format("https://gitlab.poizon.com/api/v4/projects/%d/repository/commits/%s", repo.getProjectId(), parentId); ? ?logger.info("fixWhenOnlyOneMerge url: " + fetchUrl); ? ?Request request = new Request.Builder().url(fetchUrl).header("PRIVATE-TOKEN", "xxxxxxxxxxxxx").build(); ? ?try { ? ? ? ?Response response = getOkHttpClient().newCall(request).execute(); ? ? ? ?if (response.code() == 200) { ? ? ? ? ? ?String json = response.body().string(); ? ? ? ? ? ?logger.info("fixWhenOnlyOneMerge json: " + json); ? ? ? ? ? ?JSONObject jsonObject = JSONObject.parseObject(json); ? ? ? ? ? ?gitCommitLogs.add(jsonObjectToGitCommitLog(jsonObject)); ? ? ? ?} else { ? ? ? ? ? ?throw new RuntimeException("fixWhenOnlyOneMerge code != 200"); ? ? ? ?} ? ? ? ?return gitCommitLogs; ? ?} catch (IOException e) { ? ? ? ?logger.error("fixWhenOnlyOneMerge", e); ? ? ? ?throw new RuntimeException(e); ? ?}}App 构建优化App 构建流程主要是在构建成功后,需要进行组件信息的拉取以及聚合,然后再根据 App 的 md5 值为key进行落库存储。其中 ,md5 还需要通过 瓦力工具,更新到 App 中,这样,App才能在启动后,通过改字段去拉取到自己的Log信息。def generateChannelApk(File apkFile, File channelOutputFolder, Map nameVariantMap, channel, extraInfo, alias) { ? ?//poizon modify ? ?Map apkFileMd5Cache = nameVariantMap.computeIfAbsent("apkFileMd5Cache", { k -> new HashMap()}); ? ?def apkFileMd5 = apkFileMd5Cache.computeIfAbsent(apkFile.absolutePath, { k -> ? ? ? ?String md5 = MD5Utils.md5(IOUtils.toString(new BufferedInputStream(new FileInputStream(apkFile)))); ? ? ? ?println("add gitCommitLogKey sourceApk: ${apkFile} -> ${md5}") ? ? ? ?return md5; ? ?}); ? ?if (extraInfo == null) { ? ? ? ?extraInfo = new HashMap() ? ?} ? ?extraInfo.put("componentLogId", apkFileMd5) ? ?nameVariantMap.put("sourceApkMd5", apkFileMd5); ? ?println("----put extra componentLogId: ${apkFileMd5}----") ? ?Extension extension = Extension.getConfig(targetProject); ? ?... ? ?WallePlusExtUtils.putChannelOutput(targetProject, variant, nameVariantMap)}TestFlight 优化Testflight 的改动主要涉及列表逻辑重构,之前是直接使用 OSS 的 SDK 拉取数据。这次通过新做的接口完成统一处理。由于一个版本apk信息过多,为了减轻列表接口的负担,只做前50个apk的 log信息拉取,后面的只支持在详情中查看 Log 信息。由于新增点击跳转详情页,TestFlight 交互也有调整,主要涉及点击安装逻辑的调整。另外引入了 得物App中的网络库,后续将统一 TestFlight 和 得物App 的开发环境,实现代码通用。小结通过本次任务,更加深刻了解 git 的一些知识,顺利落地组件信息展示,做到每个App可溯源,改动可感知。同时也为我们的CI前置卡口,大家的日常代码提交反馈出更细致的问题,敦促大家严格要求自己,提高代码提交规范,为测试,开发反馈提供有效支持,为持续集成和交互提供有力保障。*文/lovejjfg
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-11 18:35 , Processed in 1.482601 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表