Creating UI using Groovy

Report
Jenkins User Conference
San Francisco, Sept 30 2012
#jenkinsconf
Take Control. Write a Plugin.
Part II
Baruch Sadogursky
JFrog
www.jfrog.com
About me
Developer Advocate @JFrog
Job definition:
Write code
Talk about it
github.com/jbaruch
@jbaruch
JFrog & Jenkins
With Jenkins from day 1
Jenkins Artifactory Plugin
Hosted JUC Israel
repo.jenkins-ci.org
JavaOne DEMOzone
Agenda
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
Agenda
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
First, let’s vote
Who saw “Take Control. Write a Plugin” session
on YouTube?
Let me guess…
one or two hands…
“Hello, my name is Noam Tenne”
PREVIOUSLY IN “TAKE CONTROL.
WRITE A PLUGIN”…
Overview – Why plugins
What you can do with plugins
What you can’t do with plugins
Plugins statistics
What can I extend?
UI
SCM
Build Processes
Slave management
Tooling
... Many, many, more
You can even create new extension points!
Environment
IDE
All majors have good support
We love IntelliJ IDEA
Build tool
Can be Maven or Gradle
The “Motivation” Plugin
Target: Rewarding failing builds with insulting
mockery
Global configuration: Motivation phrase
Project configuration: Is motivator enabled
Outcome: Message appears in log after failure
Nowdays…
BACK TO OUR AGENDA
Agenda
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
Working with remote agents
Jenkins has remote agents!
Working with remote agents
Send closures to remote agents
hudson.remoting.Callable
Java Serialization
Closure
Poor guy’s Java closure
Usually anonymous inner class (not always)
1
2
3
4
5
6
private static class GetSystemProperties implements Callable<Properties,RuntimeException> {
public Properties call() {
return System.getProperties();
}
private static final long serialVersionUID = 1L;
}
Cast your bread on the waters
1
this.systemProperties = channel.call(new GetSystemProperties());
Channel?
Channel
Represents a communication channel to the
remote peer
Obtain from:
Distribution Abstractions – FilePath
Where is the file?
Distribution Abstractions – FilePath
hudson.FilePath
Much like java.util.File
Consider pushing logic to the file
Use FilePath.act(FileCallable)
Distribution Abstractions – Launcher
Launch stuff remotely!
Distribution Abstractions – Launcher
hudson.Launcher
Much like java.lang.ProcessBuilder
Pick your environment variables wisely!
Agenda
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
Working in multiple OSs
WORA. You know. But.
/ vs \
.sh vs .bat
Quotes around commands
Permissions
(wait for it…)
Running script…
Executing
file…
remotely…
platform independent…
Can You Spot The Error?
1 String workspaceDir = build.getWorkspace().getRemote();
2 String scriptName = launcher.isUnix() ? "proc.sh" : "proc.bat";
3 Launcher.ProcStarter procStarter =
launcher.launch().stdout(System.out);
4 procStarter.cmds(new File(workspaceDir, scriptName)).join();
Executed
locally!
Going Remote with File
Use FilePath – it will take care of all the details!
Execute FilePath.act(FileCallable)
If you need the File API, invoke() method
has it, converted to remote file properly
Permissions Dance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public boolean isDirectoryReadOnly(final FilePath filePath) throws IOException,
InterruptedException {
return filePath.getChannel().call(new Callable<Boolean, IOException>() {
public Boolean call() throws IOException {
Path path = FileSystems.getDefault().getPath(filePath.getRemote());
Set<String> systems = FileSystems.getDefault().supportedFileAttributeViews();
if (systems.contains("dos")) {
DosFileAttributeView dosView =
Files.getFileAttributeView(path, DosFileAttributeView.class);
DosFileAttributes dosAttributes = dosView.readAttributes();
return dosAttributes.isReadOnly();
}
if (systems.contains("posix")) {
PosixFileAttributeView posixView =
Files.getFileAttributeView(path, PosixFileAttributeView.class);
PosixFileAttributes posixAttributes = posixView.readAttributes();
Set<PosixFilePermission> permissions = posixAttributes.permissions();
return !permissions.contains(PosixFilePermission.OTHERS_WRITE)
}
throw new IOException("Unknown filesystem");
}
});
}
Agenda
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
Creating UI using Groovy
First, let’s look at the docs:
Creating UI using Groovy
Analogous to Jelly
Can use Jelly tags and libraries
Kohsuke:
When
What
Lots of program logic
Groovy
Lots of HTML layout markup
Jelly
Creating UI using Groovy
Analogous to Jelly
Can use Jelly tags and libraries
me:
When
Always!
What
Groovy
Creating UI using Groovy
Jelly:
1 <j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
2
<f:section title="Motivation Plugin">
3
<f:entry title=" Motivating Message" field="motivatingMessage"
4
description="The motivational message to display when a build fails">
5
<f:textbox/>
6
</f:entry>
7
</f:section>
8 </j:jelly>
Groovy:
1 f=namespace('lib/form')
2
3 f.section(title:'Motivation Plugin') {
4
f.entry(title:'Motivating Message', field:'motivatingMessage',
5
description:'The motivational message to display when a build fails'){
6
f.textbox()
7
}
8 }
Creating UI using Groovy
Real code
Debuggable, etc.
(stay tuned…)
1 f=namespace('lib/form')
2
3 f.section(title:'Motivation Plugin') {
4
f.entry(title:'Motivating Message', field:'motivatingMessage',
5
description:'The motivational message to display when a build fails'){
6
f.textbox()
7
}
8 }
Agenda
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
Writing custom Jelly(?) tags
Documentation:
Writing custom Jelly(?) tags
Writing custom Jelly Groovy tags
Simple as 1,2…
that’s it.
1. Implement
1 class MotivationTagLib extends
2
AbstractGroovyViewModule {
3
def tools = builder.namespace('hudson/tools')
4
5
MotivationTagLib(JellyBuilder b) {
6
super(b)
7
}
8
9
def evilLaugh() {
10
tools.label('mu-ha-ha!')
11
}
12 }
2. Use!
1
2
3
4
5
6
7
8
11
import org._10ne.jenkins.MotivationTagLib
f = namespace('lib/form')
m = new MotivationTagLib(builder);
f.entry(title: '') {
m.evilLaugh()
f.checkbox(…)
}
Agenda
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
Maintaining backwards compatibility
Back to Motivation plugin…
Refactoring!
Rename defaultMotivatingMessage
to
motivatingMessage
What happens to existing configuration on
users machines?
XStream Aliasing To The Rescue
1
2
3
4
5
@Initializer(before = PLUGINS_STARTED)
public static void addAliases() {
Items.XSTREAM2.aliasField("defaultMotivatingMessage",
DescriptorImpl.class, "motivatingMessage");
}
Register field (or class) alias
In Initializer that runs before plugins started
More complex cases might reqiure XStream
converter
Thank you!
See you at our DEMOzone!
Thank You To Our Sponsors

similar documents