How to import the jenkins-api in Groovy? - maven

I have a small groovy script that I want to edit and I have issues with the dependency management in Groovy. I am new to groovy and sorry in advance if this question is kinda studip / easy to answer.
I use IntelliJ as IDEA.
#Grab(group='org.jenkins-ci.main', module='jenkins-core', version='2.167', scope='provided')
//import jenkins...
def call()
{
Jenkins.instance.getItemByFullName(currentBuild.fullProjectName).getBuilds().each{ build ->
if (currentBuild.number > build.number && exec != null)
{
build.rawBuild.doKill()
}
}
}
I try to use the jenkins-core dependency to get the autocomple of the code, documentation etc. etc. for the code but it simply does not work. I also tried the maven dependency in the pom it does work neither.
So now to my question: How do I import the dependency of Jenkins correctly in Groovy?

You can get this dependency from jenkins-ci maven repo:
#GrabResolver(name='jenkins', root='http://repo.jenkins-ci.org/public/')
#Grab(group='org.jenkins-ci.main', module='jenkins-core', version='2.167')

Related

How to centralize Gradle build settings?

Say I'm using the palantir/gradle-git-version Gradle plugin, and have the following code in build.gradle.kts to determine the project version:
// If release branch, return after incrementing patch version.
// Else, return $lastTag-SNAPSHOT.
val projectVersion: String by lazy {
val versionDetails: groovy.lang.Closure<VersionDetails> by extra
with(versionDetails()) {
if (!lastTag.matches("^(?:(?:\\d+\\.){2}\\d+)\$".toRegex())) {
throw GradleException("Tag '$lastTag' doesn't match 'MAJOR.MINOR.PATCH' format")
}
// If it detached state, get branch name from GitLab CI env var
val branch = branchName ?: System.getenv("CI_COMMIT_REF_NAME")
if (branch?.startsWith("release/") == true) {
val tokens = lastTag.split('.')
"${tokens[0]}.${tokens[1]}.${tokens[2].toInt() + commitDistance}"
} else "$lastTag-SNAPSHOT"
}
}
This works, but the code is duplicated across all the projects, which is difficult to maintain except for a very small number of projects.
This is just one example, the same applies for other Gradle tasks that assume certain conventions within the company/team, like creating a Dockerfile.
What is a good way to centralize such code so that all projects can use them? Note that code like this don't usually stand on their own, but rely on Gradle plugins.
What is a good way to centralize such code so that all projects can use them?
You'll want to create a custom Gradle plugin to hold your project's conventions.
If you have Gradle installed locally, you can use the Build Init Plugin to create a skeleton plugin project. With Gradle installed locally, simple run gradle init in a new project directory and follow the prompts to create the plugin project.
As a concrete example (assuming you generated a plugin project as mentioned earlier), to apply your versioning conventions, a plugin could be:
// Plugin's build.gradle.kts
dependencies {
// Add dependency for plugin, GAV can be found on the plugins page:
// https://plugins.gradle.org/plugin/com.palantir.git-version
implementation("com.palantir.gradle.gitversion:gradle-git-version:0.12.3")
}
Then a versioning conventions plugin could be:
import com.palantir.gradle.gitversion.VersionDetails
import groovy.lang.Closure
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
class VersioningConventionsPlugin : Plugin<Project> {
override fun apply(project: Project) {
// Apply plugin to project as you would in the main Gradle build file.
project.pluginManager.apply("com.palantir.git-version")
// Configure version conventions
val projectVersion: String by lazy {
// Gradle generates some Kotlin DSL code on the fly, in a plugin implementation we don't have that.
// So we must convert the DSL to the Gradle API.
val versionDetails: Closure<VersionDetails> = project.extensions.extraProperties.get("versionDetails") as Closure<VersionDetails>
with(versionDetails.call()) {
if (!lastTag.matches("^(?:(?:\\d+\\.){2}\\d+)\$".toRegex())) {
throw GradleException("Tag '$lastTag' doesn't match 'MAJOR.MINOR.PATCH' format")
}
val branch = branchName ?: System.getenv("CI_COMMIT_REF_NAME")
if (branch?.startsWith("release/") == true) {
val tokens = lastTag.split('.')
"${tokens[0]}.${tokens[1]}.${tokens[2].toInt() + commitDistance}"
} else "$lastTag-SNAPSHOT"
}
}
// Set the version as an extra property on the project
// Accessible via extra["projectVersion"]
project.extensions.extraProperties["projectVersion"] = projectVersion
}
}
I gave a Kotlin example since your sample used the Kotlin DSL. Once you've finished development work of your conventions plugin, then you would publish to a repository such as the Gradle Plugins repository. If it's an internal company plugin, then publish it to an internal Nexus Repository or similar.
Follow the docs for the maven-publish plugin for more details on publishing. Gradle plugins can be published like any other artifact/JAR.

Git repository with Maven project as a Gradle source dependency

This article describes an interesting feature of Gradle 4.10+ called a source dependency:
https://blog.gradle.org/introducing-source-dependencies
It allows to use a Git (for example a GitHub) source code repository to build a dependency from it. However it seems like it supports only Gradle projects as source dependencies. Is it possible to use a Maven project as well and if it's possible, please show an example.
When I try to use this feature with Maven project Gradle tries to find the build.gradle file there anyway (I see it when run Gradle with the --info option) and fails with an error message like:
Git repository at https://github.com/something/something.git did not contain a project publishing the specified dependency.
The short answer
... is: "no".
Under the hood, source dependencies are composite builds. These needs to be Gradle projects as the external projects are sort of merged with the main project.
The long answer
... is: "yes but it is hard".
It is actually mentioned in the same blog post you linked to (emphasis mine):
Source dependencies make these use cases simpler to implement. Gradle takes care of automatically checking out the correct versions of dependencies, making sure the binaries are built when required. It does this everywhere that the build is run. The checked out project doesn’t even need to have an existing Gradle build. This example shows a Gradle build consuming two source dependencies that have no build system by injecting a Gradle build via plugins. The injected configuration could do anything a regular Gradle plugin can do, such as wrapping an existing CMake or Maven build.
Because it sounded like it wasn't the biggest thing in the world to create bridge between a Maven and a Gradle project in source dependencies, I gave it a shot. And I have it working except for transitive dependencies. You will basically need to do what is shown in the examples linked to above, but instead of building native libraries, you make a call-out to Maven (e.g. using a Maven plugin for Gradle).
However, the scripts I ended up with are complex enough that I would suggest you instead build the Maven project yourself, deploy it to a local Maven repository and then add that repository to the Gradle project.
<edit>
The loooooooong answer
Alright, so here is how to actually do it. The feature is poorly documented, and appears to be mostly targeted towards native projects (like C++ or Swift).
Root project setup
Take a normal Gradle project with the Java plugin applied. I did a "gradle init" in an empty folder. Assume that in this project, you are depending on a library called `` that you later want to include as a source dependency:
// [root]/build.gradle
dependencies {
implementation 'org.example:my-maven-project:1.1'
}
Note that the version number defined here must match a Git tag in the repository. This is the code revision that will be checkout out.
Then in the settings file, we define a source dependency mapping for it:
// [root]/settings.gradle
rootProject.name = 'my-project'
includeBuild('plugins') // [1]
sourceControl {
gitRepository("https://github.com/jitpack/maven-simple") { // [2]
producesModule("org.example:my-maven-project") // [3]
plugins {
id "external-maven-build" // [4]
}
}
}
[1]: This includes a Gradle project called plugins that will be explained later.
[2]: This is just an arbitrary Maven project that I found, which was relatively simple. Substitute with the actual repository you have.
[3]: This is the name of the Maven module (the same as in the dependency block) that we are defining a source build for
[4]: This defines a custom settings plugin called external-maven-build that is defined in the plugins project, which will be explained later.
Plugins project structure
Inside the root project, we define a new Gradle project. Again, you can use gradle init to initialize it as a Groovy (or whatever you like) project. Delete all generated sources and tests.
// [root]/plugins/settings.gradle
// Empty, but used to mark this as a stand-alone project (and not part of a multi-build)
// [root]/plugins/build.gradle
plugins {
id 'groovy'
id 'java-gradle-plugin' // [1]
}
repositories {
gradlePluginPortal() // [2]
}
dependencies {
implementation "gradle.plugin.com.github.dkorotych.gradle.maven.exec:gradle-maven-exec-plugin:2.2.1" // [3]
}
gradlePlugin {
plugins {
"external-maven-build" { // [4]
id = "external-maven-build"
implementationClass = "org.example.ExternalMavenBuilder"
}
}
}
[1]: In this project, we are defining a new Gradle plugin. This is a standard way to do that.
[2]: To invoke Maven, I am using another 3rd party plugin, so we need to add the Gradle plugin portal as a repository.
[3]: This is the plugin used to invoke Maven. I am not too familiar with it, and I don't know how production ready it is. One thing I noticed is that it does not model inputs and outputs, so there are no built-in support for up-to-date checking. But this can be added retrospectively.
[4]: This defines the custom plugin. Notice that it has the same ID as used in the settings file in the root project.
Plugin implementation class
Now comes the fun stuff. I chose to do it in Groovy, but it can be done in any supported JVM languages of cause.
The plugin structure is just like any other Gradle plugin. One thing to note is that it is a Settings plugin, whereas you normally do Project plugins. This is needed as it we are basically defining a Gradle project at run-time, which needs to be done as part of the initialization phase.
// [root]/plugins/src/main/groovy/org/example/ExternalMavenBuilder.groovy
package org.example
import com.github.dkorotych.gradle.maven.exec.MavenExec
import org.gradle.api.Plugin
import org.gradle.api.artifacts.ConfigurablePublishArtifact
import org.gradle.api.initialization.Settings
class ExternalMavenBuilder implements Plugin<Settings> {
void apply(Settings settings) {
settings.with {
rootProject.name = 'my-maven-project' // [1]
gradle.rootProject {
group = "org.example" //[2]
pluginManager.apply("base") // [3]
pluginManager.apply("com.github.dkorotych.gradle-maven-exec") // [4]
def mavenBuild = tasks.register("mavenBuild", MavenExec) {
goals('clean', 'package') // [5]
}
artifacts.add("default", file("$projectDir/target/maven-simple-0.2-SNAPSHOT.jar")) { ConfigurablePublishArtifact a ->
a.builtBy(mavenBuild) // [6]
}
}
}
}
}
[1]: Must match the Maven module name
[2]: Must match the Maven module group
[3]: Defines tasks like "build" and "clean"
[4]: The 3rd party plugin that makes it more easy to invoke Maven
[5]: For options, see https://github.com/dkorotych/gradle-maven-exec-plugin
[6]: Adds the Maven output as an artifact in the "default" configuration
Be aware that it does not model transitive dependencies, and it is never up-to-date due to missing inputs and outputs.
This is as far as I got with a few hours of playing around with it. I think it can be generalized into a generic plugin published to the Gradle portal. But I think I have too much on my plate as it is already. If anyone would like to continue on from here, you have my blessing :)

Gradle: Unable to get plugin class in scope in build script

I've been creating my own gradle plugin for the last few days (partly for learning, and partly to clean up some gradle build scripts I have laying around), however I've run into a problem that I can't figure out how to fix. In my plugin jar there is an interface named me.alxandr.gradle.bintray.maven.MavenPackage, yet when I try to import it I get an error saying that it can't be found. This is really weird, because the plugin is obviously running (I'm seeing output from it, and it's tasks are registering).
Currently I've done a hack to get around this as following:
project.ext.MavenPackage = MavenPackage
This just makes MavenPackage an available name in the buildscript, which works, but I loose any editor support for it cause it's entirely dynamic. Is there any way I can (from my plugin) get the buildscript to import a package when it's applied? Like how MavenPublication is in scope without me needing to import it. If not, why can I not import classes from my plugin package?
The entire source code is available at https://github.com/Alxandr/gradle-utils. You can see an attempted here: https://github.com/Alxandr/gradle-utils/blob/master/gradle/publish.gradle#L1. The current code works as is (with the hack explained above), but I'm just looking for a better way to do this.
In order to use a class from your plugin in your Gradle script, you must add the plugin JAR to the buildscript classpath.
Here's how to do just that:
buildscript {
repositories { /* where to get your plugin jar from, e.g. mavenLocal() */ }
dependencies {
classpath 'some_group:me.alxandr.plugin:1.0.0'
}
}
import me.alxandr.gradle.bintray.maven.MavenPackage
You can find more information in the Gradle User Guide about using external dependencies in your build script

OSGi bundle build issue in Gradle

I have a simple use case of building an OSGi bundle using Gradle build tool. The build is successful if there are java files present in the build path, but it fails otherwise.
I am using 'osgi' plugin inside the gradle script and trying to build without any java files. The build always fails with following error:
Could not copy MANIFEST.MF to
I am sure there must be some way to do it in Gradle but not able to fine. Any idea what can be done to resolve this depending on your experience.
I ran into this today as well, and #Peter's fix didn't work for me (I hadn't applied the java plugin in the first place...). However, after hours of Googling I did find this thread, which helped me find the problem.
Basically, it seems that the error occurs (as Peter stated) when no class files are found in the jar - my guess is because the plugin then cannot scan the classes for package names on which to base all the Import and Export information.
My solution was to add the following to the manifest specification:
classesDir = theSourceSet.output.classesDir
classpath = theSourceSet.runtimeClasspath
In my actual build code, I loop over all source sets to create jar tasks for them, so then it looks like this:
sourceSets.each { ss ->
assemble.dependsOn task("jar${ss.name.capitalize()}", type: Jar, dependsOn: ss.getCompileTaskName('Java')) {
from ss.output
into 'classes'
manifest = osgiManifest {
classesDir = ss.output.classesDir
classpath = ss.runtimeClasspath
// Other properties, like name and symbolicName, also set based on
// the name of the source set
}
baseName = ss.name
}
}
Running with --stacktrace indicates that the osgi plugin doesn't deal correctly with the case where both the osgi and the java plugins are applied, but no Java code is present. Removing the java plugin should solve the problem.
I had the same issue also when java code was present.
Adding these two lines to the osgiManifest closure fixed the problem:
classesDir = sourceSets.main.output.classesDir
classpath = sourceSets.main.runtimeClasspath
-- erik

How to get all maven dependencies using aether?

I'm trying to fetch a maven artifact's dependencies using aether. I see a RepositorySystem.collectDependencies(), but that fetches only compile and runtime scoped dependencies. How do I fetch all dependencies for the artifact, including test and provided?
Take a look at jcabi-aether (I'm a developer), which is a wrapper around Sonatype Aether:
File repo = this.session.getLocalRepository().getBasedir();
Collection<Artifact> deps = new Aether(this.getProject(), repo).resolve(
new DefaultArtifact("junit", "junit-dep", "", "jar", "4.10"),
JavaScopes.RUNTIME
);
Assuming you are using DefaultRepositorySystemSession you may do the following:
defaultRepositorySystemSession.setDependencySelector(new DependencySelector() {
#Override
public boolean selectDependency(Dependency dependency) {
return true;
}
#Override
public DependencySelector deriveChildSelector(DependencyCollectionContext context) {
return this;
}
});
and then
CollectResult result = repositorySystem.collectDependencies(defaultRepositorySystemSession, request);
Here is an example project that does this.
These three files:
https://github.com/terraframe/Runway-SDK/tree/v1.8.0/runwaysdk-server/src/main/java/com/runwaysdk/business/generation/maven
Are a working, stand-alone, example using Aether.
It worked for a few months then I all of a sudden had an issue pop up where it would sometimes on Mac JRE throw a DependencyResolutionException on com.sun:tools.jar.
Good luck, if you decide to use it, I'm instead going to use maven-dependency-plugin dependency:build-classpath.
You can utilize DependencyFilter in Eclipses Aether. A complete version for a sample below can be found in this awesome set of Aether snippets.
DependencyFilter classpathFilter = DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE, JavaScopes.PROVIDED);
CollectRequest collectRequest = new CollectRequest();
collectRequest.setRoot( new Dependency( artifact, JavaScopes.COMPILE ) );
collectRequest.setRepositories(repositories);
DependencyRequest dependencyRequest = new DependencyRequest( collectRequest, classpathFilter );
List<ArtifactResult> artifactResults =
system.resolveDependencies( session, dependencyRequest ).getArtifactResults();
UPDATE
Version 0.9.0M3 is not compatible with Maven 3.1.0, so don't use it inside Maven, i.e. in a plugin.
Take a look at this github project: https://github.com/Gemba/mvn-dd
It downloads all dependencies including test and provided.
It uses aether's library to fetch them.

Resources