前言
目前最为流行的Java项目构建工具当属Apache Maven了。但是也有另外一种构建工具Gradle在逐步的变得强大。与此同时,很多公司会使用gitlab作为内部代码管理的工具,这样会带来一个问题,如果项目使用gradle进行构建,如何使用gitlab发布jar包呢?
一些必备的知识点
在实现过程中会使用到一些必须的知识点,首先简要介绍下。
Gradle
Gradle官方文档:Gradle User Manual
如果说gradle很强大,可能会有人认为口说无凭,个人认为Google将gradle作为编译Android的默认构建工具这个案例就可以充分证明gradle的强大。
什么是gradle
先来看下官方的定义。What is Gradle?
Gradle is an open-source build automation tool focused on flexibility and performance. Gradle build scripts are written using a Groovy or Kotlin DSL. Read about Gradle features to learn what is possible with Gradle.
Highly customizable — Gradle is modeled in a way that is customizable and extensible in the most fundamental ways.
Fast — Gradle completes tasks quickly by reusing outputs from previous executions, processing only inputs that changed, and executing tasks in parallel.
Powerful — Gradle is the official build tool for Android, and comes with support for many popular languages and technologies.
大意是:Gradle 是一个专注于灵活性和性能的开源构建自动化工具。 Gradle 构建脚本是使用 Groovy 或 Kotlin DSL 编写的。
- 高度可定制——Gradle 以最基本的方式可定制和可扩展的方式建模。
- 快速——Gradle 通过重用先前执行的输出、仅处理更改的输入以及并行执行任务来快速完成任务。
- 功能强大 — Gradle 是 Android 的官方构建工具,支持多种流行语言和技术。
build java Library
关于gradle的一些语法,由于不是本文的重点暂时不过多的介绍,使用起来并不复杂,个人认为比Maven更加的方便,有兴趣的话,可以参考官方文档:Building Java Libraries Sample (gradle.org)
gradle vs maven
下图是官方对gradle和maven编译过程的对比图。
可以从以下四个方面将gradle和maven进行对比:
| 特性 | maven | gradle |
|---|---|---|
| 灵活性 | Maven 使用XML进行配置,对模型有着严格的要求,必须基于定义的schema。 | 更为灵活方便。 |
| 性能 | 支持并行构建、并行解析依赖 | 支持并行构建、并行解析依赖。支持 work avoidance 以及 incrementality仅仅编译构建变化的数据支持对构建结果的缓存,甚至在不同的机器中Daemon机制,可以维护一个持久的守护进程,一直保证此进程处于激活状态,无需频繁的创建构建线程。更多的可以参见:gradle.org/features/#p… |
| 用户体验 | 编写xml代码 | 使用grovy的DSL编写配置文件 |
| 依赖管理 | 本地缓存依赖依赖覆盖,仅支持version覆盖无依赖范围管理冲突管理,基于最短路径查找允许发布者通过可选依赖项提供元数据,但仅作为文档。 | 本地缓存依赖可自定义的依赖选择和替换规则,可以声明一次并在项目范围内处理不需要的依赖。 这种替换机制使 Gradle 能够一起构建多个源项目以创建复合构建。有丰富的依赖范围管理功能冲突管理,完全冲突解决,选择图中找到的最高版本使用。允许生产者声明 api 和 implementation 依赖项,以防止不需要的库泄漏到消费者的类路径中。 |
gitlab-ci
认证方式
gitlab认证相关的官网地址:docs.gitlab.com/ee/user/pac…
主要包含以下三种方法:To authenticate to the Package Registry, you need one of the following:
- A personal access token with the scope set to api.
- A deploy token with the scope set to read_package_registry, write_package_registry, or both.
- A CI_JOB_TOKEN.
Runner debug
CICD过程中,经常仅仅通过job日志有时候很难定位到问题,可以借助于gitlab提供的Debug功能。在job运行起来后,右侧会出现【Debug】连接,点击进入后,可以进入此次ci过程中的docker镜像容器。
Linux三剑客
awk、grep、sed是linux操作文本的三大利器,合称文本三剑客,拥有强大的功能。
三者的功能都是处理文本,但侧重点各不相同,属awk功能最强大,但也最复杂。
- grep更适合单纯的查找或匹配文本。
- sed更适合编辑匹配到的文本。
- awk更适合格式化文本,对文本进行较复杂格式处理。
有很多介绍三剑客的文章,这里就不再赘述,自行google即可。
实现过程
使用gradle构建并不意味这不使用maven私有仓库,相反,解决问题的思路应该是如何将使用gradle构建的jar包发布的maven仓库中。
修改build.gradle
添加对maven-publish的依赖
apply plugin: 'maven-publish'
配置publishing
publishing {
repositories {
maven {
name = "mavenRepository"
url = uri("releaseurl")
credentials {
username = "username"
password = "#gradleCredentialsPwdValue#"
}
}
}
}
添加gradle.properties
有时候公司内部环境中gitlab并不能直接访问公网,但是使用gradle时会下载一些依赖这时候如何解决呢?答案是可以使用公司提供的代理。
接下来要做的事情就是gradle如何配置代理呢?有两种方式
- 在gradle.properties中配置proxy
- 在命令后直接拼接代理属性
完整的gradle.properties可用配置项可参考官方文档:Build Environment (gradle.org)
实际操作相关配置如下。
systemProp.http.proxyHost=proxyurl
systemProp.http.proxyPort=proxyport
systemProp.https.proxyHost=proxyurl
systemProp.https.proxyPort=proxyport
这里再额外提出两个配置org.gradle.daemon和org.gradle.parallel,主要目的是为了提高gradle的build性能,从配置名称可以看出来,一个是使用daemon运行,一个是开启并行处理。如下配置即可开启。
org.gradle.daemon=true
org.gradle.parallel=true
实测下来,build效率提升缺失很高,hollow工程从3-5min提高到了20s。
添加 .gitlab-ci.yml
【.gitlab-ci.yml】文件可以配置相关gitlab的ci执行过程,所有的过程在stages中定义。
image: docker:lasted
stages:
- build
# - test
- deploy
before_script:
# - echo `pwd` # debug
# - echo "$CI_BUILD_NAME, $CI_BUILD_REF_NAME $CI_BUILD_STAGE" # debug
- export GRADLE_USER_HOME=`pwd`/.gradle
cache:
paths:
- .gradle/wrapper
- .gradle/caches
build:
stage: build
script:
- chmod +x ./gradlew
- ./gradlew assemble
# - gradle assemble
artifacts:
paths:
- build/libs/*.jar
# expire_in: 1 week
# only:
# - master
#test:
# stage: test
# script:
# - ./gradlew check
deploy:
stage: deploy
script:
- sed -i 's/#gradleCredentialsPwdValue#/'$gradleCredentialsPwd'/g' build.gradle
- chmod +x ./gradlew
- ./gradlew publish
# only:
# - master
after_script:
- echo "End CI"
一些注意点:
image使用stable或lasted等类似tag,进行下载,而无需指定具体image。build和deploy可以指定具体的分支,上述配置暂时是关闭的test可以执行具体的单元测试等,由于开源项目原因,暂时也是关闭了,如果需要也可以自行开启。
gitlab Variables 配置
我们选在使用在build.gradle文件配置账号密码的方式来进行认证,为保证密码的保密性,可以在本仓库下配置Variables,来隐藏密码。这里务必选择marked类型的变量。
cicd
经过上述一顿操作后,会有两个passed的job,就说明整个过程成功了。
为保险起见,可以查看具体的job执行日志,看看是否都运行正常。
Running with gitlab-runner 13.7.0~beta.113.g406f5c9e (406f5c9e)
on Kubernetes Runner qHRCkywE
Preparing the "kubernetes" executor
00:00
Using Kubernetes namespace: pro-gitlabci
WARNING: Pulling GitLab Runner helper image from Docker Hub. Helper image is migrating to registry.gitlab.com, for more information see https://docs.gitlab.com/runner/configuration/advanced-configuration.html#migrating-helper-image-to-registrygitlabcom
Using Kubernetes executor with image hub.cloud.com/devops/centos7.2-javaopenjdk1.8.0_202-maven3.3.9:0.0.7 ...
Preparing environment
00:03
Waiting for pod pro-gitlabci/runner-qhrckywe-project-93543-concurrent-0d2vcr to be running, status is Pending
Running on runner-qhrckywe-project-93543-concurrent-0d2vcr via gitlab-runner-7b7d5d469d-z74lr...
Getting source from Git repository
00:02
Fetching changes...
Initialized empty Git repository in /builds/resource/hollow/.git/
Created fresh repository.
Checking out 50d747d5 as mhlin...
Skipping Git submodules setup
Restoring cache
00:09
Checking cache for default...
Downloading cache.zip from http://gitlabci.com/gitlabci/project/93543/default
Successfully extracted cache
Executing "step_script" stage of the job script
00:28
$ export GRADLE_USER_HOME=`pwd`/.gradle
$ chmod +x ./gradlew
$ ./gradlew assemble
Welcome to Gradle 6.8.3!
Here are the highlights of this release:
- Faster Kotlin DSL script compilation
- Vendor selection for Java toolchains
- Convenient execution of tasks in composite builds
- Consistent dependency resolution
For more details see https://docs.gradle.org/6.8.3/release-notes.html
Starting a Gradle Daemon (subsequent builds will be faster)
locking FileBasedConfig[/root/.config/jgit/config] failed after 5 retries
> Task :assemble UP-TO-DATE
> Task :hollow:compileJava
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
> Task :hollow:processResources NO-SOURCE
> Task :hollow:classes
> Task :hollow:createPropertiesFileForJar
> Task :hollow:writeManifestProperties
> Task :hollow:jar
> Task :hollow:javadoc SKIPPED
> Task :hollow:javadocJar
> Task :hollow:assemble
> Task :hollow-test:compileJava
Note: /builds/resource/hollow/hollow-test/src/main/java/com/netflix/hollow/test/consumer/TestHollowConsumer.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
> Task :hollow-ui-tools:compileJava
> Task :hollow-diff-ui:compileJava
> Task :hollow-diff-ui:processResources
> Task :hollow-diff-ui:classes
> Task :hollow-diff-ui:createPropertiesFileForJar
> Task :hollow-diff-ui:writeManifestProperties
> Task :hollow-diff-ui:jar
> Task :hollow-diff-ui:javadoc SKIPPED
> Task :hollow-diff-ui:javadocJar
> Task :hollow-diff-ui:assemble
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
> Task :hollow-explorer-ui:compileJava
> Task :hollow-explorer-ui:processResources
> Task :hollow-explorer-ui:classes
> Task :hollow-explorer-ui:createPropertiesFileForJar
> Task :hollow-explorer-ui:writeManifestProperties
> Task :hollow-explorer-ui:jar
> Task :hollow-explorer-ui:javadoc SKIPPED
> Task :hollow-explorer-ui:javadocJar
> Task :hollow-explorer-ui:assemble
> Task :hollow-jsonadapter:compileJava
> Task :hollow-jsonadapter:processResources NO-SOURCE
> Task :hollow-jsonadapter:classes
> Task :hollow-jsonadapter:createPropertiesFileForJar
> Task :hollow-jsonadapter:writeManifestProperties
> Task :hollow-jsonadapter:jar
> Task :hollow-jsonadapter:javadoc SKIPPED
> Task :hollow-jsonadapter:javadocJar
> Task :hollow-jsonadapter:assemble
> Task :hollow-perf:compileJava
> Task :hollow-perf:processResources NO-SOURCE
> Task :hollow-perf:classes
> Task :hollow-perf:createPropertiesFileForJar
> Task :hollow-perf:writeManifestProperties
> Task :hollow-perf:jar
> Task :hollow-perf:javadoc SKIPPED
> Task :hollow-perf:javadocJar
> Task :hollow-perf:assemble
> Task :hollow-test:processResources NO-SOURCE
> Task :hollow-test:classes
> Task :hollow-test:createPropertiesFileForJar
> Task :hollow-test:writeManifestProperties
> Task :hollow-test:jar
> Task :hollow-test:javadoc SKIPPED
> Task :hollow-test:javadocJar
> Task :hollow-test:assemble
> Task :hollow-ui-tools:processResources NO-SOURCE
> Task :hollow-ui-tools:classes
> Task :hollow-ui-tools:createPropertiesFileForJar
> Task :hollow-ui-tools:writeManifestProperties
> Task :hollow-ui-tools:jar
> Task :hollow-ui-tools:javadoc SKIPPED
> Task :hollow-ui-tools:javadocJar
> Task :hollow-ui-tools:assemble
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
> Task :hollow-zenoadapter:compileJava
> Task :hollow-zenoadapter:processResources NO-SOURCE
> Task :hollow-zenoadapter:classes
> Task :hollow-zenoadapter:createPropertiesFileForJar
> Task :hollow-zenoadapter:writeManifestProperties
> Task :hollow-zenoadapter:jar
> Task :hollow-zenoadapter:javadoc SKIPPED
> Task :hollow-zenoadapter:javadocJar
> Task :hollow-zenoadapter:assemble
BUILD SUCCESSFUL in 27s
42 actionable tasks: 42 executed
Running after_script
00:00
Running after script...
$ echo "End CI"
End CI
Saving cache for successful job
00:25
Creating cache default...
.gradle/wrapper: found 13085 matching files and directories
.gradle/caches: found 1910 matching files and directories
Uploading cache.zip to http://gitlabci.com/gitlabci/project/93543/default
Created cache
Uploading artifacts for successful job
00:00
Uploading artifacts...
WARNING: build/libs/*.jar: no matching files
ERROR: No files to upload
Job succeeded
Running with gitlab-runner 13.7.0~beta.113.g406f5c9e (406f5c9e)
on Kubernetes Runner qHRCkywE
Preparing the "kubernetes" executor
00:00
Using Kubernetes namespace: pro-gitlabci
WARNING: Pulling GitLab Runner helper image from Docker Hub. Helper image is migrating to registry.gitlab.com, for more information see https://docs.gitlab.com/runner/configuration/advanced-configuration.html#migrating-helper-image-to-registrygitlabcom
Using Kubernetes executor with image hub.cloud.com/devops/centos7.2-javaopenjdk1.8.0_202-maven3.3.9:0.0.7 ...
Preparing environment
00:04
Waiting for pod pro-gitlabci/runner-qhrckywe-project-93543-concurrent-0kz9kf to be running, status is Pending
Running on runner-qhrckywe-project-93543-concurrent-0kz9kf via gitlab-runner-7b7d5d469d-6drkb...
Getting source from Git repository
00:01
Fetching changes...
Initialized empty Git repository in /builds/resource/hollow/.git/
Created fresh repository.
Checking out 50d747d5 as mhlin...
Skipping Git submodules setup
Restoring cache
00:09
Checking cache for default...
Downloading cache.zip from http://gitlabci.com/gitlabci/project/93543/default
Successfully extracted cache
Executing "step_script" stage of the job script
$ export GRADLE_USER_HOME=`pwd`/.gradle
$ sed -i 's/#gradleCredentialsPwdValue#/'$gradleCredentialsPwd'/g' build.gradle
$ chmod +x ./gradlew
$ ./gradlew publish
Welcome to Gradle 6.8.3!
Here are the highlights of this release:
- Faster Kotlin DSL script compilation
- Vendor selection for Java toolchains
- Convenient execution of tasks in composite builds
- Consistent dependency resolution
For more details see https://docs.gradle.org/6.8.3/release-notes.html
Starting a Gradle Daemon (subsequent builds will be faster)
locking FileBasedConfig[/root/.config/jgit/config] failed after 5 retries
> Task :publish UP-TO-DATE
> Task :hollow:compileJava
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
> Task :hollow:processResources NO-SOURCE
> Task :hollow:classes
> Task :hollow:createPropertiesFileForJar
> Task :hollow:writeManifestProperties
> Task :hollow:jar
> Task :hollow:javadoc SKIPPED
> Task :hollow:javadocJar
> Task :hollow:generateMetadataFileForNebulaPublication
> Task :hollow:generatePomFileForNebulaPublication
> Task :hollow:sourceJar
> Task :hollow:publishNebulaPublicationToMavenRepositoryRepository
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-256. This will not fail the build.
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-512. This will not fail the build.
> Task :hollow:publish
> Task :hollow-test:compileJava
Note: /builds/resource/hollow/hollow-test/src/main/java/com/netflix/hollow/test/consumer/TestHollowConsumer.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
> Task :hollow-ui-tools:compileJava
> Task :hollow-diff-ui:compileJava
> Task :hollow-diff-ui:processResources
> Task :hollow-diff-ui:classes
> Task :hollow-diff-ui:createPropertiesFileForJar
> Task :hollow-diff-ui:writeManifestProperties
> Task :hollow-diff-ui:jar
> Task :hollow-diff-ui:javadoc SKIPPED
> Task :hollow-diff-ui:javadocJar
> Task :hollow-diff-ui:generateMetadataFileForNebulaPublication
> Task :hollow-diff-ui:generatePomFileForNebulaPublication
> Task :hollow-diff-ui:sourceJar
> Task :hollow-diff-ui:publishNebulaPublicationToMavenRepositoryRepository
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-256. This will not fail the build.
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-512. This will not fail the build.
> Task :hollow-diff-ui:publish
> Task :hollow-explorer-ui:compileJava
> Task :hollow-explorer-ui:processResources
> Task :hollow-explorer-ui:classes
> Task :hollow-explorer-ui:createPropertiesFileForJarNote: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
> Task :hollow-explorer-ui:writeManifestProperties
> Task :hollow-explorer-ui:jar
> Task :hollow-explorer-ui:javadoc SKIPPED
> Task :hollow-explorer-ui:javadocJar
> Task :hollow-explorer-ui:generateMetadataFileForNebulaPublication
> Task :hollow-explorer-ui:generatePomFileForNebulaPublication
> Task :hollow-explorer-ui:sourceJar
> Task :hollow-explorer-ui:publishNebulaPublicationToMavenRepositoryRepository
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-256. This will not fail the build.
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-512. This will not fail the build.
> Task :hollow-explorer-ui:publish
> Task :hollow-jsonadapter:compileJava
> Task :hollow-jsonadapter:processResources NO-SOURCE
> Task :hollow-jsonadapter:classes
> Task :hollow-jsonadapter:createPropertiesFileForJar
> Task :hollow-jsonadapter:writeManifestProperties
> Task :hollow-jsonadapter:jar
> Task :hollow-jsonadapter:javadoc SKIPPED
> Task :hollow-jsonadapter:javadocJar
> Task :hollow-jsonadapter:generateMetadataFileForNebulaPublication
> Task :hollow-jsonadapter:generatePomFileForNebulaPublication
> Task :hollow-jsonadapter:sourceJar
> Task :hollow-jsonadapter:publishNebulaPublicationToMavenRepositoryRepository
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-256. This will not fail the build.
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-512. This will not fail the build.
> Task :hollow-jsonadapter:publish
> Task :hollow-perf:compileJava
> Task :hollow-perf:processResources NO-SOURCE
> Task :hollow-perf:classes
> Task :hollow-perf:createPropertiesFileForJar
> Task :hollow-perf:writeManifestProperties
> Task :hollow-perf:jar
> Task :hollow-perf:javadoc SKIPPED
> Task :hollow-perf:javadocJar
> Task :hollow-perf:generateMetadataFileForNebulaPublication
> Task :hollow-perf:generatePomFileForNebulaPublication
> Task :hollow-perf:sourceJar
> Task :hollow-perf:publishNebulaPublicationToMavenRepositoryRepository
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-256. This will not fail the build.
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-512. This will not fail the build.
> Task :hollow-perf:publish
> Task :hollow-test:processResources NO-SOURCE
> Task :hollow-test:classes
> Task :hollow-test:createPropertiesFileForJar
> Task :hollow-test:writeManifestProperties
> Task :hollow-test:jar
> Task :hollow-test:javadoc SKIPPED
> Task :hollow-test:javadocJar
> Task :hollow-test:generateMetadataFileForNebulaPublication
> Task :hollow-test:generatePomFileForNebulaPublication
> Task :hollow-test:sourceJar
> Task :hollow-test:publishNebulaPublicationToMavenRepositoryRepository
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-256. This will not fail the build.
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-512. This will not fail the build.
> Task :hollow-test:publish
> Task :hollow-ui-tools:processResources NO-SOURCE
> Task :hollow-ui-tools:classes
> Task :hollow-ui-tools:createPropertiesFileForJar
> Task :hollow-ui-tools:writeManifestProperties
> Task :hollow-ui-tools:jar
> Task :hollow-ui-tools:javadoc SKIPPED
> Task :hollow-ui-tools:javadocJar
> Task :hollow-ui-tools:generateMetadataFileForNebulaPublication
> Task :hollow-ui-tools:generatePomFileForNebulaPublication
> Task :hollow-ui-tools:sourceJar
> Task :hollow-ui-tools:publishNebulaPublicationToMavenRepositoryRepository
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-256. This will not fail the build.
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-512. This will not fail the build.
> Task :hollow-ui-tools:publish
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
> Task :hollow-zenoadapter:compileJava
> Task :hollow-zenoadapter:processResources NO-SOURCE
> Task :hollow-zenoadapter:classes
> Task :hollow-zenoadapter:createPropertiesFileForJar
> Task :hollow-zenoadapter:writeManifestProperties
> Task :hollow-zenoadapter:jar
> Task :hollow-zenoadapter:javadoc SKIPPED
> Task :hollow-zenoadapter:javadocJar
> Task :hollow-zenoadapter:generateMetadataFileForNebulaPublication
> Task :hollow-zenoadapter:generatePomFileForNebulaPublication
> Task :hollow-zenoadapter:sourceJar
> Task :hollow-zenoadapter:publishNebulaPublicationToMavenRepositoryRepository
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-256. This will not fail the build.
Cannot upload checksum for module-maven-metadata.xml because the remote repository doesn't support sha-512. This will not fail the build.
> Task :hollow-zenoadapter:publish
Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.8.3/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 2m 5s
74 actionable tasks: 74 executed
Running after_script
00:01
Running after script...
$ echo "End CI"
End CI
Saving cache for successful job
Creating cache default...
.gradle/wrapper: found 13085 matching files and directories
.gradle/caches: found 1937 matching files and directories
Uploading cache.zip to http://gitlabci.com/gitlabci/project/93543/default
Created cache
Job succeeded
总结
经过这次踩坑实践,发现gradle以及gitlab有很多优秀的特性,之前探究的并不够,鉴于此有必要进一步进行分析和研究下。今天就写到这里吧。
参考文献
- stackoverflow.com/questions/6…
- stackoverflow.com/questions/1…
- github.com/actions/sta…
- docs.gitlab.com/ee/user/pac…
- docs.gradle.org/current/use…
- stackoverflow.com/questions/5…
- docs.gitlab.com/ee/ci/yaml/
- docs.gitlab.com/ee/ci/yaml/…
- gitlab.com/gitlab-org/…
- docs.gitlab.com/ee/ci/examp…
- gist.github.com/daicham/5ac…
- gogradle.github.io/gogradle/do…
- jhooq.com/make-gradle…
- levelup.gitconnected.com/publish-you…
- c.biancheng.net/view/4028.h…
- stackoverflow.com/questions/5…
- gradle.org/gradle-vs-m…
- gradle.org/maven-vs-gr…