四川省建设厅招投标网站,wordpress的模板制作,外贸网站 设计,呼市做开发网站的公司文章目录 前言基本原理执行流程diff 报告不同分支 merge 过来的 diff 报告同个分支产生的 merge 报告同个分支提交的 diff 报告 具体实现原理我们需要监控怎样的 Dendenpency 变化怎样获取 dependency Treeproject.configurations 方式./gradlew dependenciesAsciiDependencyRe… 文章目录 前言基本原理执行流程diff 报告不同分支 merge 过来的 diff 报告同个分支产生的 merge 报告同个分支提交的 diff 报告 具体实现原理我们需要监控怎样的 Dendenpency 变化怎样获取 dependency Treeproject.configurations 方式./gradlew dependenciesAsciiDependencyReportRenderer方案选择 怎样对 dependency Tree 进行 diff 计算传统 diff 方案自定义的 diff 方案 如何找到一个基准点进行 diff 计算怎样集合 Gialab CI 进行计算 总结参考文章 前言
这篇文章其实在一年之前的时候就已经写好了。当时是在公司内部分享的作为一个监控框架。当时是想着过一段时间之后分享到技术论坛上面的没想到计划赶不上变化过完国庆被裁了。
当时忙着找工作就一直没有更新了放在笔记里面吃灰。
最近发现好久没有分享技术文章了从笔记里面找了一下就拿来分享了。
在项目开发中会有很多第三方依赖通过 gradle 引入进来的。比如 androidxDesignVersion、androidxSupportVersion、 rxjava2Version、 okhttpVersion 等第三方库。有时候第三方库改到了或者升级了我们并不能及时发现往往需要等到出问题的时候去排查的时候才发现是某个依赖版本改动导致的。
这时候其实是有点晚了如果能够提前暴露那么我们能够大大地减少风险因此我们希望能够监控起来。 基本原理
代码 merge 到 dev 分支的时候借助 gitlab ci促发 gradle task 任务自动分析 dependency 链表对比上一次打包的 dependency 链表如果发现变更了会通过 机器人进行通知。并附上最新的 commit提交作者信息需要 author 确认一下
执行流程
目前主要对 dev 分支进行监控以下几种场景会促发 diff 检查
MR 合并进 dev 分支的时候在 dev 分支直接提交代码的时候 diff 报告
diff 报告主要包括以下几种信息
作者当前 commitId 的 authorbranch 分支名commitId 当前的 commitId, baseCommitId基准 id变动依赖这里最多显示 6 行超过会截断具体变动可以见详情提交如果是 MR 合并进来的会显示 MR 链接否则会显示 commit 链接
不同分支 merge 过来的 diff 报告
检测到 Dependency 变化
分支: 573029_test
作者: 徐俊
commitId: 4844590b baseCommitId: bed4cb64
变动依赖:
\--- project :component-matrix\--- com.google.code.gson:gson:2.8.2 - 2.8.9
详情: {url}
提交:{url}/merge_requests/4425/diffs同个分支产生的 merge 报告
检测到 Dependency 变化
分支: 573029_dep_diff
作者: xujun
commitId: 16145365 baseCommitId: 4844590b
变动依赖:
\--- project :component-matrix\--- com.squareup.retrofit2:converter-gson:2.4.0 (*)
详情: {url}
提交: {url)/commit/16145365同个分支提交的 diff 报告
检测到 Dependency 变化
分支: 573029_dep_diff
作者: xujun
commitId: 19f22516 baseCommitId: 8c90d512
变动依赖:
\--- project :component-tcpcache\--- com.google.code.gson:gson:2.8.2 - 2.8.9
详情: {url}
提交: {url)/commit/16145365我们主要讲述以下几点
我们需要监控怎样的 Dendenpency 变化怎样获取 dependency Treedependency Tree 怎样做 diff如何找到基准点进行 diff 计算怎样结合 CI 进行计算
具体实现原理
我们需要监控怎样的 Dendenpency 变化
众所周知Android 的 Dependency 是通过 gradle 进行配置的如果我们在 build.gradle 下面配置了这样证明了我们依赖 recyclerview 这个库。
dependencies {implementation androidx.recyclerview:recyclerview:1.1.0 ”
}那一行代码会给我们的 Dendenpency 带来怎样的变化呢
有人说它是新增了 recyclerview 这个库。
这个说法对嘛
不全对。
因为 gradle 依赖默认是有传递性的。他还会同时引入 recyclerview 自身所依赖的库。
--- androidx.recyclerview:recyclerview:1.1.0
| --- androidx.annotation:annotation:1.1.0
| --- androidx.core:core:1.1.0
| --- androidx.customview:customview:1.1.0
| \--- androidx.collection:collection:1.0.0 - 1.1.0 (*)如果项目当中当前没有这些库的会同时导入这些库。如果项目中有这些库了库的版本比较低会升级到相应的版本。比如 collection 会从 1.0.0 升级到 1.1.0
然而这些情况就是我们往往所忽略的即使有代码 review有时候也会漏了。即使 review 待了可能下意识也只以为只引入了这个库却很难看到它背后的变化。
而这些如果带到线上去有时候会发生一些难以预测的结果因此我们需要有专门的手段来监控这些变化。能够监测到整条链路的变化而不仅仅只是 implementation androidx.recyclerview:recyclerview:1.1.0 ” 这行代码的变化
至于如果依赖的传递性可以通过 transitive、exclude 等用法做到。 可以看这些文章这里不再一一展开。
解决 Android Gradle 依赖的各种版本问题
build.gradle管理依赖的版本(传递(transitive)\排除(exclude)\强制(force)\动态版本())
怎样获取 dependency Tree
获取 dependency Tree 的话有多种方式
通过 project.configurations 这种方式获取通过 gradlew :app:dependencies task通过 AsciiDependencyReportRenderer 获取需要适配不同版本的 gradle 版本
project.configurations 方式
通过这种方式获取的他是能够获取到所有的 dependencies但是并不能看到 dependencies 的树形关系。 伪代码如下 def configuration project.configurations.getByName(debugCompileClasspath)configuration.resolvedConfiguration.lenientConfiguration.allModuleDependencies.each {def identifer it.module.iddepList.add(identifer)}./gradlew dependencies
./gradlew dependencies 会输出所有 configuration 的 Dependcency Tree。包括 testDebugImplementation、testDebugProvided、testDebugRuntimeOnly 等等
事实上我们只关心打进 APK 包里面的 dependencies。因此我们可以指定更详细的 configuration 。即
gradlew :app:dependencies --configuration releaseRuntimeClasspath这样就只会输出 Release 包 runtimeClasspath 相关的东西。
RuntimeClasspath 跟我们常用的 implementation关系大概如下 在输出的 dependencies tree 报告中我们看到的格式一般是这样的 ** 这里有几个格式需要说明一下**
x.x.x (*), 比如图中的 4.2.2(*), 该依赖已经有了将不再重复依赖x.x.x - x.x.x 该依赖的版本被箭头所指的版本代替x.x.x - x.x.x(*) 该依赖的版本被箭头所指的版本代替并且该依赖已经有了不再重复依赖
AsciiDependencyReportRenderer
AsciiDependencyReportRenderer 这个东东在不同的 gradle 版本有不同的差异需要适配一下。
如果要这种方案建议将某个版本的代码剥离出来伪代码一般如下单独集成一个库。
project.afterEvaluate {Log.i(TAG, afterEvaluate)val renderer AsciiDependencyReportRenderer()val sb StringBuilder()val f StreamingStyledTextOutputFactory(sb)renderer.setOutput(f.create(javaClass, LogLevel.INFO))val projectDetails ProjectDetails.of(project)renderer.startProject(projectDetails)// sort all dependenciesval configuration: org.gradle.api.artifacts.Configuration project.configurations.getByName(releaseRuntimeClasspath)renderer.startConfiguration(configuration)renderer.render(configuration)renderer.completeConfiguration(configuration)// finish the whole processingrenderer.completeProject(projectDetails)val textOutput renderer.textOutputtextOutput.println()Log.i(TAG, end sb is $sb)}方案选择
从上面阐述可知第一种方案 project.configurations, 通过这种方式获取的他是能够获取到所有的 dependencies但是并不能看到 dependencies 的树形关系。
第二种方案 ./gradlew dependencies 的优点是简单直接采用 gradle 原生 Task输出特定格式的文本。然后根据规律将所有的 dependency tree 提出出来。
可能有人担心 ./gradlew dependencies 的输出格式会变化。
其实还好看了几个 gradle 版本的输出格式基本都是一样的。
第三种方案 AsciiDependencyReportRenderer 的优点是可定制性高缺点是麻烦需要适配不同版本的 gradle。
最终我选择的方案是方案二。
怎样对 dependency Tree 进行 diff 计算
传统 diff 方案
可能很多人想到的方案是使用 Git diff 进行 diff 计算。但是这种方式有局限性。
当有多个修改的时候key -value 可能无法一一对应。他的 diff 类型 add、remove、 change 并不能一一对应我们 dependency add、remove、 change 的类型。
这无法达到我们想要的结果。因此我们需要整合自己的 diff 算法。
自定义的 diff 方案
这里的方案是借鉴了 JakeWharton 大神的方案在其基础之上进行了改造。
原理大概如下
分别计算当前上一次的 dependency tree用 SetList 储存分别表示为 oldPathsnewPaths接着根据 oldPaths 和 newPaths 计算出 removedTree addedTree changedTree最后根据 removedTree addedTree 计算出 diff 第一步
对于这里的依赖我们会使用 SetListString 的数据结构储存
转换之后的数据结构 这样的好处就是可以看到每一个 dependency 的全路径如果 dependency 的全路径不一样那么可以 diff 出来。
第二步 计算 remove 树 和 add 树
有了第一步的基础其实很简单直接调用 kotlin 的扩展方法 SetT.minus
如何找到一个基准点进行 diff 计算
其实这个说到底就是找到上一个 commit 提交的 diff 文件。
看是不是 MR如果是 MR我们应该找到 MR 合并前的一个 commit不是 MR 合并进来的我们直接找到上一个 commit 即可
因此我们可以借助 git 命令来处理。对于 merge request目前主要有几种情况会产生 merge request。
直接 MR 合并进来的这时候 parent 会产生两个点我们去 parent[0] 即可当前本地分支落后远程分支 且 local 分支有 commit 的时候pull 或者 push 的时候会产生一个 merge 节点这时候 parent 会产生两个点我们去 parent[1] 即可
原理图如下 怎样集合 Gialab CI 进行计算
Gialab push 或者 merge 的时候我们需要感知到接着执行特定的 task进行计算。 每个公司的 CI 可能不太一样具体可以修改一下
gradlew :{appName}:checkDepDiff总结
dependency diff 监控的原理其实不难主要是涉及到挺多方面的有兴趣的可以看一下。如果觉得对你有所帮助的话希望可以一键三连。
参考文章
https://wajahatkarim.com/2020/03/gradle-dependency-tree/
https://tomgregory.com/gradle-dependency-tree/
https://github.com/jfrog/gradle-dep-tree
http://muydev.top/2018/08/21/Analyze-Android-Dependency-Tree/