原文地址:Nexus私有maven庫部署和使用 - Stars-One的雜貨小窩
前段聖誕節前後,Jitpack網站突然崩潰了,無法下載依賴,然後過了一個星期才解決了,好在沒啥緊急的Android開發任務,沒啥影響,但是也給我了一些提醒,可能搭建個私有庫會比較保險,於是就是研究了私有庫nexus的部署和使用
nexus私有庫,支援npm,java,pythod等庫的存放,支援的也比較全面,搭建起來也十分簡單。
除此之外,還可以拿私有庫當一個映象中轉站,比如說阿里雲映象,實際上它也是將一些中央倉庫的庫都下載儲存下來了,以防中央倉庫無法存取導致依賴無法下載的問題,有個缺點就是阿里雲沒有支援Jitpack這種小眾網站
我們可以利用私有庫,把Jitpack這種網站也加入進來,也提高了下載依賴的速度和環境的設定
前往官方下載頁下載安裝檔案,這裡以window為例,下載了window的壓縮包檔案
之後解壓,得到兩個資料夾目錄
開啟sonatype-work\nexus3\etc\nexus.properties
檔案,修改埠號,如下圖所示,我是修改為了9888埠(這裡需要執行nexus之後,待其安裝好相關環境之後才會有這個nexus.properties的檔案)
之後進入到nexus-3.45.0-01\bin
命令下,開啟cmd視窗,執行命令nexus.exe /run
,即可將nexus執行起來了
執行起來後,我們存取localhost:9888
即可進入到web頁面
點選右上角的賬號登入,提示我們可以在該路徑找到使用者名稱和密碼
賬號名為admin
,密碼則是你用記事本開啟那個admin.password
檔案裡的內容
登入成功後會提示我們輸入新密碼來修改密碼,照著走即可
下一步需要設定是否公開庫的選項,不公開的話,下載則需要設定賬號和密碼才能下載庫,我這裡就選擇了不公開(第二個選項)
之後完成就可以使用了
首先,先介紹說明的對應的倉庫資訊
maven-releases
發行版元件, hosted 型別maven-snapshots
:快照(偵錯版本)元件, hosted 型別maven-central
:maven 中央庫,就是代理 https://repo1.maven.org/maven2/,proxy 型別maven-public
:倉庫分組概念,虛擬的 把上面三個倉庫合併形成一個組,方便參照, group 型別設定maven依賴的時候,我們只需要參照maven-public的地址即可
而我們需要上傳元件,可以上傳到maven-releases
和maven-snapshots
這兩個倉庫中即可
原理如下:
進入到上傳jar包的頁面
選擇一個jar檔案進行上傳
然後上傳成功,就可以檢視到我們的jar包了
首先,需要maven的setting.xml
檔案中設定私服的賬號和密碼
<server>
<id>myLocalRepo</id>
<username>admin</username>
<password>admin</password>
</server>
PS:注意外層還有個servers標籤
之後根據你的需要,在專案裡的pom.xml
或setting.xml
中新增設定distributionManagement
標籤資訊
專案裡,則是單獨設定;而setting.xml
,則是全域性設定的
這裡我以專案裡為例,在pom.xml中加上設定
<distributionManagement>
<repository>
<id>myLocalRepo</id>
<name>本地私有庫</name>
<url>http://localhost:9888/repository/maven-releases/</url>
</repository>
<!-- 設定快照版本的倉庫上傳地址,這裡不演示了,自行修改 -->
<!-- <snapshotRepository>-->
<!-- <id>myLocalRepo</id>-->
<!-- <name>本地私有庫</name>-->
<!-- <url>http://localhost:9888/repository/maven-snapshots/</url>-->
<!-- </snapshotRepository>-->
</distributionManagement>
PS:外層還有個project標籤,注意id要與上面的serve中的id一致!
這裡如果你設定了snapshotRepository
,當你當前的pom檔案版本帶有snapshot
字尾,就會上傳到snapshotRepository
對應的倉庫裡
使用maven命令進行釋出jar包,注意專案路徑:
mvn deploy
或者直接點右側的maven選單也可以進行
這裡需要注意的是,如果你的pom檔案裡的版本是有snapshot結尾,釋出後會jar包會出現在maven-snapshots
倉庫中
因為倉庫用的不是host型別的,所以導致的錯誤,更換倉庫地址即可解決問題
錯誤如下提示:
Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy (default-deploy) on project mydemo: Failed to deploy artifacts: Could not transfer artifact site.starsone:mydemo:jar:1.0-20230105.091429-1 from/to myLocalRepo (http://localhost:9888/repository/maven-releases/): Transfer failed for http://localhost:9888/repository/maven-releases/site/starsone/mydemo/1.0-SNAPSHOT/mydemo-1.0-20230105.091429-1.jar 400 Repository version policy: RELEASE does not allow version: 1.0-20230105.091429-1
解決方案:
由於當前的pom裡面的版本帶有關鍵字snapshots
,而上傳的倉庫只能接收release版本的,所以導致的錯誤
將版本的snapshots關鍵字刪除即可解決問題
當你釋出了一個1.0版本後,然後更改了程式碼,想重新發布1.0版本,會提示報錯
Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy (default-deploy) on project mydemo: Failed to deploy artifacts: Could not transfer artifact site.starsone:mydemo:jar:1.0 from/to myLocalRepo (http://localhost:9888/repository/maven-releases/): Transfer failed for http://localhost:9888/repository/maven-releases/site/starsone/mydemo/1.0/mydemo-1.0.jar 400 Repository does not allow updating assets: maven-releases
需要去該倉庫裡的設定進行以下設定:
如果專案中有maven-plugin外掛的話,需要設定skip屬性為true,否則會出現打包之後出現BOOT-INF,導致引入依賴時沒法使用
Gradle主要是以上傳Android的aar包為例,如果是普通jar包,可以參考官方檔案
Gradle7.0版本之後都必須使用這個了,但此外掛也支援舊版本的Gradle來使用
這裡,假設我們只有一個module,如下圖目錄所示:
有一個auth的庫需要進行釋出,我們想要將此庫釋出在本地私有庫中,則需要對其的build.gradle
進行更改,如下面程式碼(省略了android閉包等部分:
plugins {
id 'com.android.library'
id 'maven-publish'
}
//定義你的私有庫地址和密碼
def NEXUS_MAVEN_URL = "http://localhost:9888/repository/maven-releases/"
def NEXUS_USERNAME = "admin"
def NEXUS_PASSWORD = "admin"
//對飲的依賴座標和描述
def POM_NAME = "webviewbase"
def POM_GROUP_ID = "site.starsone"
def POM_ARTIFACT_ID = "webviewbase"
def POM_VERSION = "1.5"
def POM_PACKAGING = "aar"
def POM_DESCRIPTION = "webviewbase基礎庫"
afterEvaluate {
publishing {
publications {
aar_pub(MavenPublication) {
//定義group和版本
group = POM_GROUP_ID
//定義構造物id
artifactId = POM_ARTIFACT_ID
version = POM_VERSION
pom {
name = POM_NAME
description = POM_DESCRIPTION
url = 'http://www.example.com/library'
//型別設定為aar
packaging = POM_PACKAGING
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'starsone'
name = 'stars-one'
email = '[email protected]'
}
}
}
//帶上依賴 ,否則會報錯
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
def scopes = [configurations.compile]
if (configurations.hasProperty("api")) {
scopes.add(configurations.api)
}
if (configurations.hasProperty("implementation")) {
scopes.add(configurations.implementation)
}
if (configurations.hasProperty("debugImplementation")) {
scopes.add(configurations.debugImplementation)
}
if (configurations.hasProperty("releaseImplementation")) {
scopes.add(configurations.releaseImplementation)
}
scopes.each { scope ->
scope.allDependencies.each {
if (it instanceof ModuleDependency) {
boolean isTransitive = ((ModuleDependency) it).transitive
if (!isTransitive) {
println "<<<< not transitive dependency: [${it.group}, ${it.name}, ${it.version}]"
return
}
}
if (it.group == "${project.rootProject.name}.libs" || it.version == 'unspecified') {
return
}
if (it.group && it.name && it.version) {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
dependencyNode.appendNode('scope', scope.name)
}
}
}
}
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
}
}
repositories {
maven {
//設定倉庫地址和賬號密碼
url = NEXUS_MAVEN_URL
credentials {
username = NEXUS_USERNAME
password = NEXUS_PASSWORD
}
//下面是動態切換release倉庫或snapshot倉庫
// def releasesRepoUrl = "$buildDir/repos/releases"
// def snapshotsRepoUrl = "$buildDir/repos/snapshots"
// url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
}
}
}
}
編寫了上述的指令碼程式碼後,及得resync一下仙姑,之後從右側的Gradle選單進入到你的模組下,展開task目錄,可以找到釋出的命令,如下圖所示
這裡釋出的話,步驟有4個:
POM_VERSION
欄位)具體步驟如下圖所示:
gradle如何依賴私有庫的話,請看下面的章節說明
如果我們是想要釋出多個Module,如果採用上述的方法,就是得在每個Module裡都加上指令碼,十分繁瑣。
研究了下,想實現Jitpack那種可以一鍵釋出所有Module的方法,最終還是不成功,於是就採用了一種稍微折中的方法,就是建立一個公共的gradle指令碼,用來發布aar,然後所有的Module去參照此指令碼即可,雖然也要對每個Module進行修改,但也算是比較折中的方法了,圖省事的話可以用替換功能進行指令碼的新增
公共指令碼nexus-maven-publish.gradle
,放在了專案的根目錄,與setting.gradle
同級別,指令碼程式碼如下:
apply plugin: 'maven-publish'
def NEXUS_MAVEN_URL = "http://localhost:9888/repository/maven-releases/"
def NEXUS_USERNAME = "admin"
def NEXUS_PASSWORD = "admin"
//與jitpack釋出的保持同個組織名
def POM_GROUP_ID = "com.github.TYKYTeam.swan-android-libray"
//版本好
def POM_VERSION = "2.1"
//aar包方式
def POM_PACKAGING = "aar"
//下面三個數值自動讀取模組名
def POM_NAME = ""
def POM_ARTIFACT_ID = ""
def POM_DESCRIPTION = ""
def depList = parent.getDependencies()
depList.getModules().each {
println "資料。。" + this.name
POM_ARTIFACT_ID = this.name
POM_NAME = this.name
}
afterEvaluate {
publishing {
publications {
aar_pub(MavenPublication) {
//定義group和版本
group = POM_GROUP_ID
//定義構造物id
artifactId = POM_ARTIFACT_ID
version = POM_VERSION
// artifact androidSourcesJar//將原始碼打包進aar,如果不需要可以去掉
// artifact androidJavadocsJar//將註釋打包進aar,如果不需要可以去掉
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
pom {
name = POM_NAME
description = POM_DESCRIPTION
url = 'http://www.example.com/library'
//型別設定為aar
packaging = POM_PACKAGING
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'com'
name = 'stars-one'
email = '[email protected]'
}
}
}
//帶上依賴 ,否則會報錯
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
def scopes = [configurations.compile]
if (configurations.hasProperty("api")) {
scopes.add(configurations.api)
}
if (configurations.hasProperty("implementation")) {
scopes.add(configurations.implementation)
}
if (configurations.hasProperty("debugImplementation")) {
scopes.add(configurations.debugImplementation)
}
if (configurations.hasProperty("releaseImplementation")) {
scopes.add(configurations.releaseImplementation)
}
scopes.each { scope ->
scope.allDependencies.each {
if (it instanceof ModuleDependency) {
boolean isTransitive = ((ModuleDependency) it).transitive
if (!isTransitive) {
println "<<<< not transitive dependency: [${it.group}, ${it.name}, ${it.version}]"
return
}
}
if (it.group == "${project.rootProject.name}.libs" || it.version == 'unspecified') {
return
}
if (it.group && it.name && it.version) {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
dependencyNode.appendNode('scope', scope.name)
}
}
}
}
}
}
repositories {
maven {
//設定倉庫地址和賬號密碼
url = NEXUS_MAVEN_URL
credentials {
username = NEXUS_USERNAME
password = NEXUS_PASSWORD
}
//下面是動態切換release倉庫或snapshot倉庫
// def releasesRepoUrl = "$buildDir/repos/releases"
// def snapshotsRepoUrl = "$buildDir/repos/snapshots"
// url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
}
}
}
}
在Module裡build.gradle
檔案裡進行參照:
buildscript {
apply from: "../nexus-maven-publish.gradle"
}
plugins {
id 'com.android.library'
id 'com.github.dcendents.android-maven'
}
//後面的省略...
PS:buildscript要放在plugins之前
釋出aar的步驟:
1.修改nexus-maven-publish.gradle
檔案裡定義的版本號POM_VERSION
注意下面的步驟執行的命令不是單個Module裡的哦!!
2.清除資料和重新編譯
3.釋出(注意和上面的單Module有所不同)
本來想著自定義一個Task,實現上面的各命令的點選操作的,不然每次釋出都要點個幾次滑鼠可太累了,但最終還是實現不了,如果有大佬路過可以在評論區指點一下,感激不盡
plugins {
id 'com.android.library'
id 'publishing'
id 'maven'
}
def NEXUS_MAVEN_URL = "http://localhost:9888/repository/maven-releases/"
def NEXUS_USERNAME = "admin"
def NEXUS_PASSWORD = "admin"
def POM_NAME = "webviewbase"
def POM_GROUP_ID = "site.starsone"
def POM_ARTIFACT_ID = "webviewbase"
def POM_VERSION = "1.0"
def POM_PACKAGING = "aar"
def POM_DESCRIPTION = "webviewbase基礎庫"
uploadArchives {
configuration = configurations.archives
repositories {
mavenDeployer {
repository(url: NEXUS_MAVEN_URL) {
authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
}
// snapshotRepository(url: NEXUS_MAVEN_SNAPSHOT_URL) {
// authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
// }
//'groupId:artifactId:version' 或 'groudId:artifactId:version@aar' 形式
pom.project {
name POM_NAME
groupId POM_GROUP_ID
artifactId POM_ARTIFACT_ID
version POM_VERSION
packaging POM_PACKAGING
description POM_DESCRIPTION
}
}
}
}
// 生成sources.jar 寫 artifacts {} 之前
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
}
artifacts {
//編譯的原始碼型別
archives androidSourcesJar
//archives androidJavadocsJar
}
android{
...
}
通過右側的gradle選單可以觸發上傳
步驟稍微有些繁瑣,需要修改maven目錄下的setting.xml
檔案,不過改一次就好
首先宣告一下私有庫的賬號密碼
注意這裡的id是隨意定義的,可以自由編寫,但需要與下面的profile裡的id對應
<server>
<id>localRepo</id>
<username>admin</username>
<password>admin</password>
</server>
這裡看網上的介紹說,只是打包上傳的時候maven會讀取這裡,實際上,如果私有庫沒有開放允許任何人存取(即我上面部署操作的步驟),到時候依賴的時候也會去根據id去讀取這個server標籤,否則到時候也是無法依賴的
網上看其他資料,是去加了mirror,但實際上:
如果mirrors下即使有多個mirror設定,實際上只會去第一個mirror裡找,找不到不會往下個mirror裡找的
除非第一個mirror因為網路原因連結不上,才會觸發到下一個mirror裡去找依賴的操作邏輯
<profile>
<id>localRepoProfile</id>
<activation>
<jdk>1.8</jdk>
</activation>
<repositories>
<repository>
<id>localRepo</id>
<name>本地私有庫</name>
<url>http://localhost:9888/repository/maven-public/</url>
<layout>default</layout>
<snapshotPolicy>always</snapshotPolicy>
</repository>
</repositories>
</profile>
<activeProfiles>
<activeProfile>localRepoProfile</activeProfile>
</activeProfiles>
之後在pom.xml裡輸入本地私有庫裡的依賴座標即可正常依賴
上面的2和3步驟,是在全域性的maven中進行設定的個更改,而還有可以單獨為某個專案去設定的,直接去修改pom.xml檔案即可,如下面例子:
<project>
...
<repositories>
<repository>
<id>localRepo</id>
<name>本地私有庫</name>
<url>http://localhost:9888/repository/maven-public/</url>
</repository>
</repositories>
</project>
如果私有庫是開了賬號限制,可以在依賴的倉庫源中設定賬號和密碼
maven{
url 'http://xxxx:8082/artifactory/android_group'
//下面這是賬號和密碼
credentials{
username=""
password = ""
}
}