There are many shortcomings with the existing model and DSL for publishing. This specification describes a plan to implement a new publication model and DSL.
The basic strategy taken here is to incrementally grow the new model along-side the existing model.
Note: this spec is very much a work in progress.
AKA 'Ivy deliver'. In this instance, dependency declarations in the generated descriptors should use the resolved versions that were used to build the artifacts.
- Use a (groupId, artifactId, version) identifier that is different to defaults.
- Add some custom Ivy attributes for the module or a configuration or an artifact.
- Mark some dependencies as optional in the pom.xml.
- Map some runtime dependencies to provided scope in the pom.xml.
- I have separate API and implementation Jars that I want to publish as separate Maven modules.
- I want to publish test fixtures as a separate module.
- I produce Groovy 1.8 and Groovy 2.0 variants that I want to publish as separate modules.
- I want to map the 32-bit and 64-bit variants of a native library to
mylib-x86-1.2.dll
andmylib-amd64-1.2.dll
when publishing to an Ivy repository, and tomylib-1.2-x86.dll
andmylib-1.2-amd64.dll
when publishing to a Maven repository. - I want a consumer in a different build to use the JAR file, and a consumer in the same build to use the compiled classes dir.
- To smoke test the generated meta-data before publishing.
- I want to sign all artifacts published to a repository.
- I want to sign the artifacts only when performing a release build.
This list is not complete. There are more use cases to come.
The end goal is to introduce 2 concepts:
- A software component.
- A publication.
Both of these are defined in the dependency model spec.
A component is a logical piece of software, such as a Java library or native executable or report.
A publication is a mapping of that component to a set of artifacts and meta-data, ready to be used by some consumer project. A publication is a strongly-typed model element. There will initially be 3 types of publication:
- An Ivy publication, for publishing to an Ivy repository.
- A Maven publication, for publishing to a Maven repository.
- A local publication, for consumption within the same build.
The following sections present a series of steps that allow us to evolve towards this model.
At the end of the process described below, a project will have a set of publications that define the major outputs of the project. Each publication will be fully and independently configurable. The existing Maven deployer and Maven installer DSL will be discontinued, and the existing Configuration DSL will be split into incoming dependencies and outgoing publications.
Note: for the following discussion, all changes are @Incubating
unless specified otherwise.
- Change the
ivy-publishing
plugin so that it no longer defines any publications. - Allow
IvyPublication
instances to be added to the publications container.- Default (organisation, module, revision) to (project.group, project.name, project.version)
- Allow zero or one components to be added to an Ivy publication.
- When publishing a java library, declare the runtime dependencies and the JAR artifact in the descriptor.
- When publishing a web application, declare the WAR artifact in the descriptor.
- Include a default configuration in the descriptor.
Note: there is a breaking change in this story.
- Run
gradle publish
for a project with just theivy-publish
plugin applied. Verify nothing is published. - Publish a java project with compile, runtime and testCompile dependencies.
- Verify that the jar artifact is included in the descriptor runtime configuration.
- Verify only the compile and runtime dependencies are included in the descriptor runtime configuration.
- Publish a war project with compile, runtime, providedCompile, providedRuntime and testCompile dependencies.
- Verify that the war artifact is published and included in the descriptor runtime configuration.
- Verify that no dependencies are included in the descriptor.
- Publish multiple projects with the
java
orwar
plugin applied and project dependencies between the projects.- Verify descriptor files contain appropriate artifact and dependency declarations.
- Verify that libraries and transitive dependencies can be successfully resolved from another build.
- Cross-version test that verifies a Java project published by the current version of Gradle can be consumed by a previous version of Gradle, and vice versa.
- Add an
IvyArtifact
interface with the following attributes:name
type
extension
file
conf
classifier
- Add an
IvyArtifactSet
interface. This is a collection of objects that can be converted to a collection ofIvyArtifact
instances. - Add an
IvyConfiguration
interface. Add aconfigurations
container toIvyModuleDescriptor
- Add an
artifacts
property toIvyConfiguration
. - When publishing or generating the descriptor, validate that the (name, type, extension, classifier) attributes are unique for each artifact.
- When publishing, validate that the artifact file exists and is a file.
To customise an Ivy publication:
apply plugin: 'ivy-publish'
publishing {
publications {
ivy {
configurations {
runtime
distributions
other {
extend "runtime"
}
}
artifacts = [jar]
artifact sourceJar {
conf 'other'
}
artifact file: distZip, type: 'java-library-distribution', conf: 'other,distributions'
}
}
}
The 'artifact' creation method will accept the following forms of input:
- A PublishArtifact, that will be adapted to IvyArtifact
- An AbstractArchiveTask, that will be adapted to IvyArtifact
- Anything that is valid input to Project.file()
- Any of the previous 4, together with a configuration closure that permits setting of name, type, classifier, extension and builtBy properties
- A map with 'file' entry, that is interpreted as per Project.file(). Additional entries for 'name', 'type', 'extension', 'classifier' and 'builtBy'.
Configurations will be constructed with no value for 'visibility', 'description', 'transitive', or 'deprecated' attributes, and will inherit ivy defaults. The configuration 'extends' attribute is a string value, not validated.
Artifacts will be constructed with no attribute for 'conf' unless explicitly specified. This allows them to inherit the default ('*') in ivy.xml or take the default value from the parent element.
- Verify that
archivesBaseName
does not affect the published artifact names.
Currently, all artifact inputs map one-one with a published artifact. We should handle compound inputs that will result in the creation of multiple artifacts.
Inputs to consider:
- FileCollection
- TaskOutputs
- Task (take TaskOutputs)
- Any Iterable
Any supplied configuration closure will be applied to each created artifact.
- Unit tests for various conversions
- For both of
ivy-publish
andmaven-publish
- Publish with artifact constructed by task with multiple file outputs. Validate that the task executes before publishing, and that each output is added to the publication as an artifact.
- Publish with artifacts constructed from a Collection containing a File, Map, and Task inputs.
- Verify that configuration closure is applied to each artifact generated from a compound input.
- Verify that publication fails with artifact from TaskOutputs that includes a directory output.
- Validate the following prior to publication:
- The groupId, artifactId and version specified for a Maven publication are non-empty strings.
- The groupId and artifactId specified for a Maven publication match the regexp
[A-Za-z0-9_\\-.]+
(seeDefaultModelValidator
in Maven source) - The organisation, module and revision specified for an Ivy publication are non-empty strings.
- Each publication identifier in the build (ie every publication in every project) is unique.
- The XML actions do not change the publication identifier.
- Reorganise validation so that it is triggered by the
MavenPublisher
service and the (whatever the ivy equialent is) service. - Use a consistent exception heirarchy for publication validation failures. For example, if using an
InvalidMavenPublicationException
then add an equivalentInvalidateIvyPublicationException
.
- Publication fails for a project with no group or version defined.
- Publication coordinates can contain non-ascii characters, whitespace, XML markup and reserved filesystem characters, where permitted by the format. Verify that these publications can be resolved by Gradle.
- Reasonable error messages are given when the above validation fails.
Validate the following prior to publication:
- The extension, classifier specified for a Maven artifact are non-empty strings.
- The name, extension, type, and classifier if specified, for an Ivy artifact are non-empty strings.
- When publishing to a repository, validate that each artifact (including the meta-data file) will be published as a separate resource.
- Artifact attributes can contain non-ascii characters, whitespace, XML markup and reserved filesystem characters, where permitted by the format. Verify that these artifacts can be resolved by Gradle.
- Reasonable error messages are given when the above validation fails.
- Check that an Ant build that uses Ivy can resolve a Java library published to an Ivy repository.
- Check that an Ant build that uses Ivy can resolve a Java library published to an Maven repository.
- Check that a Maven build can resolve a Java library published to a Maven repository.
There are many cases where a repository may fail to publish the requested artifacts successfully. One example is publishing to a Windows FileRepository an artifact with version containing ":", which is illegal in a windows file name. The Maven Ant tasks (and possibly the Ivy DependencyResolver) will silently fail in these cases.
This story will address this issue, by ensuring that failure to publish is detected by the supported repository implementations, and that this failure is reported to the user.
- Replace DependencyResolverIvyPublisher with an implementation built directly on top of ExternalResourceRepository.
- No ivy concepts should be in this implementation if possible
- Reuse code from resolver for mapping artifact attributes -> primary URL
- Replace AntTaskBackedMavenPublisher with an implementation built directly on top of ExternalResourceRepository.
- Reuse Maven code for creating maven-metadata.xml if possible
- No ivy concepts should be introduced to this implementation
- Reuse code from resolver for mapping artifact attributes -> primary URL
- Update ExternalResourceRepository.put() so that it reports on any failure to publish, and ensure that these failures are reported in the publishing output.
- Create Ivy publication with version = "1:3" and publish to FileSystem repository on Windows. Assert that failure is reported.
- Publish 2 Ivy publications with versions that only differ by case to a FileSystem repository on Windows. Assert that the second publication does not overwrite the first.
- Publish an Ivy publication with extension ending in '.' to FileSystem repository on Windows. Assert that failure is reported.
- Publish an Ivy publication to an HTTP repository that returns a 500. Assert that failure is reported.
- Similar tests for Maven publications.
This step will allow some basic customisation of the meta data model for each publication:
- Add
groupId
,artifactId
,version
properties toMavenPublication
andMavenPom
. Addpackaging
property toMavenPom
. - Change
pom.xml
generation to use these properties. - Add
organisation
,module
,revision
properties toIvyPublication
andIvyModuleDescriptor
. Addstatus
property toIvyModuleDescriptor
. - Change
ivy.xml
generation to use these properties. Do not defaultstatus
toproject.status
(this value should have not effect on ivy publication). - Change the
ivy.xml
generation to prefer the (organisation, module, revision) identifier of theIvyPublication
instance from the target project for a project dependencies, over the existing candidate identifiers. - Change the
pom.xml
generation to prefer the (groupId, artifactId, version) identifier of theMavenPublication
instance from the target project for project dependencies, over the existing candidate identifiers.
To customise the
pom.xml
:apply plugin: 'maven-publish' publishing { repositories.maven { ... } publications { maven(MavenPublication) { groupId 'my-maven-group' artifactId 'my-artifact-id' version '1.2' pom.packaging 'war' } } }
Running
gradle publish
will publish to the remote repository, with the customisations. Runninggradle publishLocalMaven
will publish to the local Maven repository, with the same customisations.To customise the
ivy.xml
:apply plugin: 'ivy-publish' publishing { repositories.ivy { ... } publications { ivy(IvyPublication) { organisation 'my-organisation' module 'my-module' revision '1.2' } } }
We might also add an
ivy
andmaven
project extension as a convenience to specify defaults for all publications of the appropriate type:apply plugin: 'ivy-publish' ivy { organisation 'my-organisation' module 'my-module' revision '1.2' }
And:
apply plugin: 'maven-publish' maven { groupId 'my-group' artifactId 'my-module' version '1.2' }
- A build with project-A depends on project-B.
- Customise the Ivy module identifier and Maven coordinates of project-B.
- Publish both projects to an Ivy repository.
- Assert that another build can resolve project-A from this Ivy repository.
- Publish both projects to a Maven repository.
- Assert that another build can resolve project-A from this Maven repository.
- Run
gradle publish
for a project that defines multiple publications and verify that they are all published
This step decouples the incoming and outgoing dependency declarations, to allow each publication to include a different set of dependencies:
- Add a
MavenDependency
interface, with the following properties:groupId
(required)artifactId
(required)version
(required)type
(optional, not empty string)optional
(boolean, default to false and do not include in POM)scope
(optional, default to null and restrict values to [compile, provided, runtime, test, system])
- Add a
MavenDependencySet
concept. This is a collection ofMavenDependency
instances. - Add a
MavenDependencySet
toMavenPublication
. - Extend the
IvyDependency
to add the following properties:organisation
(required)module
(required)revision
(required)confMapping
(optional, not empty string)
- Add an
IvyDependencySet
concept. This is a collection ofIvyDependency
instances. - Add an
IvyDependencySet
toIvyPublication
.
To add dependencies to a Maven publication:
apply plugin: 'maven-publish' publishing { publications { maven(MavenPublication) { dependency "foo:bar:1.0:provided" dependency "other-group:other-artifact:1.0" { scope "compile" } dependency groupId: "some-group", artifactId: "some-artifact", version: "1.4", scope: "provided" } } }
To replace dependencies in a Maven publication:
apply plugin: 'maven-publish' publishing { publications { maven(MavenPublication) { dependencies = [ "other-group:other-artifact:1.0", {groupId: "some-group", artifactId: "some-artifact", version: "1.4", scope: "provided"} ] } } }
To add dependencies to an Ivy publication:
apply plugin: 'ivy-publish' publishing { publications { ivy(IvyPublication) { configurations { ... } dependency "some-org:some-group:1.0" // empty confMapping value dependency "some-org:some-group:1.0:confMapping" dependency organisation: "some-org", module: "some-module", revision: "some-revision", confMapping: "*->default" dependency { organisation "some-org" module "some-module" revision "1.1" // use empty confMapping value } } } }
To replace dependencies in an Ivy publication:
publishing { publications { ivy(IvyPublication) { configurations { ... } dependencies = [ "some-org:some-group:1.0:confMapping" ] } } }
The 'dependency' creation method will accept the following forms of input:
- An
ExternalModuleDependency
, that will be adapted to IvyDependency/MavenDependency - An string formatted as "groupId:artifactId:revision[:scope]" for Maven, or "organisation:module:version[:confMapping]" for Ivy
- A configuration closure to specify values for created dependency
- Either of the first 2, together with a configuration closure that permits further configuration (like adding scope/conf)
- A map that is treated as per the configuration closure.
- excludes on configuration.
- dynamic versions.
- wildcard excludes.
TBD
TBD
Provided dependencies should be included in the generated POM and ivy.xml
- Publishing Ear -> container runtime dependencies should be included.
- Publishing C++ Exe -> runtime dependencies should be included.
- Publishing C++ Lib -> runtime, link and compile-tome dependencies should be included. Artifacts should not use classifiers, header type should be 'cpp-headers', not 'zip'.
- Publishing distribition -> no dependencies should be included.
- Fix No pom published when using 'cpp-lib' plugin, due to no main artifact.
Add an SFTP resource transport and allow this to be used in an Ivy or Maven repository definition.
Add a WebDAV resource transport and allow this to be used in an Ivy or Maven repository definition.
To sign an Ivy module when it is published to the remote repository:
TBD
To sign a Maven module when publishing a release build to the remote repository:
TBD
Running
gradle release
will build, sign and upload the artifacts. Runninggradle publish
will build and upload the artifacts, but not sign them. Runninggradle publishMavenLocal
will build the artifact, but not sign them.To provide progress logging, better error reporting, better handling of authenticated repositories, etc.
- Use Maven 3 classes to generate the POM.
- Expose a Maven 3
Project
object viaMavenPom.model
. - Use Maven 3 classes to update the
maven-metadata.xml
and wire this intoMavenResolver
. - Use
MavenResolver
to publish a Maven publication. - Change legacy
MavenDeployer
to useMavenResolver
. - Remove Maven 2 as a dependency.
- Remove jarjar hacks from Maven 3 classes.
TBD
Ensure that when publishing multiple components to a given destination, that they are published in dependency order.
Fail fast when user-provided credentials are not valid.
Schedule validation tasks before publication tasks, while still respecting task dependencies.
These would be mixed in to various steps above (TBD), rather than as one change at the end. They are grouped together here for now:
- Deprecate and later remove MavenDeployer and MavenInstaller and associated classes.
- Deprecate and later remove the
Upload
task. This means the only mechanism for publishing an Ivy module is via theivy-publish
plugin. - Deprecate and later remove the
maven
plugin. This means the only mechanism for publishing a Maven module is via themaven-publish
plugin. - Deprecate and later remove support for signing a configuration and Maven deployer.
- Deprecate and later remove
Configuration.artifacts
and related types. - Change
DependendencyHandler
to become a container ofResolvableDependencies
instances. Usedependencies.compile
instead ofconfigurations.compile
- Deprecate and later remove
ResolvedConfiguration
and related types. - Deprecate and later remove
Configuration
and related types. - Deprecate and later remove support for resolving or publishing using an Ivy DependencyResolver implementation.
At any point above, and as required, more meta-data for a publication can be made available for customisation. In particular:
- Add
name
,description
,url
,licenses
,organization
,scm
,issueManagement
andmailingLists
toMavenPublication
- Add extended attributes to
IvyModuleDescriptor
,IvyConfiguration
andIvyArtifact
. - Add exclusions, inclusions, etc.
- Live collections of artifacts.
- Add a packaging to a publication, add multiple packagings to a publication.
- How to get rid of
Configuration.artifacts
? - How to map a project dependency to Ivy publication or Maven publication when generating descriptor?
- Add in local publications.
- Add Gradle descriptor.
- Move Project.repositories to Project.dependencies.repositories.
- Validation: Is is an error to call
publish
without defining any publications and/or repositories?