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

1,379
-1

Published on

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

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,379
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
18
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

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

×