# 背景

我们的项目标配使用gradle进行编译,我们也配置了nexus作为私服,理论上来说,不应该再会出现因包依赖的原因导致编译失败的情况,但是这个问题仍旧会偶尔发生,我解决过一两次,只看到通常失败的编译都是在开头的时候去 plugins.gradle.org 拉包的情况,当时将该问题存疑,今天特来探析一下导致这个问题的根因。

# 摸索

简单分析: 理论上来说,当我们的gradle通过build.gradlerepositories指向私服地址之后,就不应该再出现任何一个第三方的私服地址才对,但 gradle 在这里就是异常头铁,偏偏会在开头请求一下官方地址。这是彼时遇到问题之后的一个原始的疑问!

缘起: 今天一位开发同学反馈说某项目在gray环境打包的时候出现编译失败的情况。我看到构建日志如下:

+ gradle --no-daemon bootJar

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring root project 'example'.
> Could not resolve all artifacts for configuration ':classpath'.
   > Could not resolve org.springframework.boot:spring-boot-gradle-plugin:2.1.5.RELEASE.
     Required by:
         project : > org.springframework.boot:org.springframework.boot.gradle.plugin:2.1.5.RELEASE
      > Could not resolve org.springframework.boot:spring-boot-gradle-plugin:2.1.5.RELEASE.
         > Could not parse POM https://plugins.gradle.org/m2/org/springframework/boot/spring-boot-gradle-plugin/2.1.5.RELEASE/spring-boot-gradle-plugin-2.1.5.RELEASE.pom
            > Could not resolve org.springframework.boot:spring-boot-tools:2.1.5.RELEASE.
               > Could not resolve org.springframework.boot:spring-boot-tools:2.1.5.RELEASE.
                  > Could not get resource 'https://plugins.gradle.org/m2/org/springframework/boot/spring-boot-tools/2.1.5.RELEASE/spring-boot-tools-2.1.5.RELEASE.pom'.
                     > Could not HEAD 'https://plugins.gradle.org/m2/org/springframework/boot/spring-boot-tools/2.1.5.RELEASE/spring-boot-tools-2.1.5.RELEASE.pom'.
                        > Remote host closed connection during handshake

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

通常遇到这种问题,我都会到对应构建的 Jenkins 服务器上,手动跑一次编译,然后 ci 机器再次打包就能过去了。这次使用同样手法将问题解决之后,我打算探究一下导致该问题的根因是什么。

后来了解到运维侧在打包的机器上有一个定时清理本地缓存的操作,是为了避免一些包依赖不更新的问题。所以当缓存清空之后,这些依赖就会重新下载,就可能因为网络原因而失败。

首先看到了报错提示的包基本都是 plugins,于是看了一眼 build.gradle 文件,可以看到在文件中有这样的配置声明:

plugins {
    id 'org.springframework.boot' version '2.1.5.RELEASE'
    id 'java'
}

1
2
3
4

报错的提示也正是在拉取该插件的时候报错的。

所以,这里得到一个问题是:即便已经配置指向了私服,但是此处的 plugins 中的包拉取的时候仍旧绕过了私服。

这是为什么呢?

通过 Google,找到了官方文档 (opens new window) 对 plugins 功能的介绍,并且里边也介绍了plugins 的仓库指向 (opens new window),需要通过单独的配置文件进行配置:

$ cat settings.gradle # 注意需要把配置文件放到这个文件的开头,而非build.gradle中
pluginManagement {
    repositories {
        maven {
            url './maven-repo'
        }
        gradlePluginPortal()
        ivy {
            url './ivy-repo'
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12

这里我做了下测试,将如下配置添加到了项目对应的配置中:

$ cat settings.gradle
pluginManagement {
    repositories {
        //本地资源库
        mavenLocal()
        //自定义maven仓库
        maven {
            name 'aifocus-repository'
            url 'http://nexus.eryajf.net/nexus/content/groups/public/'
        }
    }
}

......

1
2
3
4
5
6
7
8
9
10
11
12
13
14

接着执行构建,发现如下报错:

FAILURE: Build failed with an exception.

* Where:
Build file '/data/gradle/example/build.gradle' line: 2

* What went wrong:
Plugin [id: 'org.springframework.boot', version: '2.1.5.RELEASE'] was not found in any of the following sources:

- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Plugin Repositories (could not resolve plugin artifact 'org.springframework.boot:org.springframework.boot.gradle.plugin:2.1.5.RELEASE')
  Searched in the following repositories:
    MavenLocal(file:/root/.m2/repository)
    aifocus-repository(http://nexus.eryajf.net/nexus/content/groups/public/)

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 3s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

在私服仓库中,找不到这个包,需要注意的是,虽然通过org.springframework.boot 能够搜索到包,但是这个包还有gradle.plugin的 path,所以如下检索到的包并不符合条件。

那么就需要将官方地址代理进来,此处要注意一个坑点在于,官方代理的正确地址是 https://plugins.gradle.org/m2/ 而非 https://plugins.gradle.org ,此处踩坑可见该帖子 (opens new window)

国内一些镜像库也有 plugin 的镜像,所以这里直接配置了阿里的镜像仓库:https://maven.aliyun.com/repository/gradle-plugin/

当我把官方地址加入到私服之后,再次回来进行构建,可以看到这个插件通过我们的私服进行拉取了,而正确拉取之后,私服当中也缓存了该依赖,符合预期,喜大普奔!

申明

原创文章eryajf,未经授权,严禁转载,侵权必究!此乃文中随机水印,敬请读者谅解。

# 结论

这个问题以后就变得简单了,所有使用 gradle 打包的项目,只要使用到了 plugins,那么就应该在 settings.gradle 文件的开头,加上如下配置:

pluginManagement {
    repositories {
        //本地资源库
        mavenLocal()
        //自定义maven仓库
        maven {
            name 'aifocus-repository'
            url 'http://nexus.eryajf.net/nexus/content/groups/public/'
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11

从此不会再有与 plugins.gradle.org 这个域名交互的情况出现,自然也与编译失败说拜拜了!