How can I add/change a Maven publishing repository in a Gradle init script - gradle

I'd like an init script that lets me take arbitrary Gradle projects and change the Maven repository location that they publish artifacts to.
Adding a repository is easy enough when you edit the build file directly, just add a maven{} block inside publishing { repositories { } }. However, trying to do this generically leads to frustration and failure. I tried this:
allprojects {
beforeEvaluate {
pluginManager.withPlugin("maven-publish") {
extensions.getByType<PublishingExtension>().publications {
repositories {
maven {
url = uri("file:///my/path")
name = "myrepo"
}
}
}
}
}
}
but, no luck. No such repository appears. I suspect there is a timing issue here: although my code does run, it presumably runs after the publishing plugin has created these tasks. What I want to do is register a callback that is run before the publish plugin gets a chance to do that, but I don't know how.

Related

Publishing test fixtures with submodules, gradle

I have been looking around for a way to include test fixtures in my gradle publications.
https://developer.android.com/studio/publish-library/configure-test-fixtures#kts suggests that it should work automatically so long as the project name is set correctly, which I have done in the settings.gradle file. This seems to solve the issue in the case of https://github.com/slackhq/EitherNet/issues/44.
For context, my project is built with several sub modules and I have defined a custom publication for each (I suspect this is the clue to the issue) as shown here:
subprojects {
// ... some repos and unimportant plugin applications
tasks {
register("prepareKotlinBuildScriptModel") {}
withType<BootJar> {
enabled = false // this is enabled in the jar I wish to be bootable
}
withType<Test> {
useJUnitPlatform()
}
getByName<Jar>("jar") {
enabled = true
archiveClassifier.set("")
}
}
publishing {
publications {
create<MavenPublication>(project.name) {
version = projectVersion
artifactId = tasks.jar.get().archiveBaseName.get()
groupId = "${projectGroup}.${rootProject.name}"
from(components["kotlin"])
}
}
}
For ref, this is currently what my module structure and build.gradle looks like for the module in question:
module structure
plugins {
id("java-test-fixtures")
id("java-library")
}
dependencies {
testFixturesApi(project(":model"))
... unrelated stuff
The test fixtures work fine as internal dependencies in the project itself, but they do not get published so that they can be used in external projects.
So my question is if there is a way to bake the test fixtures into my submodule jars so they can be used in external projects?
Any input would be highly appreciated.
Tried, expected, result:
Publishing to local repo, expected the test fixtures to be bundled, they were not.

Naming deployed jar file with gradle publish?

We're looking at ditching artifactory and are intending to move to reposilite, because artifactory is just too big and maintenance-heavy for our small team.
However, when using gradle publish to deploy to reposilite, I am unable to get control of the name of the actual jar. The pushed jar is always named project-x.x.x-{build-number}.jar.
I would like to do it the way the artifactory plugin did it, which deployed a properly named snapshot jar (just project-x.x.x-SNAPSHOT.jar), letting subsequent snapshots of the same version overwrite each other, which is a more convenient arrangement for us for several reasons that I won't go into right now.
Long story short, is there any way I can tell gradle publish how to name the deployed jar exactly? Our usual setup currently looks like this:
configure<PublishingExtension> {
publications {
create<MavenPublication>("myproject") {
from(components["java"])
groupId = "our.company.myproject"
artifactId = "myproject"
artifact(sourceJar)
}
}
repositories {
maven {
credentials {
username = $mavenUser
password = $mavenPassword
}
url = uri("http://localhost:8080/snapshots") # while testing
authentication {
create<BasicAuthentication>("basic")
}
}
}
}
There isn't really a problem with that configuration. It works. All I want to do is control the exact name of the jar file being deployed in addition to what I'm doing here, and I can't find a way to do it. The jar that is put to the build/libs directory is named properly, it's just the publication throwing me a curve ball.

Changing configuration with the gretty plugin?

I haven't done anything with Gradle for a while, so it appears I've forgotten how configuration resolution works.
I'm trying to use the gretty plugin (instead of core, deprecated jetty), but I cannot seem to create a custom configuration.
I've boiled it down to a very short, simple script (using Gradle 3.4):
buildscript {
repositories {
maven {
url 'https://plugins.gradle.org/m2/'
}
}
dependencies {
classpath 'org.akhikhl.gretty:gretty:1.4.0'
}
}
plugins {
id 'org.akhikhl.gretty' version '1.4.0'
}
configurations {
fooTest
}
configurations.fooTest.each {
println it.toString()
}
It seems to not like me iterating over the fooTest configuration.
Assuming I need to know the dependencies for that configuration (I stripped that part from the code above)
What am I doing wrong here?
The script above gives me this:
org.gradle.api.InvalidUserDataException: Cannot change strategy of configuration ':fooTest' after it has been resolved.
The key point here was that I needed an unresolved configuration to loop over. Admittedly this information was neglected in the initial description as I didn't know it was critical information. We needed to loop over the files in the dependency and copy/unzip them into certain locations.
However, we cannot do that with a resolved configuration. That said, we can copy the configuration into a unresolved one, and loop over that instead:
configurations.fooTest.copy().each {
println it.toString()
}
This will successfully print out the files involved in the dependency (or unzip them, as my case needs).

Override a gradle option defined in the master project

I'm adding an upload archive directory for all subprojects via
uploadArchives {
repositories {
flatDir {dirs '../REPO'}
}
}
Now I need to specify a different directory for one subproject.
I've found out that doing it adds to the list, but I'd like to replace the directory. I know I could use subprojects.findAll, but I'll need the possibility to override a setting elsewhere, too.
Disclaimer: My question may sound stupid, but I'm using gradle since a few weeks and must confess, I know hardly anything about it. I like it and it works fine, but reading the manual is not an option (I'm just a BFU and I'd rather switch to makefile before reading it all).
Often, the cleanest solution is to configure things the right way from the start. There are several ways to do this. For example:
rootProject/build.gradle:
configure(subprojects - project(":foo")) {
uploadArchives {
repositories {
flatDir {dirs '../REPO'}
}
}
}
project(":foo") {
uploadArchives {
repositories {
flatDir {dirs '../OTHER'}
}
}
}
Alternatively, you could have two auxiliary scripts gradle/repoA.gradle and gradle/repoB.gradle, and each subproject build script would apply the appropriate script with apply from: "$rootDir/gradle/repoX.gradle".
Finally, overriding the value might work too (untested):
rootProject/subproject/build.gradle:
uploadArchives.repositories[0].dirs = ["../OTHER"]
PS: Now go and learn some Gradle. :-)

How can plugin programmatically configure maven-publish publishing and allow build.gradle to modify it

I have project wide settings in a plugin, called parent, that attempts to apply the maven-publish plugin and then programmatically configure the publishing extension. This seems to work but when I apply this plugin in a build.gradle script I can not configure publishing extension to set the project specific publications.
I receive the error:
Cannot configure the 'publishing' extension after it has been accessed.
My intent was to set up the publishing repository in the parent plugin and then let each build.gradle script add the appropriate publications.
Is there a way to do this?
Currently ParentPlugin.groovy looks like:
def void apply(Project project) {
project.getProject().apply plugin: 'maven-publish'
def publishingExtension = project.extensions.findByName('publishing')
publishingExtension.with {
repositories {
maven {
mavenLocal()
credentials {
username getPropertyWithDefault(project.getProject(), 'publishUserName', 'dummy')
password getPropertyWithDefault(project.getProject(), 'publishPassword', 'dummy')
}
}
}
}
}
My client build.gradle fails when it tries to configure the publishing extension.
apply plugin: 'parent'
publishing {
publications {
mavenJava(MavenPublication) {
groupId 'agroup'
artifactId 'anartifactid'
version '1.0.0-SNAPSHOT'
from components.java
}
}
}
Is this possible? Is there another way I should be approaching this?
NOTE regarding repositories{} and publications{} for plugin maven-publish:
Topic: How to workaround this perplexing gradle fatal error message:
Cannot configure the 'publishing' extension after it has been accessed
First thing to try (deep magic):
(note "project." prefix is optional)
-- Configure publications and repositories not like this:
project.publishing {publications {...}}
project.publishing {repositories {...}}
but instead like this recommended style:
project.publishing.publications {...}
project.publishing.repositories {...}
It would be instructive for a gradle guru to explain why this trick works.
Another known workaround is to make sure that each apply of plugin
maven-publish is in the same project code block as
project.publishing.repositories and project.publishing.publications.
But that is more complex and harder to do than the first thing to try,
since by default the CBF applies maven-publish and a second apply of it
may itself cause the same error.
maven-publish is normally applied in pub/scripts/publish-maven.gradle,
unless PUB_PUBLISH_MAVEN is set to override that file location,
in which case the caller should apply plugin maven-publish.
See https://orareview.us.oracle.com/29516818 for how this not-preferred
workaround can be done (for project emcapms) while still using the CBF.
P.S. Someday I'll write this up with minimal code examples. But I'm putting this hard-won knowedge out there now to save other folks from wasting days on this common maven-publish issue.
To deal with this, I wrote another plugin, which can delay modifications to the publication while also avoid a "reading" of the extension, which would put it in the "configured" state. The plugin is called nebula-publishing-plugin, the code for the "lazy" block can be found in the github repo. It looks like this:
/**
* All Maven Publications
*/
def withMavenPublication(Closure withPubClosure) {
// New publish plugin way to specify artifacts in resulting publication
def addArtifactClosure = {
// Wait for our plugin to be applied.
project.plugins.withType(PublishingPlugin) { PublishingPlugin publishingPlugin ->
DefaultPublishingExtension publishingExtension = project.getExtensions().getByType(DefaultPublishingExtension)
publishingExtension.publications.withType(MavenPublication, withPubClosure)
}
}
// It's possible that we're running in someone else's afterEvaluate, which means we need to run this immediately
if (project.getState().executed) {
addArtifactClosure.call()
} else {
project.afterEvaluate addArtifactClosure
}
}
You would then call it like this:
withMavenPublication { MavenPublication t ->
def webComponent = project.components.getByName('web')
// TODO Include deps somehow
t.from(webComponent)
}
The plugin is available in jcenter() as 'com.netflix.nebula:nebula-publishing-plugin:1.9.1'.
A little bit late, but I found a solution that does not require an additional plugin:
(This has been taken from one of my internal plugins, that can work with old and new publishing, thus the ...withType... stuff.
instead of:
project.plugins.withType(MavenPublishPlugin) {
project.publishsing {
publications {
myPub(MavenPublication) {
artifact myJar
}
}
}
}
do this:
project.plugins.withType(MavenPublishPlugin) {
project.extensions.configure PublishingExtension, new ClosureBackedAction( {
publications {
myPub(MavenPublication) {
artifact myJar
}
}
})
}
This will not resolve the Extension immediately, but will apply the configuration at the time when it gets first resolved by someone.
Of course it would perfectly make sense to use this style of configuration in your project-wide plugin to configure the repositories and use the publication extension in the build scripts as usual. This would avoid confusion for buildscript authors.

Resources