Creating UI using Groovy - CloudBees

hedgebornabaloneSoftware and s/w Development

Dec 2, 2013 (3 years and 8 months ago)

54 views


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