How to expose WebJars with Stapler

35 views
Skip to first unread message

Ullrich Hafner

unread,
Oct 26, 2019, 7:03:27 AM10/26/19
to Jenkins Developers
Has someone already used WebJars (https://www.webjars.org) in a plugin to refer to JS libraries? 

While it seems to be simple to bundle it with my HPI file (it is then part of the WEB-INF/lib folder) I do not see an easy way to refer to the containing css/js files afterwards, since they are located in a jar file that seems to be not accessible in Jenkins UI. 

I can reference my local plugin web resources with 

<link rel="stylesheet" href="${resURL}/plugin/warnings-ng/css/font-awesome/css/fontawesome.min.css"/>

But the same does not work when the fontawesome.min.css is part of the other webjar. (Or which path do I need here?)



Robert Sandell

unread,
Oct 30, 2019, 11:57:53 AM10/30/19
to Jenkins Developer List
Stapler has a mechanism called adjuncts that works similarly.

/B

--
You received this message because you are subscribed to the Google Groups "Jenkins Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jenkinsci-de...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jenkinsci-dev/740C8BC3-AD97-4A4B-9148-8DF5547480F7%40gmail.com.


--
Robert Sandell
Software Engineer
CloudBees, Inc.
CloudBees-Logo.png
Twitter: robert_sandell

Ullrich Hafner

unread,
Oct 30, 2019, 7:01:32 PM10/30/19
to Jenkins Developers
In stapler-adjunct-jquery the JS file is part of the main plugin jar. That works for me as well:
I can access all my JS files using the prefix ${resURL}/plugin/warnings-ng

However, if I have resources in a library of my project, I seems to be that I can’t access them with ${resURL}/plugin/warnings-ng
So what works is to extract the dependency and copy the containing JS files to the target folder and then pack them with the main jar. This is how  stapler-adjunct-jquery does it, but it is somewhat cumbersome, it would be much simpler if I can refer to the resources in one of my dependencies jars (i.e. in the webjar). 


Robert Sandell

unread,
Oct 31, 2019, 7:35:01 AM10/31/19
to Jenkins Developer List
You could make your own simple AdjunctManager and expose a web method in a descriptor or hidden root action that uses for example Stapler.serveFile. Just be careful not to expose the entire class hierarchy :)
And possibly provide an etag or expiration date so the browser doesn't continuously hammer your method.

/B

Jesse Glick

unread,
Oct 31, 2019, 8:56:24 AM10/31/19
to Jenkins Dev
On Sat, Oct 26, 2019 at 7:03 AM Ullrich Hafner <ullrich...@gmail.com> wrote:
> I can reference my local plugin web resources with
>
> <link rel="stylesheet" href="${resURL}/plugin/warnings-ng/css/font-awesome/css/fontawesome.min.css"/>
>
> But the same does not work when the fontawesome.min.css is part of the other webjar.

Right, because the Jenkins resource URLs only serve files from the
`*.hpi` outside `WEB-INF/` and `META-INF/`, built by Maven from
`src/main/webapp/`. This is to match the traditional behavior of Java
EE WARs.

WebJars sounds neat, and definitely more friendly than Stapler
adjuncts, but to make them usable from Jenkins you would need to write
the infrastructure to serve their contents. I suppose this could take
the form of a WebJars support plugin which defines a `RootAction &
InvisibleAction` and then defines a page variable `webJarsUrl` so that
you could refer to URLs like

${webJarsUrl}/warnings-ng/bootstrap/js/bootstrap.js

This specifies the hosting plugin for purposes of `ClassLoader`
lookup, the JAR `artifactId`, and the rest of path inside that JAR. (I
am assuming the JAR version should be omitted, since a given plugin
can only host one version of a given dependency without some Shade
contortions, and you do not want to have to change a bunch of sources
every time Dependabot bumps the version.) This would resolve to an
actual path like

/jenkins/webJars/4f86c88e/warnings-ng/bootstrap/js/bootstrap.js

The random string in there would be `Jenkins.SESSION_HASH`, so the
server can set a fairly long cache expiry without improper caching
after restarts with plugin updates. See the code used for `static` and
`adjunct` URLs in Jenkins.

Ullrich Hafner

unread,
Oct 31, 2019, 10:47:27 AM10/31/19
to Jenkins Developers
I see. I’ll give it a try and report back if I manage it in this way...
> --
> You received this message because you are subscribed to the Google Groups "Jenkins Developers" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to jenkinsci-de...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/jenkinsci-dev/CANfRfr0CjSdzECgVbR_mLrJ0pHS1_NvO4h-9D7QQfxknS3-WLw%40mail.gmail.com.

Ullrich Hafner

unread,
Nov 11, 2019, 3:00:34 AM11/11/19
to Jenkins Developers
I finally managed it to get this running (at least in the Jenkins instance but not in the test harness, see below). 
Currently I am using a mix of the ideas of Jesse and Robert: the JS libs can be included as a stapler adjunct and provide the new JS libs: 

<l:main-panel>

<st:adjunct includes="io.jenkins.plugins.bootstrap4"/>
<st:adjunct includes="io.jenkins.plugins.echarts"/>
<st:adjunct includes="io.jenkins.plugins.font-awesome"/>
...

Rather than writing a new resource handler that extracts the resources dynamically (stripping of the versions, etc.) I use Maven to extract the WebJars and repack them to the Webapps folder automatically in the build. So users of the library can reuse the same concepts as before (st:adjunct, …), no new variable is required. 

If someone is interested, a draft PR is available for the warnings plugin: [3]

This works quite well in a Jenkins instance, however I can’t get this running with the Jenkins test harness. Now all my UI tests fail because the resources of the depending libraries are not found here, I always get a 404 (see [2]). Is this problem related to JENKINS-41827 [1]? Or do I need to include the JS dependencies in another way? Currently I’m using something like:

<dependency>
<groupId>io.jenkins.plugins</groupId>
<artifactId>font-awesome-api</artifactId>
<version>${font-awesome-api.version}</version>
</dependency>
...
   
The test harness unpacks these libraries correctly in the tmp folder, however I can’t access these resources. The JS plugins are correctly shown as installed in Jenkins plugin manager.


Reply all
Reply to author
Forward
0 new messages