| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 1 | Gerrit Code Review - Plugin Development |
| 2 | ======================================= |
| 3 | |
| Edwin Kempin | af27532 | 2012-07-16 11:04:01 +0200 | [diff] [blame] | 4 | The Gerrit server functionality can be extended by installing plugins. |
| 5 | This page describes how plugins for Gerrit can be developed. |
| 6 | |
| 7 | Depending on how tightly the extension code is coupled with the Gerrit |
| 8 | server code, there is a distinction between `plugins` and `extensions`. |
| 9 | |
| Edwin Kempin | f5a7733 | 2012-07-18 11:17:53 +0200 | [diff] [blame] | 10 | [[plugin]] |
| Edwin Kempin | 948de0f | 2012-07-16 10:34:35 +0200 | [diff] [blame] | 11 | A `plugin` in Gerrit is tightly coupled code that runs in the same |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 12 | JVM as Gerrit. It has full access to all server internals. Plugins |
| 13 | are tightly coupled to a specific major.minor server version and |
| 14 | may require source code changes to compile against a different |
| 15 | server version. |
| 16 | |
| Edwin Kempin | f5a7733 | 2012-07-18 11:17:53 +0200 | [diff] [blame] | 17 | [[extension]] |
| Edwin Kempin | 948de0f | 2012-07-16 10:34:35 +0200 | [diff] [blame] | 18 | An `extension` in Gerrit runs inside of the same JVM as Gerrit |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 19 | in the same way as a plugin, but has limited visibility to the |
| Edwin Kempin | fd19bfb | 2012-07-16 10:44:17 +0200 | [diff] [blame] | 20 | server's internals. The limited visibility reduces the extension's |
| 21 | dependencies, enabling it to be compatible across a wider range |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 22 | of server versions. |
| 23 | |
| 24 | Most of this documentation refers to either type as a plugin. |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 25 | |
| Edwin Kempin | f878c4b | 2012-07-18 09:34:25 +0200 | [diff] [blame] | 26 | [[getting-started]] |
| 27 | Getting started |
| 28 | --------------- |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 29 | |
| Edwin Kempin | f878c4b | 2012-07-18 09:34:25 +0200 | [diff] [blame] | 30 | To get started with the development of a plugin there are two |
| 31 | recommended ways: |
| Dave Borowitz | 5cc8f66 | 2012-05-21 09:51:36 -0700 | [diff] [blame] | 32 | |
| Edwin Kempin | f878c4b | 2012-07-18 09:34:25 +0200 | [diff] [blame] | 33 | . use the Gerrit Plugin Maven archetype to create a new plugin project: |
| 34 | + |
| 35 | With the Gerrit Plugin Maven archetype you can create a skeleton for a |
| 36 | plugin project. |
| 37 | + |
| 38 | ---- |
| 39 | mvn archetype:generate -DarchetypeGroupId=com.google.gerrit \ |
| 40 | -DarchetypeArtifactId=gerrit-plugin-archetype \ |
| 41 | -DarchetypeVersion=2.5-SNAPSHOT \ |
| 42 | -DgroupId=com.google.gerrit \ |
| 43 | -DartifactId=testPlugin |
| 44 | ---- |
| 45 | + |
| 46 | Maven will ask for additional properties and then create the plugin in |
| 47 | the current directory. To change the default property values answer 'n' |
| 48 | when Maven asks to confirm the properties configuration. It will then |
| 49 | ask again for all properties including those with predefined default |
| 50 | values. |
| 51 | |
| 52 | . clone the sample helloworld plugin: |
| 53 | + |
| 54 | This is a Maven project that adds an SSH command to Gerrit to print |
| 55 | out a hello world message. It can be taken as an example to develop |
| 56 | an own plugin. |
| 57 | + |
| Dave Borowitz | 5cc8f66 | 2012-05-21 09:51:36 -0700 | [diff] [blame] | 58 | ---- |
| 59 | $ git clone https://gerrit.googlesource.com/plugins/helloworld |
| 60 | ---- |
| Edwin Kempin | f878c4b | 2012-07-18 09:34:25 +0200 | [diff] [blame] | 61 | + |
| 62 | When starting from this example one should take care to adapt the |
| 63 | `Gerrit-ApiVersion` in the `pom.xml` to the version of Gerrit for which |
| 64 | the plugin is developed. If the plugin is developed for a released |
| 65 | Gerrit version (no `SNAPSHOT` version) then the URL for the |
| 66 | `gerrit-api-repository` in the `pom.xml` needs to be changed to |
| 67 | `https://gerrit-api.commondatastorage.googleapis.com/release/`. |
| Dave Borowitz | 5cc8f66 | 2012-05-21 09:51:36 -0700 | [diff] [blame] | 68 | |
| Edwin Kempin | f878c4b | 2012-07-18 09:34:25 +0200 | [diff] [blame] | 69 | [[API]] |
| 70 | API |
| 71 | --- |
| 72 | |
| 73 | There are two different API formats offered against which plugins can |
| 74 | be developed: |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 75 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 76 | gerrit-extension-api.jar:: |
| 77 | A stable but thin interface. Suitable for extensions that need |
| 78 | to be notified of events, but do not require tight coupling to |
| 79 | the internals of Gerrit. Extensions built against this API can |
| 80 | expect to be binary compatible across a wide range of server |
| 81 | versions. |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 82 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 83 | gerrit-plugin-api.jar:: |
| 84 | The complete internals of the Gerrit server, permitting a |
| 85 | plugin to tightly couple itself and provide additional |
| 86 | functionality that is not possible as an extension. Plugins |
| 87 | built against this API are expected to break at the source |
| 88 | code level between every major.minor Gerrit release. A plugin |
| 89 | that compiles against 2.5 will probably need source code level |
| 90 | changes to work with 2.6, 2.7, and so on. |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 91 | |
| 92 | Manifest |
| 93 | -------- |
| 94 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 95 | Plugins may provide optional description information with standard |
| 96 | manifest fields: |
| Nasser Grainawi | e033b26 | 2012-05-09 17:54:21 -0700 | [diff] [blame] | 97 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 98 | ==== |
| 99 | Implementation-Title: Example plugin showing examples |
| 100 | Implementation-Version: 1.0 |
| 101 | Implementation-Vendor: Example, Inc. |
| 102 | Implementation-URL: http://example.com/opensource/plugin-foo/ |
| 103 | ==== |
| Nasser Grainawi | e033b26 | 2012-05-09 17:54:21 -0700 | [diff] [blame] | 104 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 105 | ApiType |
| 106 | ~~~~~~~ |
| Nasser Grainawi | e033b26 | 2012-05-09 17:54:21 -0700 | [diff] [blame] | 107 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 108 | Plugins using the tightly coupled `gerrit-plugin-api.jar` must |
| 109 | declare this API dependency in the manifest to gain access to server |
| Edwin Kempin | 948de0f | 2012-07-16 10:34:35 +0200 | [diff] [blame] | 110 | internals. If no `Gerrit-ApiType` is specified the stable `extension` |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 111 | API will be assumed. This may cause ClassNotFoundExceptions when |
| 112 | loading a plugin that needs the plugin API. |
| Nasser Grainawi | e033b26 | 2012-05-09 17:54:21 -0700 | [diff] [blame] | 113 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 114 | ==== |
| 115 | Gerrit-ApiType: plugin |
| 116 | ==== |
| 117 | |
| 118 | Explicit Registration |
| 119 | ~~~~~~~~~~~~~~~~~~~~~ |
| 120 | |
| 121 | Plugins that use explicit Guice registration must name the Guice |
| 122 | modules in the manifest. Up to three modules can be named in the |
| Edwin Kempin | 948de0f | 2012-07-16 10:34:35 +0200 | [diff] [blame] | 123 | manifest. `Gerrit-Module` supplies bindings to the core server; |
| 124 | `Gerrit-SshModule` supplies SSH commands to the SSH server (if |
| 125 | enabled); `Gerrit-HttpModule` supplies servlets and filters to the HTTP |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 126 | server (if enabled). If no modules are named automatic registration |
| 127 | will be performed by scanning all classes in the plugin JAR for |
| 128 | `@Listen` and `@Export("")` annotations. |
| 129 | |
| 130 | ==== |
| 131 | Gerrit-Module: tld.example.project.CoreModuleClassName |
| 132 | Gerrit-SshModule: tld.example.project.SshModuleClassName |
| 133 | Gerrit-HttpModule: tld.example.project.HttpModuleClassName |
| 134 | ==== |
| 135 | |
| Edwin Kempin | f729574 | 2012-07-16 15:03:46 +0200 | [diff] [blame] | 136 | [[reload_method]] |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 137 | Reload Method |
| 138 | ~~~~~~~~~~~~~ |
| 139 | |
| 140 | If a plugin holds an exclusive resource that must be released before |
| 141 | loading the plugin again (for example listening on a network port or |
| Edwin Kempin | 948de0f | 2012-07-16 10:34:35 +0200 | [diff] [blame] | 142 | acquiring a file lock) the manifest must declare `Gerrit-ReloadMode` |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 143 | to be `restart`. Otherwise the preferred method of `reload` will |
| 144 | be used, as it enables the server to hot-patch an updated plugin |
| 145 | with no down time. |
| 146 | |
| 147 | ==== |
| 148 | Gerrit-ReloadMode: restart |
| 149 | ==== |
| 150 | |
| 151 | In either mode ('restart' or 'reload') any plugin or extension can |
| 152 | be updated without restarting the Gerrit server. The difference is |
| 153 | how Gerrit handles the upgrade: |
| 154 | |
| 155 | restart:: |
| 156 | The old plugin is completely stopped. All registrations of SSH |
| 157 | commands and HTTP servlets are removed. All registrations of any |
| 158 | extension points are removed. All registered LifecycleListeners |
| 159 | have their `stop()` method invoked in reverse order. The new |
| 160 | plugin is started, and registrations are made from the new |
| 161 | plugin. There is a brief window where neither the old nor the |
| 162 | new plugin is connected to the server. This means SSH commands |
| 163 | and HTTP servlets will return not found errors, and the plugin |
| 164 | will not be notified of events that occurred during the restart. |
| 165 | |
| 166 | reload:: |
| 167 | The new plugin is started. Its LifecycleListeners are permitted |
| 168 | to perform their `start()` methods. All SSH and HTTP registrations |
| 169 | are atomically swapped out from the old plugin to the new plugin, |
| 170 | ensuring the server never returns a not found error. All extension |
| 171 | point listeners are atomically swapped out from the old plugin to |
| 172 | the new plugin, ensuring no events are missed (however some events |
| 173 | may still route to the old plugin if the swap wasn't complete yet). |
| 174 | The old plugin is stopped. |
| 175 | |
| Edwin Kempin | f729574 | 2012-07-16 15:03:46 +0200 | [diff] [blame] | 176 | To reload/restart a plugin the link:cmd-plugin-reload.html[plugin reload] |
| 177 | command can be used. |
| 178 | |
| Luca Milanesio | 737285d | 2012-09-25 14:26:43 +0100 | [diff] [blame^] | 179 | [[init_step]] |
| 180 | Init step |
| 181 | ~~~~~~~~~ |
| 182 | |
| 183 | Plugins can contribute their own "init step" during the Gerrit init |
| 184 | wizard. This is useful for guiding the Gerrit administrator through |
| 185 | the settings needed by the plugin to work propertly. |
| 186 | |
| 187 | For instance plugins to integrate Jira issues to Gerrit changes may |
| 188 | contribute their own "init step" to allow configuring the Jira URL, |
| 189 | credentials and possibly verify connectivity to validate them. |
| 190 | |
| 191 | ==== |
| 192 | Gerrit-InitStep: tld.example.project.MyInitStep |
| 193 | ==== |
| 194 | |
| 195 | MyInitStep needs to follow the standard Gerrit InitStep syntax |
| 196 | and behaviour: writing to the console using the injected ConsoleUI |
| 197 | and accessing / changing configuration settings using Section.Factory. |
| 198 | |
| 199 | In addition to the standard Gerrit init injections, plugins receive |
| 200 | the @PluginName String injection containing their own plugin name. |
| 201 | |
| 202 | Bear in mind that the Plugin's InitStep class will be loaded but |
| 203 | the standard Gerrit runtime environment is not available and the plugin's |
| 204 | own Guice modules were not initialized. |
| 205 | This means the InitStep for a plugin is not executed in the same way that |
| 206 | the plugin executes within the server, and may mean a plugin author cannot |
| 207 | trivially reuse runtime code during init. |
| 208 | |
| 209 | For instance a plugin that wants to verify connectivity may need to statically |
| 210 | call the constructor of their connection class, passing in values obtained |
| 211 | from the Section.Factory rather than from an injected Config object. |
| 212 | |
| 213 | Plugins InitStep are executing during the "Gerrit Plugin init" phase, after |
| 214 | the extraction of the plugins embedded in Gerrit.war into $GERRIT_SITE/plugins |
| 215 | and before the DB Schema initialization or upgrade. |
| 216 | Plugins InitStep cannot refer to Gerrit DB Schema or any other Gerrit runtime |
| 217 | objects injected at startup. |
| 218 | |
| 219 | ==== |
| 220 | public class MyInitStep implements InitStep { |
| 221 | private final ConsoleUI ui; |
| 222 | private final Section.Factory sections; |
| 223 | private final String pluginName; |
| 224 | |
| 225 | @Inject |
| 226 | public GitBlitInitStep(final ConsoleUI ui, Section.Factory sections, @PluginName String pluginName) { |
| 227 | this.ui = ui; |
| 228 | this.sections = sections; |
| 229 | this.pluginName = pluginName; |
| 230 | } |
| 231 | |
| 232 | @Override |
| 233 | public void run() throws Exception { |
| 234 | ui.header("\nMy plugin"); |
| 235 | |
| 236 | Section mySection = getSection("myplugin", null); |
| 237 | mySection.string("Link name", "linkname", "MyLink"); |
| 238 | } |
| 239 | } |
| 240 | ==== |
| 241 | |
| Edwin Kempin | f5a7733 | 2012-07-18 11:17:53 +0200 | [diff] [blame] | 242 | [[classpath]] |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 243 | Classpath |
| 244 | --------- |
| 245 | |
| 246 | Each plugin is loaded into its own ClassLoader, isolating plugins |
| 247 | from each other. A plugin or extension inherits the Java runtime |
| 248 | and the Gerrit API chosen by `Gerrit-ApiType` (extension or plugin) |
| 249 | from the hosting server. |
| 250 | |
| 251 | Plugins are loaded from a single JAR file. If a plugin needs |
| 252 | additional libraries, it must include those dependencies within |
| 253 | its own JAR. Plugins built using Maven may be able to use the |
| 254 | link:http://maven.apache.org/plugins/maven-shade-plugin/[shade plugin] |
| 255 | to package additional dependencies. Relocating (or renaming) classes |
| 256 | should not be necessary due to the ClassLoader isolation. |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 257 | |
| Edwin Kempin | f5a7733 | 2012-07-18 11:17:53 +0200 | [diff] [blame] | 258 | [[ssh]] |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 259 | SSH Commands |
| 260 | ------------ |
| 261 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 262 | Plugins may provide commands that can be accessed through the SSH |
| 263 | interface (extensions do not have this option). |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 264 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 265 | Command implementations must extend the base class SshCommand: |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 266 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 267 | ==== |
| 268 | import com.google.gerrit.sshd.SshCommand; |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 269 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 270 | class PrintHello extends SshCommand { |
| 271 | protected abstract void run() { |
| 272 | stdout.print("Hello\n"); |
| 273 | } |
| 274 | } |
| 275 | ==== |
| Nasser Grainawi | e033b26 | 2012-05-09 17:54:21 -0700 | [diff] [blame] | 276 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 277 | If no Guice modules are declared in the manifest, SSH commands may |
| Edwin Kempin | 948de0f | 2012-07-16 10:34:35 +0200 | [diff] [blame] | 278 | use auto-registration by providing an `@Export` annotation: |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 279 | |
| 280 | ==== |
| 281 | import com.google.gerrit.extensions.annotations.Export; |
| 282 | import com.google.gerrit.sshd.SshCommand; |
| 283 | |
| 284 | @Export("print") |
| 285 | class PrintHello extends SshCommand { |
| 286 | protected abstract void run() { |
| 287 | stdout.print("Hello\n"); |
| 288 | } |
| 289 | } |
| 290 | ==== |
| 291 | |
| 292 | If explicit registration is being used, a Guice module must be |
| 293 | supplied to register the SSH command and declared in the manifest |
| 294 | with the `Gerrit-SshModule` attribute: |
| 295 | |
| 296 | ==== |
| 297 | import com.google.gerrit.sshd.PluginCommandModule; |
| 298 | |
| 299 | class MyCommands extends PluginCommandModule { |
| 300 | protected void configureCommands() { |
| 301 | command("print").to(PrintHello.class); |
| 302 | } |
| 303 | } |
| 304 | ==== |
| 305 | |
| 306 | For a plugin installed as name `helloworld`, the command implemented |
| 307 | by PrintHello class will be available to users as: |
| 308 | |
| 309 | ---- |
| Keunhong Park | a09a6f1 | 2012-07-10 14:45:02 -0600 | [diff] [blame] | 310 | $ ssh -p 29418 review.example.com helloworld print |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 311 | ---- |
| 312 | |
| Edwin Kempin | f5a7733 | 2012-07-18 11:17:53 +0200 | [diff] [blame] | 313 | [[http]] |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 314 | HTTP Servlets |
| 315 | ------------- |
| 316 | |
| 317 | Plugins or extensions may register additional HTTP servlets, and |
| 318 | wrap them with HTTP filters. |
| 319 | |
| 320 | Servlets may use auto-registration to declare the URL they handle: |
| 321 | |
| 322 | ==== |
| 323 | import com.google.gerrit.extensions.annotations.Export; |
| 324 | import com.google.inject.Singleton; |
| 325 | import javax.servlet.http.HttpServlet; |
| 326 | import javax.servlet.http.HttpServletRequest; |
| 327 | import javax.servlet.http.HttpServletResponse; |
| 328 | |
| 329 | @Export("/print") |
| 330 | @Singleton |
| 331 | class HelloServlet extends HttpServlet { |
| 332 | protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { |
| 333 | res.setContentType("text/plain"); |
| 334 | res.setCharacterEncoding("UTF-8"); |
| 335 | res.getWriter().write("Hello"); |
| 336 | } |
| 337 | } |
| 338 | ==== |
| 339 | |
| Edwin Kempin | 8aa650f | 2012-07-18 11:25:48 +0200 | [diff] [blame] | 340 | The auto registration only works for standard servlet mappings like |
| 341 | `/foo` or `/foo/*`. Regex style bindings must use a Guice ServletModule |
| 342 | to register the HTTP servlets and declare it explicitly in the manifest |
| 343 | with the `Gerrit-HttpModule` attribute: |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 344 | |
| 345 | ==== |
| 346 | import com.google.inject.servlet.ServletModule; |
| 347 | |
| 348 | class MyWebUrls extends ServletModule { |
| 349 | protected void configureServlets() { |
| 350 | serve("/print").with(HelloServlet.class); |
| 351 | } |
| 352 | } |
| 353 | ==== |
| 354 | |
| 355 | For a plugin installed as name `helloworld`, the servlet implemented |
| 356 | by HelloServlet class will be available to users as: |
| 357 | |
| 358 | ---- |
| 359 | $ curl http://review.example.com/plugins/helloworld/print |
| 360 | ---- |
| Nasser Grainawi | e033b26 | 2012-05-09 17:54:21 -0700 | [diff] [blame] | 361 | |
| Edwin Kempin | f5a7733 | 2012-07-18 11:17:53 +0200 | [diff] [blame] | 362 | [[data-directory]] |
| Edwin Kempin | 41f6391 | 2012-07-17 12:33:55 +0200 | [diff] [blame] | 363 | Data Directory |
| 364 | -------------- |
| 365 | |
| 366 | Plugins can request a data directory with a `@PluginData` File |
| 367 | dependency. A data directory will be created automatically by the |
| 368 | server in `$site_path/data/$plugin_name` and passed to the plugin. |
| 369 | |
| 370 | Plugins can use this to store any data they want. |
| 371 | |
| 372 | ==== |
| 373 | @Inject |
| 374 | MyType(@PluginData java.io.File myDir) { |
| 375 | new FileInputStream(new File(myDir, "my.config")); |
| 376 | } |
| 377 | ==== |
| 378 | |
| Edwin Kempin | f5a7733 | 2012-07-18 11:17:53 +0200 | [diff] [blame] | 379 | [[documentation]] |
| Nasser Grainawi | e033b26 | 2012-05-09 17:54:21 -0700 | [diff] [blame] | 380 | Documentation |
| 381 | ------------- |
| 382 | |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 383 | If a plugin does not register a filter or servlet to handle URLs |
| 384 | `/Documentation/*` or `/static/*`, the core Gerrit server will |
| 385 | automatically export these resources over HTTP from the plugin JAR. |
| 386 | |
| 387 | Static resources under `static/` directory in the JAR will be |
| 388 | available as `/plugins/helloworld/static/resource`. |
| 389 | |
| 390 | Documentation files under `Documentation/` directory in the JAR |
| 391 | will be available as `/plugins/helloworld/Documentation/resource`. |
| 392 | |
| 393 | Documentation may be written in |
| 394 | link:http://daringfireball.net/projects/markdown/[Markdown] style |
| 395 | if the file name ends with `.md`. Gerrit will automatically convert |
| 396 | Markdown to HTML if accessed with extension `.html`. |
| Nasser Grainawi | e033b26 | 2012-05-09 17:54:21 -0700 | [diff] [blame] | 397 | |
| Edwin Kempin | f5a7733 | 2012-07-18 11:17:53 +0200 | [diff] [blame] | 398 | [[macros]] |
| Edwin Kempin | c78777d | 2012-07-16 15:55:11 +0200 | [diff] [blame] | 399 | Within the Markdown documentation files macros can be used that allow |
| 400 | to write documentation with reasonably accurate examples that adjust |
| 401 | automatically based on the installation. |
| 402 | |
| 403 | The following macros are supported: |
| 404 | |
| 405 | [width="40%",options="header"] |
| 406 | |=================================================== |
| 407 | |Macro | Replacement |
| 408 | |@PLUGIN@ | name of the plugin |
| 409 | |@URL@ | Gerrit Web URL |
| 410 | |@SSH_HOST@ | SSH Host |
| 411 | |@SSH_PORT@ | SSH Port |
| 412 | |=================================================== |
| 413 | |
| 414 | The macros will be replaced when the documentation files are rendered |
| 415 | from Markdown to HTML. |
| 416 | |
| 417 | Macros that start with `\` such as `\@KEEP@` will render as `@KEEP@` |
| 418 | even if there is an expansion for `KEEP` in the future. |
| 419 | |
| Edwin Kempin | f5a7733 | 2012-07-18 11:17:53 +0200 | [diff] [blame] | 420 | [[auto-index]] |
| Shawn O. Pearce | 795167c | 2012-05-12 11:20:18 -0700 | [diff] [blame] | 421 | Automatic Index |
| 422 | ~~~~~~~~~~~~~~~ |
| 423 | |
| 424 | If a plugin does not handle its `/` URL itself, Gerrit will |
| 425 | redirect clients to the plugin's `/Documentation/index.html`. |
| 426 | Requests for `/Documentation/` (bare directory) will also redirect |
| 427 | to `/Documentation/index.html`. |
| 428 | |
| 429 | If neither resource `Documentation/index.html` or |
| 430 | `Documentation/index.md` exists in the plugin JAR, Gerrit will |
| 431 | automatically generate an index page for the plugin's documentation |
| 432 | tree by scanning every `*.md` and `*.html` file in the Documentation/ |
| 433 | directory. |
| 434 | |
| 435 | For any discovered Markdown (`*.md`) file, Gerrit will parse the |
| 436 | header of the file and extract the first level one title. This |
| 437 | title text will be used as display text for a link to the HTML |
| 438 | version of the page. |
| 439 | |
| 440 | For any discovered HTML (`*.html`) file, Gerrit will use the name |
| 441 | of the file, minus the `*.html` extension, as the link text. Any |
| 442 | hyphens in the file name will be replaced with spaces. |
| 443 | |
| 444 | If a discovered file name beings with `cmd-` it will be clustered |
| 445 | into a 'Commands' section of the generated index page. All other |
| 446 | files are clustered under a 'Documentation' section. |
| 447 | |
| 448 | Some optional information from the manifest is extracted and |
| 449 | displayed as part of the index page, if present in the manifest: |
| 450 | |
| 451 | [width="40%",options="header"] |
| 452 | |=================================================== |
| 453 | |Field | Source Attribute |
| 454 | |Name | Implementation-Title |
| 455 | |Vendor | Implementation-Vendor |
| 456 | |Version | Implementation-Version |
| 457 | |URL | Implementation-URL |
| 458 | |API Version | Gerrit-ApiVersion |
| 459 | |=================================================== |
| 460 | |
| Edwin Kempin | f5a7733 | 2012-07-18 11:17:53 +0200 | [diff] [blame] | 461 | [[deployment]] |
| Nasser Grainawi | e033b26 | 2012-05-09 17:54:21 -0700 | [diff] [blame] | 462 | Deployment |
| 463 | ---------- |
| 464 | |
| Edwin Kempin | f729574 | 2012-07-16 15:03:46 +0200 | [diff] [blame] | 465 | Compiled plugins and extensions can be deployed to a running Gerrit |
| 466 | server using the link:cmd-plugin-install.html[plugin install] command. |
| Shawn O. Pearce | da4919a | 2012-05-10 16:54:28 -0700 | [diff] [blame] | 467 | |
| 468 | Plugins can also be copied directly into the server's |
| 469 | directory at `$site_path/plugins/$name.jar`. The name of |
| 470 | the JAR file, minus the `.jar` extension, will be used as the |
| 471 | plugin name. Unless disabled, servers periodically scan this |
| 472 | directory for updated plugins. The time can be adjusted by |
| 473 | link:config-gerrit.html#plugins.checkFrequency[plugins.checkFrequency]. |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 474 | |
| Edwin Kempin | f729574 | 2012-07-16 15:03:46 +0200 | [diff] [blame] | 475 | For disabling plugins the link:cmd-plugin-remove.html[plugin remove] |
| 476 | command can be used. |
| 477 | |
| Brad Larson | d5e87c3 | 2012-07-11 12:18:49 -0500 | [diff] [blame] | 478 | Disabled plugins can be re-enabled using the |
| 479 | link:cmd-plugin-enable.html[plugin enable] command. |
| 480 | |
| Deniz Türkoglu | eb78b60 | 2012-05-07 14:02:36 -0700 | [diff] [blame] | 481 | GERRIT |
| 482 | ------ |
| 483 | Part of link:index.html[Gerrit Code Review] |