1 / 77

Hudson Dev Workshop

Kohsuke Kawaguchi kohsuke.kawaguchi@cloudbees.com CloudBees , Inc. Hudson Dev Workshop. Why a Hudson plugin ?. 300+ people have done it. Can’t be that hard Integrate Hudson with your favorite tools, systems, and etc. Make Hudson speak in domain specific terms

gomer
Télécharger la présentation

Hudson Dev Workshop

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Kohsuke Kawaguchi kohsuke.kawaguchi@cloudbees.com CloudBees, Inc. Hudson Dev Workshop

  2. Why a Hudson plugin? • 300+ people have done it. Can’t be that hard • Integrate Hudson with your favorite tools, systems, and etc. • Make Hudson speak in domain specific terms • Instead of batch files or shell scripts

  3. Pre-requisite • Maven 2 • Terminal and/or command prompt • IDE of your choice • The following entry in your .m2/settings.xml • Windows user will find this in %USERPROFILE%\.m2\settings.xml <settings> <pluginGroups> <pluginGroup>org.jvnet.hudson.tools</pluginGroup> </pluginGroups> </settings>

  4. Create a skeleton • CUI wizard to create a skeleton • This will download a lot from the internet if this is the first time • Download seed.zip and unzip it in ~/.m2/repository for faster bootstrap • From my USB key $ mvn -cpuhpi:create

  5. And while we wait for Maven to download the internet…

  6. Hudson Plugin Community • https://github.com/hudson/ • Developer resources • IRC channel: #hudson • Mailing list: hudson-dev@googlegroups.com • Wiki: http://wiki.hudson-ci.org/display/HUDSON/Extend+Hudson

  7. Build your Hudson plugin • Update Hudson version to 1.387 • In reference to parent POM • Hudson plugin follows a Maven standard • And while you wait for Maven to build… $ mvn package $ ls target/*.hpi

  8. Let’s look at source files • POM • Source files • Jelly files • Help HTML files

  9. Debug a Hudson plugin • Start your plugin in an embedded Hudson • If port 8080 is occupied, use –Dport=12345 • Point your browser to http://localhost:8080/ $ mvnhpi:run

  10. Play with the hello world builder • Configure a new job • Add a “Say hello world” build step • Run the build and see hello world • Where is HUDSON_HOME? • Hint: system configuration

  11. Interactive Development • Edit help HTML file, save • Just reload the browser and see the change • Edit Jelly files, save • Just reload the browser and see the change

  12. Interactive Development • For Eclipse • Edit source code, save • Wait for Hudson to auto reload • For other IDEs • Edit source code, compile • Wait for Hudson to auto reload

  13. Sometimes auto-reload is annoying • Most often useful with “mvnDebug” • So that you can reload classes from debugger • Add –DscanIntervalSeconds=0 to Maven

  14. When your plugin is ready… • “Manage Hudson” > “Manage Plugins” > “Advancced”

  15. Let’s look at the source codemore carefully

  16. Key ingredient check list • @DataBoundConstructor • @Extension • DescriptorImpl • Package structure for Jelly files • Help files by convention

  17. Basic pattern of plugins • Pick an extension point • http://wiki.hudson-ci.org/display/HUDSON/Extension+points • Implement it • Create a descriptor with @Extension • Or in some cases there’s no descriptor and the implementation class itself gets @Extension

  18. UI / Data binding

  19. Exercise: add a new config field • Add another text field to accept another name, then say hello to both • Add a help file for this new text field

  20. UI Samples • Unfortunately, much of config.jelly editing is monkey-see-monkey-do • UI samples = our attempt to fix this

  21. Exercise: add a new config field • Add auto-completion to the name field • Note the method name has to match with @field • Extra Credits • Try other UI samples and see if you can copy them

  22. Form Validation • Write doCheckXyz method for xyz field • “value” parameter to receive the current value • Can also retrieve nearby sibling values • Return/throw FormValidation instance for status public FormValidationdoCheckPort(@QueryParameter String value) { if(looksOk(value)) return FormValidation.ok(); else return FormValidation.error("There's a problem here"); }

  23. Different datatypes available • URL • int • boolean • Enum • hudson.util.Secret • Persisted and sent to browser in encrypted form • Can be extended by adding converter

  24. Exercise: play with Secret • Add another configuration field whose type is hudson.util.Secret • Inspect the persisted config.xml • ./work/jobs/JOBNAME/config.xml • Inspect the value set in the config page • Trace the execution in the debugger

  25. Playing with Launcher • Abstraction for forking processes • Works even when a build is on a slave • Fluent API • launcher.launch().doThis().doThat().start() • or join()

  26. Playing with BuildListener • Used to write to build console • getLogger() : PrintStream • Used to report error • e.printStackTrace(listener.error(“Failed to do X”));

  27. Exercise: Launcher&BuildListener • Tweak HelloWorldBuilder to fork a process and send its output to build console • E.g., “uname –a” or “cmd.exe /?”

  28. FilePath: working with files • java.io.File on steroid • Represents a file or a directory • Lots of convenient file operations • Works transparently on files on slaves

  29. Exercise: FilePath • Tweak HelloWorldBuilder to play with FilePath • Use AbstractBuild.getWorkspace() • Create several text files and create a zip file from them • Compute md5 checksums of all the files • Hint: getDigest()

  30. Descriptor : Describable • Descriptor : Describable = Class : Object • Describable • Created from UI through Descriptor • Live as long as the configuration remains the same • Descriptor • Singletons • Capture behaviors and operations that are “static” • Global configuration, form validation • … is a static nested type of Describable by convention

  31. @Extension • Enumerated during compile time to generate index • Sometimes “mvn compile” is needed for this • Used at runtime to find extensions

  32. View Technology

  33. Side track: RootAction • Useful for experimenting with views • Extension point to add a menu item to the top page • Descriptor-less, because it’s not configured from anywhere

  34. Apache Commons Jelly • XML based template engine • Think of it as JSPX+JSTL+customtaglibs • Used to render HTML • And other XMLs, such as RSS, JNLP, … • Most people start by monkey-see-monkey-do • References • http://commons.apache.org/jelly/tags.html • http://hudson-ci.org/maven-site/hudson-core/jelly-taglib-ref.html

  35. Apache Commons JEXL • EL on steroid • ${foo.bar[5]+”abc”} • Allow method calls • XML friendly operators • “and”/”or”,”lt”,”ge” etc in addition to &&, ||, <, >= • a?b:c and a?:b (=a?a:b)

  36. Object-oriented views • Views are like methods • Co-located to classes via naming convention • foo.jelly for org.acme.Bar should be in org/acme/Bar/foo.jelly • Views are inherited to subtypes • In JEXL, “it” variable refers to the “this” object • “index.jelly” plays the special role

  37. Exercise: Jelly, JEXL, and RootAction • Create a new root action “MyRootAction” • See javadoc for what your methods need to return • Add index.jelly • Start from this: • Play with Jelly and JEXL • Add methods to your class and invoke it from view • Use <j:forEach> to say hello 10 times • Otherwise be creative! <j:jelly xmlns:j=“jelly:core”> <html><body><h1> Hello from ${it.getClass()} </h1></body></html> </j:jelly>

  38. Object-oriented Views • In Hudson, URLs are mapped to object graph • And this determines how request is eventually handled • IOW, controller layer is implicit • More precisely, this is in Stapler • See HTTP headers for evaluation trace • So if you add “bar.jelly” to MyRootAction,

  39. Exercise: Object-oriented views • Add “bar.jelly” to MyRootAction • Modify index.jelly to add a hyperlink to the new page • RefactorMyRootAction and introduce a base type • Push down bar.jelly to the new base class. See how the UI behaves

  40. URL → Object graph mapping

  41. URL → Object graph mapping • Typical traversal methods • getFoo() → …/foo/ • getFoo(“bar”) → …/foo/bar/ • getDynamic(“foo”) → …/foo/… • Exact rules are in https://stapler.dev.java.net/reference.html

  42. Exercise: URL → Object graph • Define immutable Employee class • And populate several of them from MyRootAction • Define index.jelly on Employee and create navigable UI public class Employee { public int number; public String name; public Employee boss; public List<Employee> reports; … } public class MyRootAction { List<Employee> employees; }

  43. Programmatic request handling • Instead of “foo.jelly”, define “doFoo” method • Parameters are injected • StaplerRequest (<: HttpServletRequest) • StaplerResponse (<: HttpServletResponse) • @QueryParameter • @AncestorInPath • Return value / exception becomes HTTP response (or void) • Or void and control it yourself • org.kohsuke.stapler.HttpResponse

  44. Demo • Employee.doTerminate()

  45. Exercise: doXyz method • Define a form that takes one text field and update the employee name

  46. Persistence

  47. XStream • This is how Hudson persists most data • Characteristics • Clean almost human-readable XML • No mapping required / semi-transparent to code • Supports arbitrary object graph • Customizable enough • Robust in the face of partial unmarshalling failure

  48. XStream: naïve example public class Employee { public int number; public String name; } <org.acme.Employee> <number>1</number> <name>Kohskue</name> </org.acme.Employee>

  49. Persistence and extension points • If/how your extensions are persisted is described in javadoc • E.g., Builder is persisted as a part of AbstractProject • SecurityRealm is persisted as a part of Hudson • etc.

  50. XmlFile • Used if you need to persist things on your own • Represents a XML file that contains persisted objects • Important methods • write(Object) • Object read() • void unmarshal(Object)

More Related