【Gradle-13】SNAPSHOT版本检查

1、什么是SNAPSHOT

SNAPSHOT版本是指尚未发布的版本,是一个「动态版本」,它始终指向最新的发布工件(gav),也就是说同一个SNAPSHOT版本可以反复用来发布。

这种情况在大型app多团队的开发中比较常见,比如user模块依赖base模块,因为是在开发阶段,base模块可能会不断的修bug或者提供新能力,这时候就可以不断的发布SNAPSHOT版本提供给user模块使用,而不用发正式版本,也达不到正式版本的要求,而user模块也不用频繁的去改版本就能拉到最新的base代码了。

2、为什么要检测

这种开发效率的提升也带来一定的风险,因为SNAPSHOT版本是开发阶段的动态版本,反复发布具有覆盖的特性,如果在打release包的时候拉到最新的SNAPSHOT版本中有未验证的代码,轻则编译错误,万一带到线上去可是非常危险的。

3、检测思路

SNAPSHOT版本通常用版本号后缀表示:

javascript 复制代码
gradle:
    classpath('com.github.yechaoa.GradleX:plugin:1.3-SNAPSHOT')

maven:
    <version>1.3-SNAPSHOT</version>
  1. 获取项目中参与编译的所有依赖项;
  2. 校验依赖项的版本号;
    • 有SNAPSHOT依赖,打印出来,并打断构建流程(可选);
    • 无 则继续;

听起来并不复杂,实际上也很简单,下面来实战一下。

4、实战

4.1、获取所有依赖

  1. 先获取AppExtension。
ini 复制代码
AppExtension androidExtension = project.getExtensions().getByType(AppExtension.class);
  • 简单理解它对应的是build.gradle,而build.gradle里面有dependencies{ };
  1. 通过Variants获取Configuration。
ini 复制代码
androidExtension.getApplicationVariants().all(applicationVariant -> {
    // debug/release也可以加配置
    System.out.println(TAG + "applicationVariant.getName() = " + applicationVariant.getName());
    Configuration configuration = project.getConfigurations().getByName(applicationVariant.getName() + "CompileClasspath");
    // ...
});
  • 不同的build type它的依赖是有区别的,比如implementation和debugImplementation;
  • 获取Configuration对象是因为依赖项是在配置阶段解析的;

4.2、遍历依赖项

scss 复制代码
configuration.getResolvedConfiguration().getLenientConfiguration().getAllModuleDependencies().forEach(resolvedDependency -> {
    ModuleVersionIdentifier identifier = resolvedDependency.getModule().getId();
    if (isSnapshot(identifier.getVersion())) {
        snapshotList.add(identifier.getGroup() + ":" + identifier.getName() + ":" + identifier.getVersion());
    }
});
  • 这里我们要用getResolvedConfiguration()来获取版本决议后的依赖项;
  • 然后通过getLenientConfiguration().getAllModuleDependencies()获取所有依赖,包括依赖中的子依赖;
  • 在遍历中,我们还要获取ModuleVersionIdentifier对象,通过它获取依赖项的坐标GAV;

4.3、Snapshot校验

typescript 复制代码
    private boolean isSnapshot(String version) {
        String checkRules = "SNAPSHOT";
        return version.endsWith(checkRules) || version.contains(checkRules);
    }
  • 校验规则比较简单,就是判断是否包含SNAPSHOT字符串;
  • 符合规则的就添加到集合中,然后打印出来;

4.4、打断编译

typescript 复制代码
    private void blockBuilding() {
        String errorMassage = "检测到有SNAPSHOT版本依赖";
        throw new GradleException(errorMassage, new Exception(errorMassage));
    }

打断编译也比较简单,直接抛个异常就好了。

4.4.1、异常类型

  • GradleException:一般用于在Gradle构建过程中抛出异常,比如自定义Plugin中的逻辑错误等;
  • GradleScriptException:一般表示构建脚本(build script)执行异常,比如语法错误、解析错误等;
  • StopActionException:一般用在Task里面,表示当前Action执行异常,但是还可以继续执行下一个;
  • InvalidUserDataException:这个也是经常遇到和经常用的,一般表示无效的参数或者选项;

更多的可以去翻下源码,一般编译过程中的异常都是继承自RuntimeException。

4.5、完整代码

typescript 复制代码
    private void checkSnapshot(Project project, boolean blockSnapshot) {
        AppExtension androidExtension = project.getExtensions().getByType(AppExtension.class);
        androidExtension.getApplicationVariants().all(applicationVariant -> {
            // debug/release也可以加配置
            System.out.println(TAG + "applicationVariant.getName() = " + applicationVariant.getName());
            Configuration configuration = project.getConfigurations().getByName(applicationVariant.getName() + "CompileClasspath");

            List<String> snapshotList = new ArrayList<>();

            // 所有的依赖,包括依赖中的依赖
            configuration.getResolvedConfiguration().getLenientConfiguration().getAllModuleDependencies().forEach(resolvedDependency -> {
                ModuleVersionIdentifier identifier = resolvedDependency.getModule().getId();
                if (isSnapshot(identifier.getVersion())) {
                    snapshotList.add(identifier.getGroup() + ":" + identifier.getName() + ":" + identifier.getVersion());
                }
            });

            if (snapshotList.size() > 0) {
                snapshotList.forEach(System.out::println);
                if (blockSnapshot) {
                    blockBuilding();
                }
            } else {
                System.out.println(TAG + "无SNAPSHOT版本依赖");
            }
        });
    }

    private void blockBuilding() {
        String errorMassage = "检测到有SNAPSHOT版本依赖";
        throw new GradleException(errorMassage, new Exception(errorMassage));
    }

    private boolean isSnapshot(String version) {
        String checkRules = "SNAPSHOT";
        return version.endsWith(checkRules) || version.contains(checkRules);
    }

4.6、验证

测试找了一个androidx.core的beta版本,然后把检测规则改成beta来验证。

arduino 复制代码
implementation 'androidx.core:core:1.9.0-beta01'

然后在插件配置中加上Snapshot检查和编译打断(默认关)。

ini 复制代码
gradleX {
    checkSnapshot = true
    blockSnapshot = true
}

最终效果:

是不是还挺简单的~

在实际开发中,如果有CI/CD流程可以添加一个后置执行卡口,再补一个白名单和审批流,如果没有,也可以整个构建报告。

5、最后

如果你不想自己写,这个插件我也发布远端了,按照下面三步走,即可使用。

Step 1. Add the JitPack repository to your build file

rust 复制代码
repositories {
	...
	maven { url 'https://jitpack.io' }
}

Step 2. Add the dependency

scss 复制代码
dependencies {
    classpath('com.github.yechaoa.GradleX:plugin:1.3')
}

Step 3. Add the Plugin Id to your build file and configure the gradleX{ } dsl

ini 复制代码
plugins {
    id 'com.yechaoa.plugin.gradleX'
}

gradleX {
    printDependencies = false
    analysisSo = true
    checkSnapshot = true
    blockSnapshot = false
}

ok,以上即是本文介绍内容,学废了吗,快来三连~

6、GitHub

github.com/yechaoa/Gra...

7、相关文档

相关推荐
传人once1 小时前
多图片上传预览功能如何写
前端·css·html·jquery·html5
三少爷的鞋1 小时前
AI 时代,我们都将成为通才型开发者:只懂 Android,已经不够了
android
Eric.461 小时前
Wan‑Animate+MiniMax-H3 本地部署 AI 漫剧流水线:8G 显存动作迁移与动态分镜工程实战
前端·人工智能·comfyui·ai漫剧
库拉镜像AI牛牛3 小时前
短剧内容自动化生产:知漫剧工作室落地教程
大数据·服务器·前端·人工智能·语音识别
律宏阔3 小时前
Flutter 列表图片内存优化:CachedNetworkImage 与 ClipRRect 的取舍
前端·flutter
行者全栈架构师3 小时前
【鸿蒙心迹】相册文搜图工程化落地:HarmonyOS 7.0 端侧 AI 检索全链路实战与 5 个踩坑解法
前端
律宏阔3 小时前
Dart 3 Record:解决 Future.wait 异构返回值的类型问题
前端·flutter
Seraphina363 小时前
DVWA(XSS Reflected/Stored High,CSRF Low)
前端·xss·csrf
默_笙4 小时前
🍔 中间件不只是打日志:四个钩子、一次短路,和自带的工具
前端·javascript
程序员老赵5 小时前
Docker 部署 Dolibarr:轻松搭建开源 ERP/CRM 平台
运维·前端·后端