Closed wheelerlaw closed 3 years ago
I prefer to solve this problem by introducing a new source set and sync task. It has great IDE support, makes it easy to enable groovyc features (like static compilation), and keeps the tasks focused and cacheable. Here's how to do it:
plugins {
id 'groovy'
id 'org.jenkins-ci.jpi' version '0.40.0'
}
def ourJenkinsVersion = '2.263.1'
jenkinsPlugin {
jenkinsVersion = ourJenkinsVersion
}
repositories {
jcenter()
maven {
name 'Jenkins'
url 'https://repo.jenkins-ci.org/releases'
}
}
// this enables many things, but these are the most important:
// src/initd/groovy will now be the place to put your init scripts
// initdImplementation will be the configuration to put your dependencies
sourceSets {
initd
}
dependencies {
// I prefer to bring in the official bom here as a platform, so we can use the same dependency versions Jenkins will use
initdImplementation platform("org.jenkins-ci.main:jenkins-bom:$ourJenkinsVersion")
initdImplementation 'org.codehaus.groovy:groovy-all' // version comes from the bom
initdImplementation "org.jenkins-ci.main:jenkins-core:$ourJenkinsVersion" // needed to auto-complete core classes
}
// sync our init scripts into the existing `JENKINS_HOME`/init.groovy.d
def syncInit = tasks.register('syncServerInitScripts', Sync) {
it.from sourceSets.initd.groovy
it.into new File(jenkinsPlugin.workDir, 'init.groovy.d')
}
// ensure sync runs whenever we start up the server
tasks.named('server').configure {
it.dependsOn syncInit
}
I tested this out by running a couple builds with a file in src/initd/groovy/startup.groovy
.
Here it is with an update to the file:
$ ./gradlew server -i
...
> Task :syncServerInitScripts
Caching disabled for task ':syncServerInitScripts' because:
Build cache is disabled
Task ':syncServerInitScripts' is not up-to-date because:
Input property 'rootSpec$1' file /tmp/init-example/src/initd/groovy/startup.groovy has changed.
:syncServerInitScripts (Thread[Execution worker for ':',5,main]) completed. Took 0.008 secs.
:server (Thread[Execution worker for ':',5,main]) started.
Here it is with the file unchanged:
$ ./gradlew server -i
...
> Task :syncServerInitScripts UP-TO-DATE
Caching disabled for task ':syncServerInitScripts' because:
Build cache is disabled
Skipping task ':syncServerInitScripts' as it is up-to-date.
:syncServerInitScripts (Thread[Execution worker for ':',5,main]) completed. Took 0.001 secs.
:server (Thread[Execution worker for ':',5,main]) started.
I wouldn't change JENKINS_HOME
to be the main output's resources dir, because it would conflate a couple different things. There is a lot going on in the home directory that would cause task caching to always be considered out-of-date.
That being said, the simplest way to change the value of JENKINS_HOME
is to update the value of jenkinsPlugin.workDir
. The JpiPlugin class wires workDir
to jenkinsHome
as a read-only field. The field is set to the system property on process launch.
This is fantastic. Thank you for the assistance.
Do you think you could elaborate a little bit on the code in this plugin that pulls the .jar
from the .hpi
files? I have a parent project that doesn't have this plugin applied (instead it uses the jenkins-pipeline-shared-libraries-gradle-plugin
for the shared library development), and I would like to just include a little bit of code in the build.gradle file that the same thing that this plugin does to extra the .jar
from the .hpi
file of a plugin dependency added to my build.gradle.
Edit: Actually, it might not be code in this plugin that is doing it, because when I add @jar
after the dependency version so
initdImplementation 'org.jenkins-ci.plugins:cachet-gating:1.0.6'
becomes
initdImplementation 'org.jenkins-ci.plugins:cachet-gating:1.0.6@jar'
The plugin shows up as a jar dependency instead of an hpi dependency.
Do you think you could elaborate a little bit on the code in this plugin that pulls the .jar from the .hpi files?
This is done through variants with the JpiVariantRule
. It's implemented as a ComponentMetadataRule
. Basically for dependencies with POMs that have a packaging type of jpi
or hpi
, new compile and runtime variants are added for the jar.
The simplest way to apply this is to all components:
plugins {
// the rule must be on the classpath, but the plugin isn't applied
id 'org.jenkins-ci.jpi' version '0.40.0' apply false
}
dependencies {
components.all(org.jenkinsci.gradle.plugins.jpi.JpiVariantRule)
initdImplementation 'org.jenkins-ci.plugins:cachet-gating:1.0.6'
}
You can also apply it to just select modules using withModule
if you'd prefer.
when I add
@jar
after the dependency
One reason to not use this syntax is it will force resolve the jar, but it won't run dependency resolution and get any of its transitives. Sometimes that's not a big deal, but for plugins that bring in other libraries it can be much more convenient to use the rule.
Okay, so I have another little riddle that I have been working on.
Basically I have a couple of local .hpi
files in my repository for some custom Jenkins plugins. These plugins are loaded by a build script when we create our Jenkins container image. I also want the plugin to be added to the classpath so the IDE autocompletion works. The @jar
syntax works fine for this because the transitive dependencies don't need to be resolved for just IDE autocomplation. However, it looks like the @jar
syntax only works for remote .hpi
files located in maven central or the likes. I have tried adding that JpiVariantRule stuff to my build.gradle, and have tried a couple of things, but no dice so far. Would you happen to know how to do a similar thing to crack open the local .hpi
file and grab the right jar?
Basically I have a couple of local .hpi files in my repository for some custom Jenkins plugins.
One thing to consider upfront is if you can publish these plugins to a maven repo somewhere and resolve them from there with the JpiVariantRule
. They'd behave like other plugins and be easier to maintain going forward.
This is what I'm picturing from the description:
.
├── build.gradle
├── libs
│ └── custom.hpi
├── settings.gradle
└── src
└── main
└── java
└── org
└── example
└── MyPlugin.java
it looks like the
@jar
syntax only works for remote.hpi
files located in maven central or the likes.
It's working with remote things because there is also a .jar
published alongside the .hpi
(example).
Would you happen to know how to do a similar thing to crack open the local .hpi file and grab the right jar?
To get this working there will need to be a jar available and it will need to be added to the configuration.
If you're also building the custom plugins, it's probably simplest to also bring in the jar. If you only have the .hpi
files, you could unpack libs/custom.hpi
and copy <unpacked-dir>/WEB-INF/lib/custom-<version>.jar
to libs/custom.hpi
. The downside of this is it isn't obvious that you need to do this on each upgrade of custom.hpi
.
Another option to consider is a custom task to unpack the .hpi
to somewhere in the build directory, and add those files to the configuration. See Unpacking a subset of a ZIP file for this approach.
I am trying to configure the
JENKINS_HOME
environment variable so that the Jenkins instance run using theserver
task will execute the groovy files in$JENKINS_HOME/init.groovy.d/
. I tried setting thejenkinsHome
value to a location (since that property looks like it configures the environment variable here):However, I keep getting this error:
How should I set this?