Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Gerrit Code Review: how to script a plugin with Scala and Groovy

3,017 views

Published on

How to extend Gerrit Code Review using Scala and Groovy scripting plugins

  • Be the first to comment

Gerrit Code Review: how to script a plugin with Scala and Groovy

  1. 1. Luca Milanesio GerritForge Luca@gerritforge.com http://www.gerritforge.com Twitter: @gitenterprise How to script a Plugin
  2. 2. 2 About Luca • Luca Milanesio Co-founder of GerritForge • over 20 years of experience in Agile Development SCM, CI and ALM worldwide • Contributor to Jenkins since 2007 (and previously Hudson) • Git SCM mentor for the Enterprise since 2009 • Contributor to Gerrit Code Review community since 2011
  3. 3. 3 About GerritForge Founded in 2009 in London UK Mission: Agile Enterprise Products:
  4. 4. 4 Agenda  Where we come from ?  2 years ago: Gerrit plugins  We want more plugins  Create a plugin in 60 seconds  What, how and when are coming?  Plugins showcase
  5. 5. 5 WHO REMEMBERS THIS ? GitTogether 2011 Gerrit 2.2.x
  6. 6. 6 Why don’t we introduce plugins ? They have been so successful with Jenkins
  7. 7. 7 Gerrit Hackathon 7th of May 2012 The first “helloworld” plugin was born
  8. 8. 8 WHO REMEMBERS THIS ? Gerrit Summit 2012 Ver. 2.5.x
  9. 9. 9 Gerrit Plugins Growth in 2 years 50
  10. 10. 10 Can we do more ? Let's ask Jenkins 
  11. 11. 11 PLUGINS SUCCESS = COMMUNITY ENGAGEMENT
  12. 12. 12 COMMUNITY = BRIDGING DIFFERENCES
  13. 13. 13 That would be very useful for Gerrit Administrators to be able to write quick automation scripts for their daily tasks. I would prioritize groovy plugin more for the popularity of groovy scripting. I think adding a "scripting language plugin" to support plugins written in scripting languages is a very good idea Hi all, I was thinking about extending Gerrit capabilities to load plugins / extensions by providing "wrapper" for other JVM languages. So … we asked the Community I would prefer scala, because I've already written my plugins with it. imho the builld process is the main impediment.
  14. 14. 14 CHALLENGE for building a Gerrit Plugin
  15. 15. 15 NO STEPS, NO BUILD … just do it ! $ cat - > ~/gerrit/plugins/hello-1.0.groovy import com.google.gerrit.sshd.* import com.google.gerrit.extensions.annotations.* @Export("groovy") class GroovyCommand extends SshCommand { public void run() { stdout.println "Hi from Groovy" } } ^D This sample has 190 chars: you need to type at least 3.2 chars/sec to complete the sample in only one minute  https://gist.github.com/lucamilanesio/9687053
  16. 16. 16 IS THAT TRUE ? Let’s check on Gerrit $ ssh –p 29418 user@localhost gerrit plugin ls Name Version Status File ---------------------------------------------------------------------- hello 1.0 ENABLED hello-1.0.groovy Gerrit auto-detects the new Groovy file under $GERRIT_SITE/plugins, invoke the interpreter and auto-wrap the class into a self-contained Gerrit plugin. Groovy class is compiled into byte-code BUT is slower than native Java plugin.
  17. 17. 17 DOES IT REALLY WORK ? $ ssh –p 29418 user@localhost hello groovy Hi from Groovy No magic … IT IS ALL REAL ! Plugin name (hello) comes from the script filename. A new SSH command (groovy) has been defined in Gerrit associated to the Groovy script loaded.
  18. 18. 18 What about Scala ? Just do it again ! $ cat - > ~/gerrit/plugins/hi-1.0.scala import com.google.gerrit.sshd._ import com.google.gerrit.extensions.annotations._ @Export("scala") class ScalaCommand extends SshCommand { override def run = stdout println "Hi from Scala" } ^D Scala is more concise with just 178 chars : you can take it easy with 2.9 chars/sec to type it a minute  https://gist.github.com/lucamilanesio/9687092
  19. 19. 19 You have now two plugins loaded $ ssh –p 29418 user@localhost gerrit plugin ls Name Version Status File ---------------------------------------------------------------------- hello 1.0 ENABLED hello-1.0.groovy hi 1.0 ENABLED hi-1.0.scala There are no differences for Gerrit between Java, Groovy or Scala plugins: they have the same dignity and power. Scala compiler takes longer but byte-code runs at the same speed as a native Java plugin!
  20. 20. 20 Reactions from the Gerrit mailing list … So i checked it out and tried it, and what should i say ... Wow, it rocks! ;-) Combined with new and shiny Plugin API the code is really short. So i started new repo on gh [1] and created two working plugins [2], [3], and sure would add more, so to say, cookbook-groovy-plugin: $>ssh gerrit review approve I59302cbb $>Approve change: I59302cbb. What do YOU think ? 
  21. 21. 21 WHAT can scripting plugins do now ? 1. Define new SSH commands 2. Define new REST APIs 3. Listen to Gerrit events
  22. 22. 22 HOW can I get Gerrit with scripting plugins ? Download the Gerrit master with scripting extensions from: http://ci.gerritforge.com/job/Gerrit-master-scripting/lastSuccessfulBuild/artifact/buck- out/gen/gerrit-scripting.war Run Gerrit init and say Y for installing the Scala and Groovy scripting plugins providers: *** Plugins *** Install plugin groovy-provider version v2.9-rc1-325-g96d0d43 [y/N]? Y Install plugin scala-provider version v2.9-rc1-325-g96d0d43 [y/N]? y You may want to increase the JVM PermGen on gerrit.config when loading/unloading scripting plugins [container] javaOptions = -XX:MaxPermSize=1024m
  23. 23. 23 Scripting plugins showcase
  24. 24. 24 Create a branch through Gerrit API $ cat - > ~/gerrit/plugins/branch-1.0.groovy import com.google.gerrit.sshd.* import com.google.gerrit.extensions.annotations.* import com.google.gerrit.extensions.api.* import com.google.gerrit.extensions.api.projects.* import com.google.inject.* import org.kohsuke.args4j.* @Export("create") @CommandMetaData(name = "create-branch", description = "Create branch") class CreateBranch extends SshCommand { @Inject GerritApi api @Argument(index = 0, usage = "Project name", metaVar = "PROJECT") String projectName @Argument(index = 1, usage = "Branch name", metaVar = "BRANCH") String branchName @Argument(index = 2, usage = "Branch point sha1", metaVar = "SHA1") String sha1 void run() { BranchInput branch = new BranchInput() branch.revision = sha1 api.projects().name(projectName).branch(branchName).create(branch) stdout.println("Branch created: " + branchName) } } ^D https://gist.github.com/lucamilanesio/9687118
  25. 25. 25 Script classes get Plugins Guice Injections (but cannot add Guice Modules)
  26. 26. 26 List projects via REST API $ cat - > ~/gerrit/plugins/projects-1.0.scala import com.google.inject._ import com.google.gerrit.extensions.annotations._ import com.google.gerrit.server.project._ import javax.servlet.http._ import scala.collection.JavaConversions._ @Singleton @Export("/") class Projects @Inject() (val pc: ProjectCache) extends HttpServlet { override def doGet(req: HttpServletRequest, resp: HttpServletResponse) = resp.getWriter print "[" + (pc.all map ( """ + _.get + """ ) mkString ",") + "]" } ^D https://gist.github.com/lucamilanesio/9687133
  27. 27. 27 Scala is COMPACT + FUNCTIONAL NOTE: for Guice injection @Inject() before constructor vals
  28. 28. 28 FUNCTIONAL IS PERFECT FOR …
  29. 29. 29 Scalable in-process hooks $ cat - > ~/gerrit/plugins/inprochook-1.0.scala import org.slf4j.LoggerFactory._ import com.google.inject._ import com.google.gerrit.common._ import com.google.gerrit.server.events._ import com.google.gerrit.extensions.registration.DynamicSet @Singleton class ListenToChanges extends ChangeListener { val log = getLogger(classOf[ListenToChanges]) override def onChangeEvent(e: ChangeEvent) = e match { case comm: CommentAddedEvent => log info s"${comm.author.name} said '${comm.comment}' " + s"on ${comm.change.project}/${comm.change.number}'" case _ => } } class HooksModule extends AbstractModule { override def configure = DynamicSet bind(binder, classOf[ChangeListener]) to classOf[ListenToChanges] } ^D  Less CPU overhead (no exec/fork)  faster and scalable  Works on behalf of user thread identity  Access Gerrit injected objects (Cache, ReviewDb, JGit) https://gist.github.com/lucamilanesio/9687154
  30. 30. 30 Commit message validation $ cat - > ~/gerrit/plugins/commitheadline-1.0.scala import scala.collection.JavaConversions._ import com.google.inject._ import com.google.gerrit.extensions.annotations._ import com.google.gerrit.server.git.validators._ import com.google.gerrit.server.events._ @Singleton @Listen class HasIssueInCommitHeadline extends CommitValidationListener { override def onCommitReceived(e: CommitReceivedEvent) = "[A-Z]+-[0-9]+".r findFirstIn e.commit.getShortMessage map { issue => Seq(new CommitValidationMessage(s"Issue: $issue", false)) } getOrElse { throw new CommitValidationException("Missing JIRA-ID in commit headline") } } ^D  Native pattern-matching support  Java regex available out-of-the box  Support for Optional values (map/getOrElse) https://gist.github.com/lucamilanesio/9687174
  31. 31. 31 And there is more … audit events download commands project and group events message of the day Prolog predicates auth backends avatars
  32. 32. 32 WHEN scripting plugins will be included in Gerrit ? Hackathon #6 24-26th March we will keep on hacking on Gerrit MISSION: improve, stabilise and merge scripting plugins into Gerrit master ! TARGET VERSION: Gerrit Ver. 2.10 (?)
  33. 33. 33
  34. 34. Try it on-line (with scripting): http://gerrithub.io/login Read the Gerrit book: http://gerrithub.io/book Keep in touch: http://gitenterprise.me Learn more about Gerrit with 20% OFF Book discount for Gerrit User Summit 2014 Book PROMO-CODE: LGCRB20 eBook PROMO-CODE: LGCReB20

×