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.
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 |
---|---|
| |
| |
| |
| |
| |
| |
- |
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.
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' } } }
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
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
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 }
3.142.135.249