Set repositories and version/revison dynamically in gradle - gradle

I have a gradle buildscript. In this script I set some publications.
Later in the script I have a task which reads the Buildnumber from a file and increases it. Than the version of the project will be changed.
Now my question: Is it possible to change the revision/version after initialising the "Publishing"-PlugIn? If I doesn't set the new version, the "Publishing"-PlugIn will throw n error. If I change the version by editing the descriptor, the Plugin says, that it's not allowed to edit the descriptor directly.
I also want to change the repository-url, based on the build number.
Does anybody know a fix or had the same problem?
publishing {
publications {
ivy(IvyPublication) {
organisation project.group
module project.name
revision project.version
descriptor.status = 'milestone'
from components.java
artifact(sourceJar) {
type "source"
conf "runtime"
}
}
maven(MavenPublication) {
groupId project.group
artifactId project.name
version project.version
from components.java
}
}
repositories {
ivy {
// change to point to your repo, e.g. http://my.org/repo
url "P:/Java/Repo/ivy"
}
maven {
// change to point to your repo, e.g. http://my.org/repo
url "P:/Java/Repo/maven"
}
}
}
Here is my script for increasing the build number
def incVersion(project) {
project.versionInced = true
def versionPropsFile = file("${project.rootDir}/version.properties")
if (!versionPropsFile.canRead()) {
versionPropsFile.createNewFile();
}
def Properties versionProps = new Properties()
versionProps.load(new FileInputStream(versionPropsFile))
if(versionProps['build_version'] == null)
{
versionProps['build_version'] = 0;
}
def code = versionProps['build_version'].toInteger()+1;
versionProps['build_version']=code.toString()
versionProps.store(versionPropsFile.newWriter(), null)
project.projectInfos.version = project.version + "." + code.toString()
project.version = project.projectInfos.version
println "Version: "+project.version
return project.version
}

If I understood well, yes it can be done dynamically. You can pass a version while running gradle via project property (-P) or via system property (-D).
It will be:
gradle <some_task> -PsomeVersion=<version>
You need to alter the gradle script to read the property, so:
publishing {
publications {
ivy(IvyPublication) {
revision project.hasProperty('someVersion') ? project.someVersion : '<HERE YOU NEED TO PUT DEFAULT VERSION OR MAYBE THROW EXCEPTION IF EMPTY>'
//...
}
}
}
If you won't check if project has the property (using hasProperty method on project instance), MissingPropertyException will be thrown.

Related

How to consolidate imported plugins to custom plugin in Gradle using Kotlin

I have microservices that will share some of the same configuration between all of them, mainly Jib, publish, and release. Not sure if it's possible to do the same for dependencies but it would be beneficial to include actuator and log4j2 in each. Here is the build.gradle.kts for one of my projects.
import net.researchgate.release.BaseScmAdapter
import net.researchgate.release.GitAdapter
import net.researchgate.release.ReleaseExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("com.gorylenko.gradle-git-properties") version "1.5.1"
id("com.google.cloud.tools.jib") version "1.6.1"
id("io.spring.dependency-management") version "1.0.7.RELEASE"
id("net.researchgate.release") version "2.8.1"
id("org.sonarqube") version "2.7.1"
id("org.springframework.boot") version "2.1.6.RELEASE"
kotlin("jvm") version "1.2.71"
kotlin("plugin.spring") version "1.2.71"
jacoco
`maven-publish`
}
java.sourceCompatibility = JavaVersion.VERSION_1_8
springBoot {
buildInfo {
group = project.properties["group"].toString()
version = project.properties["version"].toString()
description = project.properties["description"].toString()
}
}
repositories {
maven(url = uri(project.properties["nexus.url.gateway"].toString()))
mavenCentral()
}
dependencies {
// Kotlin
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// Spring
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-log4j2")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.cloud:spring-cloud-config-server")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
dependencyManagement {
imports {
mavenBom("org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR3")
}
}
configurations.all {
exclude(group = "ch.qos.logback", module = "logback-classic")
exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
}
tasks {
withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "1.8"
}
}
build { dependsOn(clean) }
afterReleaseBuild { dependsOn(publish) }
publish { dependsOn(build) }
jibDockerBuild { dependsOn(build) }
jacocoTestReport {
reports {
html.isEnabled = false
xml.isEnabled = true
}
}
}
publishing {
publications {
create<MavenPublication>(project.name) {
from(components["java"])
pom {
scm {
connection.set("scm:git:git#github.com:company/${project.name}.git")
developerConnection.set("scm:git:git#github.com:company/${project.name}.git")
url.set("https://github.com/company/${project.name}/")
}
}
versionMapping {
usage("java-api") {
fromResolutionOf("runtimeClasspath")
}
usage("java-runtime") {
fromResolutionResult()
}
}
}
}
repositories {
maven {
val releasesRepoUrl = "${project.properties["nexus.url.publish"].toString()}/releases"
val snapshotsRepoUrl = "${project.properties["nexus.url.publish"].toString()}/snapshots"
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
}
fun ReleaseExtension.git(configureFn : GitAdapter.GitConfig.() -> Unit) {
(propertyMissing("git") as GitAdapter.GitConfig).configureFn()
}
release {
scmAdapters = mutableListOf<Class<out BaseScmAdapter>> ( GitAdapter::class.java )
git {
requireBranch = "develop"
pushToRemote = project.properties["release.git.remote"].toString()
pushReleaseVersionBranch = "master"
tagTemplate = "${project.name}.${project.version}"
}
}
jib {
from {
image = "openjdk:8-jdk-alpine"
}
to {
image = "host:port/${project.name}:${project.version}"
auth {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
container {
workingDirectory = "/"
ports = listOf("8080")
environment = mapOf(
"SPRING_OUTPUT_ANSI_ENABLED" to "ALWAYS",
"SPRING_CLOUD_BOOTSTRAP_LOCATION" to "/path/to/bootstrap.yml"
)
useCurrentTimestamp = true
}
setAllowInsecureRegistries(true)
}
I was able to get a custom plugin created and added to this project using git#github.com:klg71/kotlintestplugin.git and git#github.com:klg71/kotlintestpluginproject.git but I have no idea how to implement these existing plugins and their configurations. In the main Plugin class in the apply function I am able to call the project.pluginManager.apply(PublishingPlugin::class.java) which causes the task to show in the project referencing the custom plugin but I can't figure out how to configure it and it does not successfully publish to the nexus server. I can publish the plugin itself to the nexus server and reference it in the microservice but it skips running the task, which I assume is caused by the configuration not being included. Also, when trying to apply/configure the Jib plugin, all of the classes are not visible when attempting to import.
So the above answer isn't super long and to preserve the issues I ran into I am posting a new answer.
PLUGIN
This portion of the answer is going to discuss the actual custom plugin project.
Because the plugins wrapper in the build.gradle.kts is runtime, the CustomPlugin.kt does not have access to it at compile time. My boss who is much smarter than me was kind enough to point this out to me even though he has never worked with gradle. Although I looked pretty dumb in front of him he still got me up and running by basically following the 'legacy' way of applying plugins in gradle.
plugins { // This is a runtime script preventing plugins declared here to be accessible in CustomPlugin.kt but is used to actually publish/release this plugin itself
id("net.researchgate.release") version "2.8.1"
kotlin("jvm") version "1.3.0"
`maven-publish`
}
repositories {
maven { url = uri("https://plugins.gradle.org/m2/") } // This is required to be able to import plugins below in the dependencies
jcenter()
}
dependencies {
compile(kotlin("stdlib"))
compile(kotlin("reflect"))
// These must be declared here (at compile-time) in order to access in CustomPlugin.kt
compile(group = "gradle.plugin.com.gorylenko.gradle-git-properties", name = "gradle-git-properties", version = "2.2.0")
compile(group = "gradle.plugin.com.google.cloud.tools", name = "jib-gradle-plugin", version = "1.7.0")
compile(group = "net.researchgate", name = "gradle-release", version = "2.8.1")
compile(group = "org.asciidoctor", name = "asciidoctor-gradle-plugin", version = "1.5.9.2")
compile(group = "org.jetbrains.dokka", name = "dokka-gradle-plugin", version = "0.9.18")
compile(group = "org.sonarsource.scanner.gradle", name = "sonarqube-gradle-plugin", version = "2.8")
implementation(gradleApi()) // This exposes the gradle API to CustomPlugin.kt
}
This allowed me to have access to jib and everything else in the CustomPlugin.kt.
The plugins jacoco and maven-publish are automatically accessible in the plugin project but still need to be added in the microservice project referencing the plugin. I was unable to find a workaround for this unfortunately.
I included the typical maven-publish plugin in the build.gradle.kts to push to nexus with the publishing task configurations in the build.gradle.kts as well so I could pull this from nexus in the microservice that wanted to use the plugin.
publishing {
publications {
create<MavenPublication>(project.name) {
from(components["java"])
pom {
scm {
connection.set("scm:git:git#github.com:diendanyoi54/${project.name}.git")
developerConnection .set("scm:git:git#github.com:diendanyoi54/${project.name}.git")
url.set("https://github.com/diendanyoi54/${project.name}/")
}
}
}
}
repositories {
maven {
val baseUrl = "https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository"
url = uri(if (version.toString().endsWith("SNAPSHOT")) "$baseUrl/maven-snapshots" else "$baseUrl/maven-releases")
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
}
Lastly, you want to make sure you include the properties file that will tell the microservices where the plugin class is. In Intellij's IDEA, when typing the path to the implementation-class it auto completed for me.
The name of this file should reflect apply(plugin = "string") in the microservice's build.gradle.kts.
IMPLEMENTATION
This portion of the answer is going to reflect the microservice project that will be referencing the plugin. As stated above, jacoco and maven-publish still need to be added to the plugin block in the build.gradle.kts for some reason (I think because they are official gradle plugins).
To reference the plugin from the nexus server it was published to, the microservice must reference it in the buildscript.
buildscript { // Custom plugin must be accessed by this buildscript
repositories {
maven {
url = uri("https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository/maven-public")
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
dependencies { classpath("com.company:kotlin-consolidated-plugin:1.0.0-SNAPSHOT") }
}
Lastly, the plugin must be applied using the properties file name referenced above.
apply(plugin = "com.company.kotlinconsolidatedplugin") // Custom plugin cannot reside in plugin declaration above
I created sample projects of these and posted them to Github so feel free to clone or take a look:
git#github.com:diendanyoi54/kotlin-consolidated-plugin.git
git#github.com:diendanyoi54/kotlin-consolidated-plugin-implementation.git
I was able to successfully able to use the github repo examples referenced above to accomplish what I needed with the publish task. Here is my custom plugin's build.gradle.kts.
plugins {
id("com.google.cloud.tools.jib") version "1.6.1"
id("org.sonarqube") version "2.7.1"
kotlin("jvm") version "1.3.0"
`maven-publish`
}
dependencies {
compile(kotlin("stdlib"))
compile(kotlin("reflect"))
implementation(gradleApi())
}
repositories {
jcenter()
}
publishing {
publications {
create<MavenPublication>(project.name) {
from(components["java"])
pom {
scm {
connection.set("scm:git:git#github.com:company/${project.name}.git")
developerConnection.set("scm:git:git#github.com:company/${project.name}.git")
url.set("https://github.com/company/${project.name}/")
}
}
}
}
repositories {
maven {
val baseUrl = "https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository"
url = uri(if (version.toString().endsWith("SNAPSHOT")) "$baseUrl/maven-snapshots" else "$baseUrl/maven-releases")
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
}
Here is the CustomPlugin.kt class.
package com.company.gradlemicroserviceplugin
//import com.google.cloud.tools.jib.gradle.JibExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.plugins.PublishingPlugin
import org.gradle.testing.jacoco.tasks.JacocoReport
import java.net.URI
open class CustomPlugin : Plugin<Project> {
override fun apply(project: Project) {
// applySonar(project)
applyPublish(project)
// applyJib(project)
}
// private fun applySonar(project: Project) {
// project.pluginManager.apply("sonarqube")
// val task = project.task("jacocoTestReport") as JacocoReport
// task.reports = JacocoReport()
// jacocoTestReport { This was nested in the tasks declaration in build.gradle.kts so the fields below are the fields I'm trying to set in task.reports
// reports {
// html.isEnabled = false
// xml.isEnabled = true
// }
// }
// }
private fun applyPublish(project: Project) {
project.pluginManager.apply(PublishingPlugin::class.java)
val publishingExtension = project.extensions.findByType(PublishingExtension::class.java)
val mavenPublication = publishingExtension?.publications?.create(project.name, MavenPublication::class.java)
publishingExtension?.repositories?.maven {
val baseUrl = "https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository"
it.url = URI(if (project.version.toString().endsWith("SNAPSHOT")) "$baseUrl/maven-snapshots" else "$baseUrl/maven-releases")
it.credentials { cred ->
cred.username = project.properties["nexus.user"].toString()
cred.password = project.properties["nexus.password"].toString()
}
}
mavenPublication?.from(project.components.findByName("java"))
mavenPublication?.pom?.scm {
it.connection.set("scm:git:git#github.com:company/${project.name}.git")
it.developerConnection.set("scm:git:git#github.com:company/${project.name}.git")
it.url.set("https://github.com/company/${project.name}/")
}
}
// private fun applyJib(project: Project) {
// project.pluginManager.apply(JibPlugin::class.java)
//
// }
}
There are definitely areas of improvement on this but at least I got something working here. There is maven-publish logic in both build.gradle.kts because I push to the custom plugin to nexus and the maven-publish logic is in the CustomPlugin.kt class so the microservice that references this plugin can use it. However, I am unable to successfully setup Jib and Sonar. Jib doesn't give me access to anything in com.google.cloud.tools.jib.gradle preventing me from using the same approach as I used in maven-publish.
For Sonar I think I'm on the right track with retrieving the task by its name but I'm unable to set any fields that belong to task.reports because they are all final and this is necessary for Sonar to properly analyze Kotlin.
Applying built-in plugins
plugins {
java
id("jacoco")
}
You can also use the older apply syntax:
apply(plugin = "checkstyle")
Applying external plugins
plugins {
id("org.springframework.boot") version "2.0.1.RELEASE"
}
i am not good in kotlin but here is link to better understanding missing migration guide to the Gradle Kotlin DSL

How can I invoke Gradle's repository search manually?

As a precondition for building a release version, say acme-widgets-1.0, I would like to search the configured repositories (or a specific repository) to make sure acme-widgets-1.0.jar hasn't already been published. How can I do this with Gradle? Alternatively, is there a way to configure a not-exists dependency for a specific version?
group = 'com.acme'
version = '1.0'
repositories {
jcenter()
}
if (gradle.startParameter.taskNames.contains("release")) {
def taskNames = gradle.startParameter.taskNames
taskNames.add(0, "checkReleaseDoesntExist")
gradle.startParameter.taskNames = taskNames
}
task checkReleaseDoesntExist() {
}
task release() {
println "Building release"
}
checkReleaseDoesntExist.doLast {
println "Checking repositories to make sure release hasn't already been built"
// TODO What do I do here?
}
You could declare a configuration with only the artifacts for which you are trying to see if they are already published.
Upon resolving this configuration, you would have an empty fileset or not.
So something along the lines of:
configurations {
checkRelease { transitive = false }
}
dependency {
checkRelease "$group:$name:$version"
}
task ('checkReleaseDoesntExist') {
doLast {
println "Checking repositories to make sure release hasn't already been built"
try {
if (!configurations.checkRelease.files.isEmpty()) {
// already exists
}
}
catch (ResolveException e) {
// doesn't exist
}
}

Gradle - publish artifacts

I want to publish artifacts to ivy repository but it doesn't work. I read this article and after read created this sample build:
task ivyPublishTest << {
def buildDir = new File("build")
buildDir.mkdirs()
def fileToPublish = new File("build/file.abc")
fileToPublish.write("asdasdasd")
}
artifacts {
archives(ivyPublishTest.fileToPublish) {
name 'gradle-test-artifact'
builtBy ivyPublishTest
}
}
uploadArchives {
repositories {
ivy {
url "http://my.ivy.repo/ivyrep/shared"
}
}
}
Of course the problem is that it doesn't work. I get this error Could not find property 'fileToPublish' on task ':ivyPublishTest'
In Groovy, def creates a local variable, which is lexically scoped. Therefore, fileToPublish is not visible outside the task action. Additionally, configuration has to be done in the configuration phase (i.e. the declaration and assignment of fileToPublish in your task action would come too late). Here is a correct solution:
task ivyPublishTest {
// configuration (always evaluated)
def buildDir = new File("build")
ext.fileToPublish = new File("build/file.abc")
doLast {
// execution (only evaluated if and when the task executes)
buildDir.mkdirs()
fileToPublish.write("asdasdasd")
}
}
artifacts {
// configuration (always evaluated)
archives(ivyPublishTest.fileToPublish) {
name 'gradle-test-artifact'
builtBy ivyPublishTest
}
}
ext.fileToPublish = ... declares an extra property, a new property attached to an existing object that's visible everywhere the object (task in this case) is visible. You can read more about extra properties here in the Gradle User Guide.

Why do different parts of my build scripts see different values for the project.version property?

I do the following in gradle in allprojects section...
if (project.hasProperty('myVersion')) {
project.ext.realVersion = project.myVersion
project.version = project.myVersion
println("project version set")
} else {
project.ext.realVersion = 'Developer-Build'
project.version = 'Developer-Build'
println("project version set to devbuild")
}
Now, I have some code that correctly uses project.version and it works, BUT then there is other code that is ALSO using the same property project.versoin and the result is 'unspecified'. If I change both to project.realVersion, they both work. version seems to be this nasty special property that doesn't always seem to work.
The code using the properties is below....(notice where I use realVersion, version does NOT work, but it works fine in the other location :( )....how weird.
task versionFile() << {
File f = new File('output/version');
f.mkdirs()
File v = new File(f, 'version'+project.ext.realVersion)
println('v='+v.getAbsolutePath())
v.createNewFile()
}
task myZip(type: Zip) {
archiveName 'dashboard-'+project.version+'.zip'
from('..') {
include 'webserver/run*.sh'
include 'webserver/app/**'
include 'webserver/conf/**'
include 'webserver/play-1.2.4/**'
include 'webserver/public/**'
include 'webserver/lib/**'
}
from('output/version') {
include '**'
}
}
myZip.dependsOn('versionFile')
assemble.dependsOn('myZip')
The problem has nothing to do with the version property in particular. Build scripts are evaluated sequentially. If you can't guarantee that you are setting the version property before you are reading it, you have to defer reading the property until the end of the configuration phase. Otherwise you'll inevitably run into problems. One way to do this is to put the configuration code that reads the property into a gradle.projectsEvaluated {} block. task.doFirst {} is another way, but has the limitation that the configured value won't be considered for up-to-date checking.
Sometimes there is an easier solution. For example, in the case of archive tasks like Zip, you can just set baseName and extension instead of archiveName. As always, I encourage you to study the DSL reference.
Place the following in doFirst like below:
allprojects {
doFirst {
if (project.hasProperty('myVersion')) {
project.ext.realVersion = project.myVersion
project.version = project.myVersion
println("project version set")
} else {
project.ext.realVersion = 'Developer-Build'
project.version = 'Developer-Build'
println("project version set to devbuild")
}
}
}

version numbers in gradle

Anyone know how to get this working in gradle for a SUBPROJECT so that running the left hand size results in the right hand archive name...
gradle assemble -> databus-Developer-Build.zip
gradle -DmyVersion=1.0.2 assemble -> databus-1.0.2.zip
OR
gradle -PmyVersion=1.0.2 assemble -> databas-1.0.2.zip
I have tried and tried with a variable myVersion but it always says it does not exist....and in half the cases, it shouldn't exist because I didn't supply it!!!!!
How to get this working in a subproject?
thanks,
Dean
Add following snippet to allprojects configuration section of root build.gradle:
allprojects {
if (project.hasProperty('myVersion')) {
project.version = project.myVersion
} else {
project.version = 'Developer-Build'
}
}
and than pass your version in command line as -PmyVersion=1.0.2. Tested with rather old Gradle 1.0-milestone-3
proper way to do it in newer versions worked...(notice the ext addition).
if (project.hasProperty('myVersion')) {
project.ext.xVersion = project.myVersion
} else {
project.ext.xVersion = 'Developer-Build'
}
and interestingly enough this one does NOT work because 'version' seems to be some sort of reserverd property and is set to unspecified on startup...
if (project.hasProperty('version')) {
project.ext.xVersion = project.version
} else {
project.ext.xVersion = 'Developer-Build'
}
Seeing as version seems to be a reserved property, it is most likely used in publishing artifacts so the best solution then maybe the following
if (project.hasProperty('myVersion')) {
project.version = project.myVersion
} else {
project.version = 'Developer-Build'
}
and the automated build passes in myVersion and developers of course don't.

Resources