Nexus私有maven庫部署和使用

2023-01-12 21:00:43

原文地址:Nexus私有maven庫部署和使用 - Stars-One的雜貨小窩

前段聖誕節前後,Jitpack網站突然崩潰了,無法下載依賴,然後過了一個星期才解決了,好在沒啥緊急的Android開發任務,沒啥影響,但是也給我了一些提醒,可能搭建個私有庫會比較保險,於是就是研究了私有庫nexus的部署和使用

介紹

nexus私有庫,支援npm,java,pythod等庫的存放,支援的也比較全面,搭建起來也十分簡單。

除此之外,還可以拿私有庫當一個映象中轉站,比如說阿里雲映象,實際上它也是將一些中央倉庫的庫都下載儲存下來了,以防中央倉庫無法存取導致依賴無法下載的問題,有個缺點就是阿里雲沒有支援Jitpack這種小眾網站

我們可以利用私有庫,把Jitpack這種網站也加入進來,也提高了下載依賴的速度和環境的設定

部署

1.下載執行

前往官方下載頁下載安裝檔案,這裡以window為例,下載了window的壓縮包檔案

之後解壓,得到兩個資料夾目錄

開啟sonatype-work\nexus3\etc\nexus.properties檔案,修改埠號,如下圖所示,我是修改為了9888埠(這裡需要執行nexus之後,待其安裝好相關環境之後才會有這個nexus.properties的檔案)

之後進入到nexus-3.45.0-01\bin命令下,開啟cmd視窗,執行命令nexus.exe /run,即可將nexus執行起來了

2.登入

執行起來後,我們存取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-releasesmaven-snapshots這兩個倉庫中即可

原理如下:

Maven中使用

1.手動上傳jar包

進入到上傳jar包的頁面

選擇一個jar檔案進行上傳

然後上傳成功,就可以檢視到我們的jar包了

2.自動上傳jar包

首先,需要maven的setting.xml檔案中設定私服的賬號和密碼

<server>
  <id>myLocalRepo</id>
  <username>admin</username>
  <password>admin</password>
</server>

PS:注意外層還有個servers標籤

之後根據你的需要,在專案裡的pom.xmlsetting.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倉庫中

出現問題

1.出現405錯誤

因為倉庫用的不是host型別的,所以導致的錯誤,更換倉庫地址即可解決問題

2.上傳出現400問題

錯誤如下提示:

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關鍵字刪除即可解決問題

3.重複釋出版本失敗

當你釋出了一個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

需要去該倉庫裡的設定進行以下設定:

4.打包後出現boot-inf資料夾

如果專案中有maven-plugin外掛的話,需要設定skip屬性為true,否則會出現打包之後出現BOOT-INF,導致引入依賴時沒法使用

Gradle中使用

Gradle主要是以上傳Android的aar包為例,如果是普通jar包,可以參考官方檔案

使用maven-publish'外掛:

Gradle7.0版本之後都必須使用這個了,但此外掛也支援舊版本的Gradle來使用

單Module釋出

這裡,假設我們只有一個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個:

  1. 修改版本號(上述指令碼裡的POM_VERSION欄位)
  2. 執行clean命令
  3. 執行編譯命令
  4. 執行釋出命令

具體步驟如下圖所示:

gradle如何依賴私有庫的話,請看下面的章節說明

多Module釋出

如果我們是想要釋出多個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,實現上面的各命令的點選操作的,不然每次釋出都要點個幾次滑鼠可太累了,但最終還是實現不了,如果有大佬路過可以在評論區指點一下,感激不盡

使用maven外掛:

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)

步驟稍微有些繁瑣,需要修改maven目錄下的setting.xml檔案,不過改一次就好

1.serve設定

首先宣告一下私有庫的賬號密碼

注意這裡的id是隨意定義的,可以自由編寫,但需要與下面的profile裡的id對應

<server>
  <id>localRepo</id>
  <username>admin</username>
  <password>admin</password>
</server>

這裡看網上的介紹說,只是打包上傳的時候maven會讀取這裡,實際上,如果私有庫沒有開放允許任何人存取(即我上面部署操作的步驟),到時候依賴的時候也會去根據id去讀取這個server標籤,否則到時候也是無法依賴的

2.profile設定

網上看其他資料,是去加了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>

3.啟用設定

<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>

依賴本地私有庫(Gradle)

如果私有庫是開了賬號限制,可以在依賴的倉庫源中設定賬號和密碼

maven{
    url 'http://xxxx:8082/artifactory/android_group'
    //下面這是賬號和密碼
    credentials{
        username=""
        password = ""
    }
}

參考