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 in a central repository so that other developers can use our artifacts in their projects. These central repositories can be available on the company intranet, 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 have 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 called upload<configurationName>
.
The Java plugin also adds the archives
configuration 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'
As we use the Java plugin, we have the archives
configuration available. When we execute the buildArchives
task, our Java code is compiled and a JAR file is created in the build/libs
directory, called gradle-sample-1.0.jar
.
To publish our JAR file, we can execute the uploadArchives
task, but we must first configure where to publish the artifact. The repositories that we have defined for the dependencies are not used to upload the artifacts. We have to define the upload repository in the uploadArchives
task. We can reference a repository that is 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 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 uploadArchives
task, the JAR file is created and copied to the libs
and upload
directories in our project root directory. An ivy.xml
configuration file is also created and copied to the directories, as follows:
$ gradle uploadArchives :compileJava :processResources :classes :jar :uploadArchives BUILD SUCCESSFUL Total time: 0.753 secs $ ls libs/ gradle-sample-1.0.jar gradle-sample-1.0.jar.sha1 ivy-1.0.xml ivy-1.0.xml.sha1 $ ls upload/ gradle-sample-1.0.jar gradle-sample-1.0.jar.sha1 ivy-1.0.xml ivy-1.0.xml.sha1
We can use all the Ivy resolvers to define upload repositories.
If we want to upload to a Maven repository, we must create a Maven Project Object Model (POM) file. The Maven POM file contains all the 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 that it can be used as groupId
of the Maven POM. The version
property is used as version
and the archivesBaseName
property is used as the artifact ID. We can invoke the uploadArchives
task to deploy our artifact, as follows:
$ gradle uploadArchives :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :jar UP-TO-DATE :uploadArchives BUILD SUCCESSFUL Total time: 1.121 secs $ ls maven/gradle/sample/gradle-sample/1.0/ gradle-sample-1.0.jar gradle-sample-1.0.jar.md5 gradle-sample-1.0.jar.sha1 gradle-sample-1.0.pom gradle-sample-1.0.pom.md5 gradle-sample-1.0.pom.sha1
The contents of the generated gradle-sample-1.0.pom
POM file 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 on which these protocols depend on:
Protocol |
Library |
|
|
|
|
|
|
|
|
|
|
|
|
|
- |
In the following sample build file, we will 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 { jcenter() } 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 settings.xml
Maven file.
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 will use the artifacts{}
script block to define a configuration closure in order 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 will create JAR files with the source code and Javadoc documentation. We will assign both JAR files as artifacts to the archives
configuration:
apply plugin: 'java' archivesBaseName = 'gradle-sample' version = '1.0' // New task to archive the source files. task sourcesJar(type: Jar) { classifier = 'sources' from sourceSets.main.allSource } // New task to archive the documentation. task docJar(type: Jar, dependsOn: javadoc) { classifier = 'docs' from javadoc.destinationDir } artifacts { // Assign the output of the sourcesJar // task to the archives configuration. archives sourcesJar // Assign the output of the docJar // task to the archives configuration. archives docJar } uploadArchives { repositories { flatDir { dirs 'upload' } } }
We can digitally sign artifacts in Gradle with the signing plugin. The plugin only has support to generate the Pretty Good Privacy (PGP) signature, 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 signing.keyId
, signing.secretKeyRingFile
, and signing.password
Gradle project properties. 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 gradle.properties
sample 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 the artifacts that we want signed. The signing plugin has a DSL that we can use to define the tasks or configurations that 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 signArchives
task to our project as we have configured that we want the archives
configuration to be signed. The signing plugin adds tasks with the sign<configurationName>
pattern 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, as follows:
$ 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 gradle-sample-1.0.jar.asc
signature file 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 sign<taskName>
naming pattern. We can execute this task to sign the output of the configured task.
The following build file has the sourcesJar
task to create a new archive with the source files of our project. We will 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 signSourcesJar
task 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 as 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 $ 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
3.143.235.23