Publishing artifacts

A software project can contain artifacts that we want to publish. An artifact can be a ZIP or JAR archive file or any other file. In Gradle, we can define more than one artifact for a project. We can publish these artifacts to a central repository so other developers can use our artifacts in their projects. These central repositories can be available on a company intranet, a network drive, or via the Internet.

In Gradle, we group artifacts through configurations, just like dependencies. A configuration can contain both dependencies and artifacts. If we add the Java plugin to our project, we also get two extra tasks per configuration to build and upload the artifacts belonging to the configuration. The task to build the artifacts is called build<configurationName>, and the task to upload the artifacts is named upload<configurationName>.

The Java plugin also adds the configuration archives that can be used to assign artifacts. The default JAR artifact for a Java project is already assigned to this configuration. We can assign more artifacts to this configuration for our project. We can also add new configurations to assign artifacts in a project.

For our Java project we will define the following sample build file:

apply plugin: 'java'

archivesBaseName = 'gradle-sample'
version = '1.0'

Because we use the Java plugin we have the archives configuration available. When we execute the task buildArchives, our Java code gets compiled and a JAR file is created in the directory build/libs, with the name gradle-sample-1.0.jar.

To publish our JAR file, we can execute the task uploadArchives, but we must first configure where to publish the artifact. The repositories we have defined for dependencies are not used to upload the artifacts. We have to define the upload repository in the uploadArchives task. We can reference a repository already defined in our project or define the repositories in the task.

The following sample build file defines an upload repository at project level and at the task level:

apply plugin: 'java'

archivesBaseName = 'gradle-sample'
version = '1.0'

repositories {
    flatDir {
        name 'uploadRepository'
        dirs 'upload'
    }
}

uploadArchives {
    repositories {
        // Use repository defined in project 
        // for uploading the JAR file.
        add project.repositories.uploadRepository

        // Extra upload repository defined in
        // the upload task.
        flatDir {
            dirs 'libs'
        }
    }
}

If we invoke the task uploadArchives, the JAR file is created and copied to the libs and upload directories. An ivy.xml configuration file is also created and copied to the directories:

$ gradle uploadArchives
:compileJava
:processResources
:classes
:jar
:uploadArchives

BUILD SUCCESSFUL

Total time: 0.8 secs
$ ls upload
ivy-1.0.xml	sample-1.0.jar
sample mrhaki$ ls libs
ivy-1.0.xml	sample-1.0.jar

We can use all Ivy resolvers to define upload repositories.

Uploading to a Maven repository

If we want to upload to a Maven repository, we must create a Maven POM (Project Object Model) file. The Maven POM file contains all necessary information about our artifact. Gradle can generate the POM file for us. We must add the Maven plugin to our project in order to make this work.

We must configure the repository for our uploadArchives task via a closure argument of the mavenDeployer() method. In the following sample build file, we will define a Maven repository with the file protocol:

apply plugin: 'java'
apply plugin: 'maven'

archivesBaseName = 'gradle-sample'
group = 'gradle.sample'
version = '1.0'

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: 'file:./maven')
        }
    }
}

Note that we set the group property of our project so it can be used as the groupId of the Maven POM. The version property is used as the version and the archivesBaseName property is used as the artifact ID. We can invoke the uploadArchives task to deploy our artifact:

$ gradle uploadArchives
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:uploadArchives
Uploading: gradle/sample/gradle-sample/1.0/gradle-sample-1.0.jar to repository remote at file:./maven
Transferring 2K from remote
Uploaded 2K

BUILD SUCCESSFUL

Total time: 1.196 secs
$ ls maven/gradle/sample/gradle-sample/1.0/
gradle-sample-1.0.jar
gradle-sample-1.0.jar.sha1	
gradle-sample-1.0.pom.md5
gradle-sample-1.0.jar.md5	
gradle-sample-1.0.pom
gradle-sample-1.0.pom.sha1

The contents of the generated POM file gradle-sample-1.0.pom are as follows:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>gradle.sample</groupId>
  <artifactId>gradle-sample</artifactId>
  <version>1.0</version>
</project>

Gradle uses the native Maven ANT tasks to deploy the artifacts to a Maven repository. The file protocol is supported without any extra configuration, but if we want to use other protocols we must configure the libraries those protocols depend on.

Protocol

Library

http

org.apache.maven.wagon:wagon-http:1.0-beta-2

ssh

org.apache.maven.wagon:wagon-ssh:1.0-beta-2

ssh-external

org.apache.maven.wagon:wagon-ssh-external:1.0-beta-2

scp

org.apache.maven.wagon:wagon-scp:1.0-beta-2

ftp

org.apache.maven.wagon:wagon-ftp:1.0-beta-2

webdav

org.apache.maven.wagon:wagon-webdav-jackrabbit:1.0-beta-6

file

-

In the following sample build file, we use the scp protocol to define a Maven repository and use it to upload the project's artifact:

apply plugin: 'java'
apply plugin: 'maven'

archivesBaseName = 'gradle-sample'
group = 'gradle.sample'
version = '1.0'

configurations {
    mavenScp
}

repositories {
    mavenCentral()
}

dependencies {
    mavenScp 'org.apache.maven.wagon:wagon-scp:1.0-beta-2'
}

uploadArchives {
    repositories {
        mavenDeployer {
            configuration = configurations.mavenScp
            repository(url: 'scp://localhost/mavenRepo') {
                authentication(username: 'user', privateKey: 'id_sha')
            }
        }
    }
}

The Maven plugin also adds the install task to our project. With the install task, we can install the artifact to our local Maven repository. Gradle will use the default location of the local Maven repository or the location that is defined in a Maven settings.xml file.

Multiple artifacts

Until now, we have uploaded a single artifact to a repository. In a Gradle project, we can define multiple artifacts and deploy them. We need to define an archive task and assign it to a configuration. We use the artifacts{} script block to define a configuration closure, to assign an artifact to a configuration. The artifact is then deployed to a repository when we execute the upload task.

In the following sample build, we create JAR files with the source code and Javadoc documentation. We assign both JAR files as artifacts to the archives configuration:

apply plugin: 'java'

archivesBaseName = 'gradle-sample'
version = '1.0'

task sourcesJar(type: Jar) {
    classifier = 'sources'
    from sourceSets.main.allSource
}

task docJar(type: Jar, dependsOn: javadoc) {
    classifier = 'docs'
    from javadoc.destinationDir
}

artifacts {
    archives sourcesJar
    archives docJar
}

uploadArchives {
    repositories {
        flatDir {
            dirs 'upload'
        }
    }
}

Signing artifacts

We can digitally sign artifacts in Gradle with the signing plugin. The plugin only has support for generating Pretty Good Privacy (PGP) signatures, which is the signature format required for publication to the Maven Central Repository. To create a PGP signature we must install some PGP tools on our computers. Installation of the tools is different for each operating system. With the PGP software we need to create a key pair that we can use to sign our artifacts.

We need to configure the signing plugin with the information about our key pair. We need the hexadecimal representation of the public key, the path to the secret key ring file with our private key, and the passphrase used to protect the private key. The values of these properties are assigned to the Gradle project properties signing.keyId, signing.secretKeyRingFile, and signing.password. The values of these properties are best kept secret, so it is better to store them in our gradle.properties file in the Gradle user directory and apply secure file permissions to the file. It is best to make the file read-only for a single user.

The following sample gradle.properties file has the signing properties set. The values of the properties shown are sample values. These will be different for other users:

signing.keyId=4E12C354
signing.secretKeyRingFile=/Users/current/.gnupg/secring.gpg
signing.password=secret phassphrase

We are ready to sign our artifacts. We need to configure which artifacts we want signed. The signing plugin has a DSL we can use to define which tasks or configurations we want signed.

In our sample Java project, we have the archives configuration with artifacts of our project. To sign the artifacts, we can use the signing() method and a closure to configure that all artifacts of the archives configuration need to be signed. The following sample build file shows how we can do this:

apply plugin: 'java'
apply plugin: 'signing'

archivesBaseName = 'gradle-sample'
version = '1.0'

signing {
    sign configurations.archives
}

The signing plugin adds a new task, named signArchives, to our project, because we have configured that we want the archives configuration to be signed. The signing plugin adds tasks with the pattern sign<configurationName> to our project, for each configuration we configure to be signed.

We can invoke the signArchives task to sign our JAR artifact or use the jar task, which is automatically dependent on the signArchives task:

$ gradle signArchives
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:signArchives

BUILD SUCCESSFUL

Total time: 1.649 secs
$ ls build/libs/gradle-sample-1.0.jar*
build/libs/gradle-sample-1.0.jar	
build/libs/gradle-sample-1.0.jar.asc

Note that the signature file gradle-sample-1.0.jar.asc is placed next to the artifact.

If the artifact we want to sign is not part of a configuration, we can use the signing DSL to configure a task to be signed. The task must create an archive file in order to be used for signing. After we have configured the task to be signed, the signing plugin adds a new task with the naming pattern sign<taskName>. We can execute that task to sign the output of the configured task.

The following build file has the task sourcesJar, to create a new archive with the source files of our project. We use the signing DSL to configure our task for signing:

apply plugin: 'java'
apply plugin: 'signing'

archivesBaseName = 'gradle-sample'
version = '1.0'

task sourcesJar(type: Jar) {
    classifier = 'sources'
    from sourceSets.main.allSource
}

signing {
    sign sourcesJar
}

We can invoke the task signSourcesJar to digitally sign our JAR file with the sources of our project. The generated signature file is placed next to the JAR file in the build/libs directory. We can also invoke the assemble task to create the digitally signed JAR file, because this task is made dependent on all our archive tasks, including the signing tasks:

$ gradle signSourcesJar
:sourcesJar
:signSourcesJar

BUILD SUCCESSFUL

Total time: 0.87 secs
sample mrhaki$ ls build/libs/gradle-sample-1.0-sources.jar*
build/libs/gradle-sample-1.0-sources.jar	
build/libs/gradle-sample-1.0-sources.jar.asc

Publishing signature files

To publish our signatures to a repository, we don't have to do anything special. Gradle automatically adds the generated signature files to our archives configuration. So, if we configure the uploadArchives task with a valid repository, we only have to run the uploadArchives task to upload both our artifacts with their signature files.

The following code adds the task sourcesJar to the build file, and we assign it to the archives configuration. We configure the signing plugin to use the archives configuration to find the artifacts to sign. Finally, we configure a simple file-based repository to store the artifacts with their signature files:

apply plugin: 'java'
apply plugin: 'signing'

archivesBaseName = 'gradle-sample'
version = '1.0'

task sourcesJar(type: Jar) {
    classifier = 'sources'
    from sourceSets.main.allSource
}

artifacts {
    archives sourcesJar
}

signing {
    sign configurations.archives
}

uploadArchives {
    repositories {
        flatDir {
            dirs 'upload'
        }
    }
}

We can execute the task uploadArchives and look in the upload directory to see all the files that are created:

$ gradle uploadArchives
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:sourcesJar UP-TO-DATE
:signArchives UP-TO-DATE
:uploadArchives

BUILD SUCCESSFUL

Total time: 0.816 secs
sample mrhaki$ ls upload/
gradle-sample-1.0-sources.asc	
gradle-sample-1.0.asc		
ivy-1.0.xml
gradle-sample-1.0-sources.jar	
gradle-sample-1.0.jar

Configuring conditional signing

With the signing DSL, we can also configure a condition to determine whether signing is required, for example, defining a condition to only sign the artifacts when the project is ready to be released.

In the sample build file, we only want to sign the artifacts if the uploadArchives task is part of the Gradle task graph to be executed and if the version of the project doesn't end with the String value DEV:

apply plugin: 'java'
apply plugin: 'signing'

archivesBaseName = 'gradle-sample'
version = '1.0-DEV'

signing {
    required {
        !version.endsWith('DEV') &&
        gradle.taskGraph.hasTask('uploadArchives')
    }
    sign configurations.archives
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.54.255