---
# System prepended metadata

title: Gradle 發佈
tags: [Gradle]

---

---
title: 'Gradle 發佈'
disqus: kyleAlien
---

Template
===

## Overview of Content

從 Gradle 6.0 開始，Gradle 模塊元數據會將 lvy XML、Maven POM 一起發佈

[TOC]

## 發佈 - 概念

* 發佈什麼？

    通常是文件（artifact）、元數據（metadata）；不同的倉庫會保存不同的發佈內容，像是發佈到 Maven 倉庫的數據：
    
    1. 1 ~ 多個 artifact，通常是項目建構
    
    2. Gradle 模塊元數據文件將會描述發佈的變體（variants）
    
    3. Maven POM 文件將標識主要工件及其依賴項；會產生項目的 Jar 包

* 在哪裡執行發佈？

    由 Gradle 執行發佈，發佈到某個倉庫（repositories）去，該倉庫會儲存各種套件

* 如何發佈？

    Gradle 會自動產生發佈相關的任務，透過這個些任務就可以發佈
    
    > e.g 發佈到 Maven 倉庫的任務為 `PublishToMavenRepository`
    > 
    > 發佈到 lvy 倉庫的任務為 `PublishToIvyRepository`


### 發佈相關設置

1. 首先先使用發佈的用的二進制插件（Maven、lvy），這邊以 Maven 為例

    ```groovy=
    // build.gradle

    plugins {
        id 'java-library'
        id 'maven-publish'
    }
    ```

2. 配置發佈的數據：使用 [**PublishingExtension**](https://docs.gradle.org/current/dsl/org.gradle.api.publish.PublishingExtension.html) 來設定發佈時的相關數據

    * **發佈設置** `publications`：自定義一個名為 demoLibrary 的發佈，並將其透過 [**MavenPublication**](https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.MavenPublication.html) 發佈到 Maven；
        
        透過 `components.java` 設定這次為發佈 Jar 包
        
        > `publications` 也稱為出版物
    
    * **倉庫設置** `repositories`：定義一個基於文件的 Maven 儲存庫
        
        > 一般來說是基於 Https 比較多

    ```groovy=
    // 發佈、儲存庫必須訊息
    group = 'org.example'
    version = '1.0'

    publishing {

        publications {
            // 自定義一個名為 demoLibrary 的發佈 
            demoLibrary(MavenPublication) {
                // 透過 Jar 包組件發佈
                from components.java
            }
        }


        repositories {
            maven {
                // 倉庫的名稱，可以是任意的字串，用於識別倉庫
                name = 'alienRepo'
                
                // 倉庫的 URL
                url = layout.buildDirectory.dir("repo")
            }
        }

    }
    ```
    
    在這些設置結束後，同步 Gradle 就會產生對應的任務… 
    
    > 其格式如 `publish<PubName>PublicationTo<RepoName>Repository`
    > 
    > 像上面設置 PubName=demoLibrary、RepoName=alienRepo，產生的任務就會是 publishDemoLibraryPublicationToAlienRepoRepository
    > 

    > ![](https://hackmd.io/_uploads/ByO-cgjH3.png)

    
    * 執行發佈任務後，就會將打包好的 Jar 包，以及相關資訊上傳至設定好的 URL（目前是設定發佈到 `/build/repo` 資料夾）
    
        ```shell=
        # 打包上傳命令
        
        ./gradlew publishDemoLibraryPublicationToAlienRepoRepository
        ```
    
        > ![](https://hackmd.io/_uploads/r1O6oxiSn.png)


## 插件發布

我們可以把撰寫好的二進制插件，發布到私有、公有倉庫

### 發布至 Gradle Portal

* Gradle 有提供一個簡單 DSL ([Publishing Plugin](https://plugins.gradle.org/plugin/com.gradle.plugin-publish)) 將插件發布至 Gradle Plugin Portal

    1. 首先要先有 [**Gradle 帳戶**](https://plugins.gradle.org/)（沒有請申請一個帳號）
    
    2. 將 Gradle 帳戶的個人資料 **API Keys** 中的 `gradle.publish.key`、`gradle.publish.secret` 值設定到 `HOME_DIR/.gradle/gradle.properties` 
        
        > 如果沒有 `gradle.properties` 創建，並將資料寫入
        > 
        > ![](https://hackmd.io/_uploads/HJOcC_QSh.png)


    3. 添加發布用的插件 `com.gradle.plugin-publish` 到準備發布的模塊中的 `build.gradle`
    
        ```groovy=
        // build.gradle

        plugins {
            id 'com.gradle.plugin-publish' version '1.1.0'
        }
        ```
        
    4. 設定插件參數

        ```groovy=
        group = 'com.test.plugin'
        // 版本設定
        version = '1.0'

        // 使用 Java Gradle 插件開發插件
        gradlePlugin {
            // 項目網址
            website = 'https://github.com/ysb33r/gradleTest'
            // 項目倉庫
            vcsUrl = 'https://github.com/ysb33r/gradleTest.git'
            
            // 上傳的插件參數
            plugins {
                myPlugins {
                    // 插件唯一性
                    id = "com.test.test-my-plugin"
                    // 人可讀的名稱
                    displayName = 'Test do plugin'
                    description = 'Test do plugin...'
                    tags.addAll("HelloWorld")
                    implementationClass = 'com.test.plugin.MyPluginImpl'
                }
            }
        }

        ```
        :::info
        * 這裡有幾個坑要注意
            
            * 上面有的 **元素都要設定**（`website`, `version`, `website`... 等等）
            
            * **`id` 設置有兩個條件**

                * 必須以 `com.xxx` 開頭
                
                * `com.xxx` 中的 `xxx` 必須以插件源碼路徑為準
                    
                    > 源碼包是在 `com.test.plugin`，所以 `id` 取名也需要是 `com.test` 開頭 
                    > 
                    > ![](https://hackmd.io/_uploads/SkmwxoXSh.png)

        :::
    
    5. 點擊 gradle `publishPlugin` 任務自動上傳插件

        > ![](https://hackmd.io/_uploads/SJFZ1omBn.png)


### 發布至 Maven - 插件、任務

* Gradle 也有提供一個簡單 DSL ([Maven Publish](https://plugins.gradle.org/plugin/com.gradle.plugin-publish)) 將插件發布至 Maven

    ```groovy=
    // build.gradle

    plugins {
        id 'maven-publish'
    }
    ```

* **Maven 插件也有提供幾個基礎任務**
    
    > 以下的 `PubName`、`RepoName` 兩個元素可以透過 PublishingExtension#publishing 設置

    | 任務名 | 說明 | 補充 |
    | -------- | -------- | -------- |
    | generatePomFileFor<PubName\>Publication | 為 PubName 產生一個 POM 文件，內有項目的元數據（項目名稱、版本、依賴） | 產生後默認位置為 `build/publications/$pubName/pom-default.xml` |
    | publish<PubName\>PublicationTo<RepoName\>Repository | 將 PubName 發佈到名為 RepoName 的倉庫中 | 如果沒有定義 RepoName 那 **預設為 Maven** |
    | publish<PubName\>PublicationToMavenLocal | 將 PubName 發佈到本地 Maven 緩存（包含 POM 文件） | 通常路徑為 `<當前目錄>/.m2/repository` |
    | publish | 將所有項目發佈到倉庫 | 不包括本地 Maven |
    | publishToMavenLocal | 將所有項目發佈到本地 Maven |  |

### 發布至 Maven - [publications](https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.MavenPublication.html) 設置

* 當項目發佈至 Maven 時有 4 著主要配置
    
    1. [**MavenPublication**](https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.MavenPublication.html#org.gradle.api.publish.maven.MavenPublication:from(org.gradle.api.component.SoftwareComponent))#from 設置：提供相應發佈的組件

        ```groovy=
        publishing {
          publications {
            maven(MavenPublication) {
              from components.java
            }
          }
        }
        ```
        
    2. MavenArtifact 設置（Options）：當你需要自訂 Maven artifact 時需要另外配置

        ```groovy=
        publishing {
          publications {
            maven(MavenPublication) {
              artifact sourceJar // Publish the output of the sourceJar task
              artifact 'my-file-name.jar' // Publish a file created outside of the build
              artifact source: sourceJar, classifier: 'src', extension: 'zip'
            }
          }
        }
        ```
    
    3. **基礎元數據**：如 `artifactId`、`groupId`、`version`... 等等

        * POM 基礎設置可以透過 Project 物件取得（`getGroup`、`getName`、`getVersion`）
        
            ```groovy=
            group = 'org.example'
            version = '1.0'
            ```
        
        * 而這些基礎 POM 標示也是 **可以透過 MavenPublication 覆蓋**

            ```groovy=
            publishing {
                publications {
                    maven(MavenPublication) {
                        // 覆蓋設置
                        groupId = 'org.gradle.sample'
                        artifactId = 'library'
                        version = '1.1'

                        // ... 省略部分
                    }
                }
            }
            ```
            :::success
            * 上傳到本地倉庫時，對應的資料夾結構
                
                假設是使用以下設置
                ```shell=
                groupId = 'org.gradle.sample'
                artifactId = 'library'
                version = '1.1'
                ```
                那上傳到本地 `.m2/repository` 中則會以 `.m2/repository/org/gradle/sample/1.1/library-1.1.jar` 上傳
                
                > 也就是 `.m2/repository/<groupId>/<version>/<artifactId-version>.jar
            :::
    
    4. POM 文件的其他內容，可以透過 [**MavenPublication**](https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.MavenPublication.html#org.gradle.api.publish.maven.MavenPublication:pom(org.gradle.api.Action))#pom 設置

        ```groovy=
        // build.gradle

        publishing {
            publications {
                mavenJava(MavenPublication) {
                    pom {
                        name = 'My Library'
                        description = 'A concise description of my library'
                        url = 'http://www.example.com/library'
                        properties = [
                            myProp: "value",
                            "prop.with.dots": "anotherValue"
                        ]
                        licenses {
                            license {
                                name = 'The Apache License, Version 2.0'
                                url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                            }
                        }
                        developers {
                            developer {
                                id = 'johnd'
                                name = 'John Doe'
                                email = 'john.doe@example.com'
                            }
                        }
                        scm {
                            connection = 'scm:git:git://example.com/my-library.git'
                            developerConnection = 'scm:git:ssh://example.com/my-library.git'
                            url = 'http://example.com/my-library/'
                        }
                    }
                }
            }
        }
        ```

### 發布至 Maven - [repositories](https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.repositories.MavenArtifactRepository.html) 設置

* publishing 中的 repositories 主要內容有 **url（必填）**、name（Option，如果沒有填寫則預設為 Maven）

    ```groovy=
    // build.gradle

    publishing {
        repositories {
            maven {
                // change to point to your repo, e.g. http://my.org/repo
                url = layout.buildDirectory.dir('repo')
            }
        }
    }
    ```

* 發佈快照到不同 Maven 是一種常見手法

    1. 使用版本設置判斷
        
        ```groovy=
        publishing {
            repositories {
                maven {
                    def releasesRepoUrl = layout.buildDirectory.dir('repos/releases')
                    def snapshotsRepoUrl = layout.buildDirectory.dir('repos/snapshots')
                    url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
                }
            }
        }
        ```
        
    2. 使用項目屬性設置（透過指令控制）
        
        ```groovy=
        publishing {
            repositories {
                maven {
                    def releasesRepoUrl = layout.buildDirectory.dir('repos/releases')
                    def snapshotsRepoUrl = layout.buildDirectory.dir('repos/snapshots')
                    url = project.hasProperty('release') ? releasesRepoUrl : snapshotsRepoUrl
                }
            }
        }
        ```
        
        
## Appendix & FAQ

:::info
:::

###### tags: `Gradle`
