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
With Jenkins from day 1
Jenkins Artifactory Plugin
Hosted JUC Israel
repo.jenkins
-
ci.org
JavaOne
DEMOzone
JFrog & Jenkins
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
Agenda
Who saw
“Take Control. Write a Plugin” session
on YouTube
?
Let me guess…
one or two hands…
First, let’s vote
PREVIOUSLY IN “TAKE CONTROL.
WRITE A PLUGIN”…
“Hello, my name is Noam
Tenne
”
What you can do with plugins
What you can’t do with plugins
Plugins statistics
Overview
–
Why plugins
UI
SCM
Build Processes
Slave management
Tooling
... Many
, many, more
You
can even create new extension points!
What can I extend
?
IDE
All majors have good support
We love
IntelliJ
IDEA
Build tool
Can be Maven or
Gradle
Environment
Target
:
Rewarding
failing builds with insulting
mockery
Global configuration: Motivation phrase
Project configuration: Is motivator enabled
Outcome: Message appears in log after failure
The “Motivation” Plugin
BACK TO OUR AGENDA
Nowdays
…
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
Agenda
Jenkins has remote agents!
Working with remote agents
Send closures to remote agents
hudson.remoting.Callable
Working with remote agents
Java Serialization
Poor guy’s Java closure
Usually anonymous inner class (not always)
Closure
1
private
static
class
GetSystemProperties
implements
Callable<Properties,RuntimeException> {
2
public
Properties call() {
3
return
System.getProperties();
4
}
5
private
static
final
long
serialVersionUID =
1L
;
6
}
Channel?
Cast your bread on the waters
1
this
.systemProperties = channel.call(
new
GetSystemProperties());
Represents a communication channel to the
remote
peer
Obtain from:
Channel
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
–
FilePath
Launch stuff remotely!
Distribution Abstractions
–
Launcher
hudson.Launcher
Much like
java.lang.ProcessBuilder
Pick your environment variables wisely!
Distribution Abstractions
–
Launcher
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
Agenda
WORA. You know. But.
/
vs
\
.
sh
vs
.
bat
Quotes around commands
Permissions
(wait for it…)
Working in multiple OSs
Executing
file…
remotely…
platform independent…
Running script…
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!
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
Going Remote with File
Permissions Dance
1
public
boolean
isDirectoryReadOnly(
final
FilePath filePath)
throws
IOException,
2
InterruptedException {
3
return
filePath.getChannel().call(
new
Callable<Boolean, IOException>() {
4
public
Boolean call()
throws
IOException {
5
Path path = FileSystems.getDefault().getPath(filePath.getRemote());
6
Set<String> systems = FileSystems.getDefault().supportedFileAttributeViews();
7
if
(systems.contains(
"dos"
)) {
8
DosFileAttributeVi
ew dosView =
9
Files.getFileAttributeView(path, DosFileAttributeView.
class
);
10
DosFileAttributes dosAttributes = dosView.readAttributes();
11
return
dosAttributes.isReadOnly();
12
}
13
if
(systems.contains(
"posix"
)) {
14
PosixFileAttributeView posixView =
15
Files.getFileAttributeView(path, PosixFileAttributeView.
class
);
16
PosixFileAttribu
tes posixAttributes = posixView.readAttributes();
17
Set<PosixFilePermission> permissions = posixAttributes.permissions();
18
return
!permissions.contains(PosixFilePermission.OTHERS_WRITE)
19
}
20
throw
new
IOException(
"Unknown filesystem"
);
21
}
22
});
23
}
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
Agenda
First, let’s look at the docs:
Creating UI using Groovy
Analogous to Jelly
Can use Jelly tags and libraries
Kohsuke
:
Creating UI using Groovy
When
What
Lots of program logic
Groovy
Lots
of HTML layout markup
Jelly
Analogous to Jelly
Can use Jelly tags and libraries
me:
Creating UI using Groovy
When
What
Always!
Groovy
Jelly:
Groovy:
Creating UI using Groovy
1
<
j:jelly
xmlns:j=
"jelly:core"
xmlns:f=
"/lib/form"
>
2
<
f:section
title=
"Motivation Plugin"
>
3
<
f:entry
title=
"
Motivating Message"
field=
"
m
otivatingMessage"
4
description=
"The motivational message to display when a build fails"
>
5
<
f:textbox
/>
6
</
f:entry
>
7
</
f:section
>
8
</
j:jelly
>
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
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
}
Real code
Debuggable
, etc.
(stay tuned…)
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
Agenda
Documentation:
Writing custom Jelly(?) tags
Writing custom Jelly(?) tags
Simple as 1,2…
that’s it.
Writing
custom
Jelly
Groovy tags
1. Implement
1
class
MotivationTagLib
extends
2
AbstractGroovyViewModule {
3
def
tools
=
.nam
espace(
builder
)
'hudson/tools'
4
5
MotivationTagLib(JellyBuilder b) {
6
(b)
super
7
}
8
9
def
evilLaugh() {
10
.label(
tools
)
'mu
-
ha
-
ha!'
11
}
12
}
2. Use!
1
import
org._10ne.jenkins.MotivationTagLib
2
3
f = namespace(
)
'lib/form'
4
m =
new
MotivationTagLib(
builder
);
5
6
f.entry(
:
title
) {
''
7
m.evilLaugh()
8
f.checkbox
(
…
)
11
}
Vote and guessing
Working with remote agents
Working in multiple operation systems
Creating UI using Groovy
Writing custom Jelly(?) tags
Maintaining backwards compatibility
Agenda
Back to Motivation plugin…
Maintaining backwards
compatibility
Rename
defaultMotivatingMessage
to
motivatingMessage
What happens to existing configuration on
users machines?
Refactoring!
Register field (or class) alias
In Initializer that runs before plugins started
More complex cases might
reqiure
XStream
converter
XStream
Aliasing To The Rescue
1
@Initializer
(before =
PLUGINS_STARTED
)
2
public
static
void
addAliases() {
3
Items.
XSTREAM2
.aliasField(
"default
MotivatingMessage"
,
4
DescriptorImpl.
class
,
"motivatingMessage"
);
5
}
See you at our
DEMOzone
!
Thank you!
Thank You To Our Sponsors
Enter the password to open this PDF file:
File name:
-
File size:
-
Title:
-
Author:
-
Subject:
-
Keywords:
-
Creation Date:
-
Modification Date:
-
Creator:
-
PDF Producer:
-
PDF Version:
-
Page Count:
-
Preparing document for printing…
0%
Σχόλια 0
Συνδεθείτε για να κοινοποιήσετε σχόλιο