Take Control. Write a Plugin. Part II
-
Upload
richard-dejesus -
Category
Documents
-
view
29 -
download
1
description
Transcript of Take Control. Write a Plugin. Part II
Jenkins User Conference San Francisco, Sept 30 2012 #jenkinsconf
Take Control. Write a Plugin.Part II
Baruch SadogurskyJFrog
www.jfrog.com
About me
Developer Advocate @JFrogJob definition:
Write code Talk about it
github.com/jbaruch@jbaruch
With Jenkins from day 1Jenkins Artifactory PluginHosted JUC Israelrepo.jenkins-ci.orgJavaOne DEMOzone
JFrog & Jenkins
Vote and guessingWorking with remote agentsWorking in multiple operation systemsCreating UI using GroovyWriting custom Jelly(?) tagsMaintaining backwards compatibility
Agenda
Vote and guessingWorking with remote agentsWorking in multiple operation systemsCreating UI using GroovyWriting custom Jelly(?) tagsMaintaining 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 pluginsWhat you can’t do with pluginsPlugins statistics
Overview – Why plugins
UISCMBuild ProcessesSlave managementTooling ... Many, many, more
You can even create new extension points!
What can I extend?
IDEAll majors have good supportWe love IntelliJ IDEA
Build toolCan be Maven or Gradle
Environment
Target: Rewarding failing builds with insulting mockeryGlobal configuration: Motivation phraseProject configuration: Is motivator enabledOutcome: Message appears in log after failure
The “Motivation” Plugin
BACK TO OUR AGENDANowdays…
Vote and guessingWorking with remote agentsWorking in multiple operation systemsCreating UI using GroovyWriting custom Jelly(?) tagsMaintaining backwards compatibility
Agenda
Jenkins has remote agents!
Working with remote agents
Send closures to remote agentshudson.remoting.Callable
Working with remote agents
Java Serialization
Poor guy’s Java closureUsually 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.FilePathMuch like java.util.File
Consider pushing logic to the fileUse FilePath.act(FileCallable)
Distribution Abstractions – FilePath
Launch stuff remotely!
Distribution Abstractions – Launcher
hudson.LauncherMuch like java.lang.ProcessBuilder
Pick your environment variables wisely!
Distribution Abstractions – Launcher
Vote and guessingWorking with remote agentsWorking in multiple operation systemsCreating UI using GroovyWriting custom Jelly(?) tagsMaintaining backwards compatibility
Agenda
WORA. You know. But.
/ vs \.sh vs .batQuotes around commandsPermissions
(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();
Executedlocally!
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 DosFileAttributeView 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 PosixFileAttributes 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 guessingWorking with remote agentsWorking in multiple operation systemsCreating UI using GroovyWriting custom Jelly(?) tagsMaintaining backwards compatibility
Agenda
First, let’s look at the docs:
Creating UI using Groovy
Analogous to JellyCan 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 JellyCan 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="motivatingMessage" 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 codeDebuggable, etc.
(stay tuned…)
Vote and guessingWorking with remote agentsWorking in multiple operation systemsCreating UI using GroovyWriting custom Jelly(?) tagsMaintaining 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 = .namespace( 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 guessingWorking with remote agentsWorking in multiple operation systemsCreating UI using GroovyWriting custom Jelly(?) tagsMaintaining backwards compatibility
Agenda
Back to Motivation plugin…
Maintaining backwards compatibility
Rename defaultMotivatingMessageto
motivatingMessage
What happens to existing configuration on users machines?
Refactoring!
Register field (or class) aliasIn 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("defaultMotivatingMessage", 4 DescriptorImpl.class, "motivatingMessage"); 5 }
See you at our DEMOzone!
Thank you!
Thank You To Our Sponsors